/Разборы задач
25 октября 2026
Райтап на задачу «Просмотр файлов»
Райтап feadal на задачу «Просмотр файлов»: проверка пути через startswith, поведение os.path.normpath и обход запрета на чтение файла.58
24

Web
Задача «Шакализатор картинок»
Перейдём на страницу сайта. Нас встретит ряд фотографий с Пикси и разными шакалами.
По условию таска оригинальные фото Пикси были слишком большого размера и поэтому «на бэкенде был добавлен шакализатор картинок». Разберёмся, как же подгружаются эти фото на сайт. Для этого откроем DevTools.
Выберем любое из фото и перейдём на его прямой URL.
URL будет состоять из следующих частей:
1. resize
Сам эндпоинт ресайзера. Это просто имя роута, менять в нём нечего, так что для нас он малоинтересен.
2. 1248x832
Нужный размер. Можем попробовать его поменять и получить разные интересные варианты картинки.
Касательно ресайза картинки тоже существует интересная уязвимость — CWE-400, но она влияет на доступность сайта и в контексте данной задачи нас интересовать не будет.
3. upload/pixie-1.jpg
Непосредственно путь к исходному файлу. Вот эта часть интересует нас больше всего: путь подставляется в URL напрямую, попробуем загуглить, какая уязвимость актуальна при таком подходе.
Теперь мы знаем, что она называется Path Traversal (CWE-22), а значит, мы можем попробовать подсунуть свой URL и выйти за пределы папки upload.
Классическая конструкция для Path Traversal — это ../. Чтобы понять, почему она работает, вспомним, как устроена файловая система. Большинство веб-серверов работает на Linux, где файлы и папки организованы в виде дерева каталогов, а переходы между ними записываются через слеш (/).
Специальная последовательность .. говорит серверу, что надо подняться на уровень выше. То есть из каталога upload с помощью ../ можно выйти в корень приложения, а оттуда и добраться до служебных файлов с секретами. Если путь никак не фильтруется, то сервер просто отдаст их.
Но куда именно идти? К задаче приложены исходники, так что можем не гадать, а заглянуть в них напрямую.
Стоит отметить, что .env — это так называемый dotfile (файл с точкой в начале имени). В Unix-подобных системах такие файлы по умолчанию скрыты при обычном просмотре каталога, поэтому их не видно в списке и нельзя наткнуться случайно. Но это лишь визуальное сокрытие: если знать имя файла, он читается как любой другой. Чтобы увидеть такие файлы, используем флаг -a.
На файл .DS_Store (Desktop Services Store) не обращаем внимания, это скрытый системный файл, который операционная система macOS автоматически создаёт в каждой папке, к которой обращался файловый менеджер Finder.
Загуглим, что такое файл .env.
Теперь мы знаем, что это файл, в котором хранятся различные важные данные. Проверим, чем они являются в нашем случае.
Теперь мы уверены, что флаг лежит в файле .env. Попробуем прочитать флаг на работающем инстансе, используя Path Traversal. Для этого изменим URL до следующего вида /resize/100x100/../.env, где 100x100 — произвольный размер, а вместо имени картинки мы через ../ выходим из папки upload к файлу .env в корне проекта.
Но вместо содержимого получим ошибку 400. Загуглим, когда она возникает.
Так как ошибок мы не допускали, а тот же роут прекрасно работает для нормального пути к картинке, что мы подтвердили ранее, то можем предположить, что наш запрос не пропустился из-за несоответствия требованиям сервера. Если хотим убедиться в этом наверняка, то можем обратиться к коду resize.php и увидеть, что для ошибки 400 там предусмотрена своя ошибка, до которой мы просто не проходим из-за nginx.
Теперь разберёмся, почему нас не пропускает nginx. Заглянем в сам nginx.conf.
Теперь мы видим, что для роута /resize/ там прописано сразу несколько ограничений:
if ($request_uri ~* "^/resize/.*(?:%2e|%2f|%5c|%00|%25)") {
return 400;
}Правило, возвращающее 400, если в пути есть закодированные спецсимволы (%2e, %2f, %5c, %00, %25), что уже закрывает один из типичных обходов через %2e%2e;
location ~* "^/resize/[0-9]{1,4}x[0-9]{1,4}/upload/[A-Za-z0-9._-]+\\.(png|jpe?g|gif)$" {
try_files $uri @resize_php;
}В обработку пропускаются только пути строго вида /resize/WxH/upload/<имя>.(png|jpg|gif);
location ~* ^/resize/ {
return 400;
}На всё остальное под /resize/ стоит return 400.
Наш /resize/100x100/../.env под шаблон «картинка из upload» не подходит, поэтому и ловит return 400.
Можем заметить, что все ограничения в nginx навешаны только на путь /resize/…. Но сам ресайз выполняет скрипт resize.php, а в конфиге есть правило, которое отдаёт любые .php-файлы напрямую:
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}Сам путь /resize.php начинается с /resize. (с точкой), а не с /resize/, поэтому под правила WAF он не попадает. Значит, к тому же коду можно обратиться напрямую, минуя все проверки.
Теперь проверим сам resize.php, фильтрует ли он путь.
Параметр file подставляется в путь как есть, без удаления ../:
$sourcePath = __DIR__ . '/' . $_GET['file'];А в конце, если файл не распознан как картинка, он просто отдаётся клиенту:
header('Content-Type: image/png');
readfile($sourcePath);То есть любой файл, который не является изображением (как .env), сервер вернёт в сыром виде.
Соберём всё вместе и обратимся к скрипту напрямую, минуя WAF, и через ../ выходим к .env. Получится URL следующего вида:
/resize.php?w=100&h=100&file=../.envresize.php — обращаемся к скрипту ресайза напрямую;
w=100&h=100 — ширина и высота;
file=../.env — путь к целевому файлу.
Также в самом пути теперь добавились параметры w, h, file. Это связано с тем, что router.php ранее сам раскладывал части в GET-параметры w, h, file, а при прямом обращении к resize.php роутера нет, поэтому те же параметры передаём напрямую в строке запроса. ? отделяет её от адреса, а & разделяет параметры.
Суть уязвимости — чтение произвольных файлов на сервере через Path Traversal.
Защиту от выхода за пределы папки повесили только на роут /resize/… на уровне nginx, а тот же код оказался доступен напрямую через /resize.php, в обход всех проверок.
Сам же скрипт путь не фильтрует, а берёт параметр file как есть и, если файл не является картинкой, то отдаёт его содержимое в сыром виде.
Если бы путь нормализовался (например, через realpath()) и проверялось, что он действительно внутри upload/, а прямой доступ к resize.php был закрыт, то прочитать .env не вышло бы.
В OWASP Top 10 этой уязвимости соответствует A01:2025 — Broken Access Control: пользователь получает доступ к файлам, к которым не должен.
По CWE это CWE-22 (Path Traversal). Уязвимость безопасности, которая возникает, когда приложение создаёт путь к файлу из внешних (недоверенных) пользовательских данных, но не нейтрализует специальные элементы вроде ../.