Главная / Блог / Path Traversal и LFI в CTF: от первого запроса до флага шаг за шагом

12 мин.00

Path Traversal и LFI в CTF: от первого запроса до флага шаг за шагом

Path Traversal и LFI в CTF: от первого запроса до флага шаг за шагом

Path Traversal и LFI в CTF: от первого запроса до флага шаг за шагом

На недавнем CTF задача из категории Web на 300 очков решилась за 40 минут. Эндпоинт /download?report=quarterly.pdf принимал имя файла без единой проверки. Три запроса в Burp Repeater — ../../../etc/passwd, потом php://filter/convert.base64-encode/resource=config.php — и в ответе лежали креды от базы. Ещё пятнадцать минут на log poisoning, cat /flag.txt, флаг. Цепочка path traversal → LFI → RCE — одна из самых ходовых в веб-задачах CTF. Её и разберём до последнего байта.

Место path traversal и LFI в цепочке атаки

Kill chain определяет порядок действий. Когда вы открываете веб-задачу CTF, эксплуатация path traversal и local file inclusion уязвимости проходит через пять фаз — каждая соответствует конкретной технике в MITRE ATT&CK: Подробнее — в нашем руководстве по пентест веб-приложений.

  1. Exploit Public-Facing Application (T1190, Initial Access) — находите уязвимый параметр в веб-приложении. Форма скачивания отчётов, просмотр шаблонов, загрузка изображений — всё, где сервер лезет в файловую систему по пользовательскому вводу.

  2. File and Directory Discovery (T1083, Discovery) — через directory traversal атаку определяете структуру файловой системы. Подставляете пути к известным файлам, проверяете существование директорий, считаете глубину от webroot до корня.

  3. Credential Access (/etc/passwd и /etc/shadow — T1003.008, Credentials In Files — T1552.001) — вытаскиваете конфиденциальные файлы: /etc/passwd для списка пользователей, .env или config.php с паролями от БД и API-ключами, SSH-ключи из /home/user/.ssh/id_rsa.

  4. Data from Local System (T1005, Collection) — собираете данные: исходный код приложения, конфиги, содержимое /proc/self/environ с переменными окружения. На этом шаге часто лежит флаг в задачах начального уровня.

  5. Unix Shell (T1059.004, Execution) — если нужен RCE, раскручиваете LFI до выполнения кода через log poisoning, session injection или PHP wrappers. Финал для задач средней и высокой сложности.

В большинстве CTF-задач флаг лежит на шаге 4 (прямое чтение файла сервера через уязвимость) или на шаге 5 (нужна команда для извлечения). Начальный уровень — прочитал /etc/passwd, подтвердил включение локальных файлов. Продвинутые задачи требуют полную цепочку LFI to RCE.

[Применимо: CTF-среды, legacy PHP-приложения, внешний пентест. В контейнерных деплоях с read-only файловой системой path traversal по-прежнему опасен для чтения исходного кода, но LFI→RCE через логи зачастую невозможен — Docker по умолчанию не пишет логи на диск.]

Где искать уязвимые параметры в веб-задачах CTF

Первое действие при решении веб-задачи CTF на path traversal — найти точку входа. Параметр, значение которого сервер использует для обращения к файловой системе.

По данным OWASP и HackTricks, наиболее частые имена таких параметров: page, file, path, template, include, doc, dir, folder, style, pdf, conf, root, target, php_path. В CTF-задачах авторы иногда маскируют имя — lang, module, view, section — но суть та же: значение попадает в файловую операцию.

Не ограничивайтесь GET-параметрами в строке URL. Обход директорий веб-приложения часто прячется глубже:

JSON-тело запроса. Эндпоинты API типа POST /api/export с полем {"filename": "report.csv"}. В современных CTF-задачах path traversal в JSON встречается чаще, чем в query string — авторы ориентируются на реальные паттерны уязвимых приложений. По данным YesWeHack, path traversal в API-запросах — распространённый паттерн для микросервисных архитектур, где один сервис формирует запрос к другому, используя пользовательский ввод в пути.

Cookie. Классика из OWASP Path Traversal: приложение берёт значение cookie (например, TEMPLATE) и передаёт его в функцию include. Подстановка Cookie: TEMPLATE=../../../etc/passwd — и системный файл у вас.

HTTP-заголовки. Кастомные заголовки X-Template, X-File-Path, X-Include в задачах повышенной сложности. Такие параметры не видны в URL — нужен анализ трафика через прокси.

Маршруты URL. Когда часть пути интерпретируется как имя файла: /static/css/../../etc/passwd. Этот вектор легко пропустить, если не проксировать трафик.

