MiddleДебаггингИногдаЕщё не отвечали
Найдите чтение локальных файлов в обход проверки картинки на PHP
Этот эндпоинт на PHP скачивает URL, проверяет, что это похоже на картинку, и отдаёт её. Кроме SSRF найдите уязвимость и способ обойти проверку mime.
Ограничения:
urlполностью контролируется атакующим- единственная защита — проверка mime через
getimagesize()передreadfile()
$url = $_GET['url'];
$info = getimagesize($url);
if (stripos($info['mime'], 'image') === false) die();
readfile($url);
Определите причину.
Local File Read — readfile($url) отдаёт любой путь, поэтому обёртка php://filter читает произвольные файлы. Проверка mime обходится, так как цепочка фильтров перекодирует начальные байты, пока getimagesize() не примет их за картинку, либо через TOCTOU. Фикс: белый список источников, запрет php://, одно скачивание, переэнкод.
- ✗Считать проверку getimagesize() достаточной защитой от чтения файлов
- ✗Видеть только SSRF и упускать Local File Read через php://filter
- ✗Не учитывать TOCTOU между проверкой и readfile
- →Как цепочка php://filter превращает байты файла в подобие картинки?
- →Почему TOCTOU обходит проверку mime даже без обёртки php://?
Оглавление
Уязвимость
$url = $_GET['url'];
$info = getimagesize($url);
if (stripos($info['mime'], 'image') === false) die();
readfile($url);
readfile($url) отдаёт содержимое любого пути → Local File Read. Проверка getimagesize() обходится:
- php://filter chain. Обёртка
php://filterумеет применять к потоку цепочку кодировочных фильтров. Подобрав их, можно добиться того, что начало потока станет байтами, которыеgetimagesize()засчитает за картинку, тогда какreadfileотдаст исходное содержимое произвольного локального файла. Суть: проверяется одно представление данных, отдаётся другое. - TOCTOU. При
getimagesize()сервер видит валидную картинку, приreadfile()— уже payload (если URL отдаёт разный ответ на два запроса).
Исправление
$allowed = ['https://cdn.example.com/'];
if (!startsWithAny($url, $allowed) || str_starts_with($url, 'php://')) die();
$data = downloadOnce($url); // один запрос — нет TOCTOU
if (!isRealImage($data)) die();
echo reencodeImage($data); // переэнкод убивает встроенный payload
✅ Белый список источников, запрет обёртки php://, одно скачивание (нет TOCTOU) и переэнкод на сервере устраняют LFR у корня.
Оглавление