Методика поиска: откройте Burp Suite, проксируйте весь трафик, прокликайте каждую функцию приложения. В Burp HTTP History отфильтруйте запросы, где значение параметра содержит расширение файла (.php, .html, .pdf, .log) или выглядит как путь. Каждый такой параметр — кандидат на тестирование эксплуатации path traversal.

Базовая эксплуатация directory traversal атаки

Требования к окружению

  • ОС: любая (рекомендуется Kali Linux 2024.x+), минимум 2 ГБ RAM для браузера + Burp
  • Burp Suite Community Edition или Professional (перехват и модификация запросов)
  • Утилита curl для быстрых проверок из терминала
  • Доступ к целевому CTF-таску по HTTP/HTTPS

Пошаговый разбор

Шаг 1: определите уязвимый параметр. На сайте CTF-задачи обнаружена страница /view?page=about.html. Отправьте запрос в Burp Repeater.

Шаг 2: проверьте базовый traversal. Замените значение параметра на ../../../etc/passwd. Если в ответе появились строки вида root:x:0:0:root:/root:/bin/bash — path traversal уязвимость подтверждена. Чтение /etc/passwd через LFI в чистом виде.

Шаг 3: определите глубину. Три уровня ../ не сработали? Увеличивайте: ../../../../etc/passwd, ../../../../../etc/passwd и дальше. Типичная глубина от webroot до корня — 3-7 уровней. Стандартные webroot-пути на Linux: /var/www/html/, /var/www/app/, /srv/http/, /opt/app/public/. На каждый уровень вложенности — один ../.

Шаг 4: читайте целевые файлы. После подтверждения уязвимости переходите к файлам по приоритету:

Файл Зачем Когда доступен
/etc/passwd Подтверждение уязвимости, список пользователей Почти всегда
/var/www/html/.env Пароли БД, API-ключи, секреты Если приложение на Laravel/Node.js
/var/www/html/config.php Конфигурация PHP-приложения PHP-стек
/home/user/.ssh/id_rsa SSH-ключ для доступа к серверу Если известен username
/etc/shadow Хеши паролей Только при правах root (редко в CTF)
/proc/self/environ Переменные окружения процесса CGI/FastCGI конфигурация
/proc/self/cmdline Командная строка запуска приложения Linux с /proc
/var/log/apache2/access.log Логи для log poisoning (LFI→RCE) Apache с записью на диск

Шаг 5: если прямой traversal не работает — сервер фильтрует ввод. Переходите к обходу фильтров.

Обход фильтров path traversal: выбор bypass-техники

Большинство CTF-задач средней сложности и выше ставят фильтры. По данным PortSwigger, типичные защиты: удаление ../ из строки, проверка расширения файла, валидация начала пути. Каждый тип фильтра — своя техника обхода пути в URL. Вот алгоритм выбора.

Когда какой bypass применять

Сервер удаляет ../ из строки нерекурсивно? Распознаётся по тому, что ../../../etc/passwd возвращает ошибку, но сервер не блокирует запрос целиком — просто убирает последовательности. Решение: вложенные traversal-последовательности. Подставьте ....//....//....//etc/passwd. Когда приложение удалит внутренний ../, из ....// получится ../, и traversal отработает. Аналогично работает ....\/ на серверах, принимающих обратный слеш.

WAF или фильтр блокирует запрос с ../? Если сервер возвращает 403 или пустой ответ при любом упоминании ../ — пробуйте URL-кодирование: %2e%2e%2f вместо ../. Если запрос проходит через несколько слоёв декодирования (прокси → веб-сервер → приложение), работает двойное кодирование: %252e%252e%252f. Первый слой декодирует %25 в %, второй — %2e в . и %2f в /.

Сервер проверяет, что путь начинается с определённой директории? Если фильтр ожидает /var/www/images в начале — добавьте этот префикс и продолжите traversal: ?file=/var/www/images/../../../etc/passwd. Приложение пропустит проверку начала, а файловая система разрешит ../.

Фильтр проверяет расширение файла? На PHP < 5.3.4 — null byte bypass LFI: ../../../etc/passwd%00.png. Символ %00 обрезает строку, и PHP откроет /etc/passwd, проигнорировав .png. Техника устаревшая: на PHP 5.3.4 и выше null byte не работает. Для современных PHP-версий ищите другие обходы — если фильтр проверяет только последние 4 символа, попробуйте path truncation.

Абсолютный путь как bypass. Иногда фильтр проверяет только наличие ../, но допускает абсолютные пути. Попробуйте ?file=/etc/passwd — без traversal-последовательностей вообще. Работает чаще, чем кажется. Я на нескольких CTF видел, как люди полчаса ковыряли кодирование, а простой абсолютный путь проходил с первого раза.

Сводная таблица

Техника Пейлоад Предусловие
Вложенные traversal ....//....//etc/passwd Нерекурсивное удаление ../
URL-кодирование %2e%2e%2f WAF/фильтр на уровне URL
Двойное кодирование %252e%252e%252f Многослойное декодирование (proxy → app)
Overlong UTF-8 %c0%ae%c0%ae%c0%af Legacy-парсеры, устаревшие серверы
Null byte ../../../etc/passwd%00.png PHP < 5.3.4
Абсолютный путь /etc/passwd Фильтр только на ../, не на /
Prefix bypass /var/www/images/../../../etc/passwd Проверка начала пути
Обратный слеш ..\..\..\..\etc\passwd Windows или толерантный парсер

[Ограничение: overlong UTF-8 (%c0%ae) работает только на legacy-серверах и специфичных конфигурациях. В современных фреймворках (Django, Express.js, Spring Boot) с нормализацией через path.resolve() или Paths.get().normalize() этот метод бесполезен. Двойное кодирование тоже не сработает, если на сервере единственный слой декодирования.]

PHP wrappers: чтение исходного кода и php wrapper LFI

PHP wrappers — главный козырь при эксплуатации LFI в CTF-задачах на PHP. В отличие от простого path traversal, они позволяют контролировать, как сервер обрабатывает содержимое файла.

php://filter — чтение без исполнения

Когда вы включаете PHP-файл через LFI, сервер его исполняет — вы видите результат работы скрипта, а не его исходный код. Но для CTF нужен именно код: в нём лежат пароли, логика валидации и часто сам флаг.

Wrapper php://filter решает проблему. Запрос ?page=php://filter/convert.base64-encode/resource=config.php заставляет PHP прочитать файл, закодировать содержимое в Base64 и вернуть строку вместо исполнения. Декодируете локально: echo "PD9waHAg..." | base64 -d — и получаете исходный код. Этот wrapper не требует никаких специальных настроек PHP и работает везде, где есть LFI уязвимость веб-приложения на PHP.

Что читать через php://filter в CTF: index.php (точка входа, логика маршрутизации), config.php или db.php (пароли), flag.php (флаг, закомментированный в коде), login.php (логика аутентификации, SQL-запросы).

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

Если в PHP включена директива allow_url_include (по умолчанию Off, но в CTF-задачах часто включена намеренно), открываются два вектора.

data:// позволяет передать PHP-код прямо в URL: ?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOyA/Pg==. Сервер декодирует Base64 (это <?php system('id'); ?>), исполнит код и вернёт результат. С точки зрения MITRE ATT&CK здесь применяется техника Deobfuscate/Decode Files or Information (T1140) — данные проходят через декодирование перед исполнением.

php://input работает аналогично, но PHP-код передаётся в теле POST-запроса. Отправляете POST /index.php?page=php://input с телом <?php system($_GET['cmd']); ?> — и получаете webshell.

[Предусловие: data:// и php://input требуют allow_url_include = On в php.ini. В production эта директива выключена. На CTF проверяйте: если php://filter работает, попробуйте data:// — в задачах эту директиву часто включают специально.]

LFI to RCE: от чтения файлов до выполнения кода на сервере

Когда data:// и php://input недоступны (а allow_url_include выключен), нужны альтернативные пути. Три техники ниже не требуют специальных настроек PHP — только возможность читать и включать файлы.

Log poisoning через Apache/Nginx

Суть: записать PHP-код в лог-файл веб-сервера через контролируемый заголовок, затем включить этот лог через LFI.

Шаг 1. Убедитесь, что можете читать логи. Стандартные пути: /var/log/apache2/access.log, /var/log/nginx/access.log, /var/log/httpd/access_log, /var/log/httpd/error_log.

Шаг 2. Отравите лог. Отправьте запрос с PHP-кодом в User-Agent:

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

PHP-код запишется в access.log рядом с вашим IP и timestamp.

Шаг 3. Включите лог через LFI: ?page=../../../var/log/apache2/access.log&cmd=cat+/flag.txt. Сервер обработает файл лога как PHP-скрипт, найдёт ваш payload, исполнит команду и вернёт результат.

[Когда техника НЕ работает: контейнерные деплои с логированием в stdout (Docker по умолчанию не пишет логи на диск), read-only файловые системы, AppArmor/SELinux с ограничением на чтение /var/log процессом веб-сервера. В CTF-задачах на Docker логи могут писаться в /tmp/access.log — пробуйте нестандартные пути.]

Session file injection

Альтернатива, когда логи недоступны. PHP хранит сессионные данные в файлах по путям /var/lib/php/sessions/sess_PHPSESSID или /tmp/sess_PHPSESSID.

Механика: отправьте запрос, записывающий PHP-код в сессионную переменную. Если приложение сохраняет username в сессии — передайте username=<?php system('id'); ?> при авторизации. Затем узнайте свой PHPSESSID из cookie и включите файл сессии: ?page=../../../var/lib/php/sessions/sess_abc123def456.

/proc/self/environ

Файл /proc/self/environ содержит переменные окружения процесса, включая HTTP_USER_AGENT. Если LFI позволяет читать этот файл — отправьте запрос с PHP-кодом в User-Agent и включите /proc/self/environ. Метод работает на CGI/FastCGI-конфигурациях и встречается в CTF-задачах реже, чем log poisoning. Но держите в арсенале — иногда это единственный путь.

Автоматизация фаззинга LFI для решения веб-задач CTF

Ручной перебор путей и параметров жрёт время, которого на CTF и так нет. ffuf (Go, активно поддерживается, тысячи звёзд на GitHub) решает задачу за секунды.

ffuf -u "http://target/index.php?page=FUZZ" \
     -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt \
     -fc 403 -fs 0

Здесь -fc 403 фильтрует ответы Forbidden, -fs 0 — пустые ответы, FUZZ заменяется каждой строкой из вордлиста. SecLists содержит готовые списки: LFI-Jhaddix.txt для пейлоадов с разной глубиной и кодировками, burp-parameter-names.txt для перебора имён параметров.

Двойной фаззинг — одновременный перебор имени параметра и payload — полезен, когда вы не знаете, какой параметр уязвим: ffuf -w params.txt:PARAM -w traversal.txt:TRAVERSE -u "http://target/?PARAM=TRAVERSE" -fs 4242.

В Burp Suite Intruder (даже в Community Edition) можно загрузить вордлист вручную — выделите значение параметра, выберите тип атаки Sniper, загрузите файл с пейлоадами. В Pro-версии есть встроенный список Fuzzing - path traversal с закодированными вариациями, описанный в документации PortSwigger.

Из специализированных инструментов: LFImap автоматизирует тестирование LFI включая PHP wrappers, kadimus ищет и эксплуатирует LFI автоматически. Перед использованием проверяйте актуальность — некоторые из этих проектов давно не обновлялись, и с современными средами могут быть проблемы совместимости.

Чеклист: решение CTF-задачи на path traversal LFI CTF

  1. Перехватить трафик в Burp Suite, найти параметры с файловыми именами (file, page, path, template)
  2. Проверить базовый traversal: ../../../etc/passwd с увеличением глубины до 7 уровней
  3. При неудаче — bypass: ....//, %2e%2e%2f, %252e%252e%252f, абсолютный путь /etc/passwd
  4. Прочитать ключевые файлы: /etc/passwd, .env, config.php, исходный код
  5. Попробовать PHP wrappers: php://filter/convert.base64-encode/resource=index.php
  6. Для RCE — проверить доступ к логам и сессиям
  7. Log poisoning: PHP-код в User-Agent → включение лога через LFI
  8. Выполнить cat /flag.txt или find / -name "flag*" 2>/dev/null

Задачи на path traversal LFI в CTF меняются. Два-три года назад хватало прямого ../../../etc/passwd — флаг валялся в конфигурационном файле или в /root/flag.txt, доступном для чтения. Сейчас авторы тасков выстраивают цепочки: path traversal → чтение исходного кода → обнаружение второй уязвимости (SQL injection, deserialization) → RCE → флаг. Чистая directory traversal атака — лишь первое звено, а не самодостаточное решение.

Отсюда неочевидный вывод: тратить время на заучивание двадцати вариантов кодирования ../ менее продуктивно, чем натренировать навык быстрого чтения исходного кода через php://filter и поиска следующей уязвимости в прочитанном коде. Я видел десятки CTF-задач, где участники находили LFI за минуту, но тратили час, не понимая, что делать с полученным исходником. Умение читать PHP/Python/Node.js-код на скорость и видеть в нём injection points — вот что отделяет решённую задачу от «почти решённой». И этот навык переносится напрямую в реальный пентест: OWASP A03:2021 (Injection) — третья по критичности категория рисков именно потому, что пользовательский ввод без валидации попадает в файловые операции, SQL-запросы, системные вызовы. На WAPT эту цепочку — от базового directory traversal до полноценного LFI to RCE с обходом фильтров — проходят в двух модулях с лабами, где каждый шаг нужно отработать самостоятельно.

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

Поделиться

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

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

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