Введение в практическую безопасность (2019)/Небезопасная загрузка файлов

Материал из SecSem Wiki
Перейти к навигации Перейти к поиску

Загрузка файлов пользователями - довольно частая часть функциональности веб-приложения (аватарки, хостинг файлов, посылка вложений вместе с сообщениями и т. д.). Небезопасная реализация этого механизма может приводить к очень серьезным уязвимостям.

Создание загруженного файла на сервере

Самый простой способ реализации механизма загрузки файлов - просто создавать на файловой системе сервера файл с таким же содержимым как файл, отправленый пользователем (проще говоря, "класть" присланный пользователем файл куда-то на файловую систему сервера). После загрузки файл либо доступен пользователю по прямой ссылке (т. е. определенные URL-адреса мапятся на загруженные файлы и существует URL, обращение к которому ведет к отдаче загруженного файла), либо как то еще обрабатывается веб-приложением. Само веб-приложение во многом состоит из файлов на диске, поэтому добавление файлов на сервер может быть небезопасным, так как (в общем случае) может изменить логику работы приложения.

PHP web shell

Простейшим примером является загрузка файла с расширением .php на сайт, работающий на PHP. По умолчанию PHP-сайты работают так что пользователь обращается по URL к файлу (URL сайта, начиная с первого /, мапятся на определенную директорию на сервере, она называется web root) и, если этот файл имеет расширение php (или другое расширение из предопределенного списка расширений для PHP-файлов), то содержимое этого файла выполняется как код на PHP, вывод этого кода возвращается пользователю в ответе (кстати, так работает не только PHP, но и ASP.NET Web Forms, JSP, ASP). В результате, если пользователь может загрузить на сайт файл c расширением .php и обратиться к нему по URL (т. е. этот файл будет создан где-то внутри web root), то, если сервер специально не настроен чтобы файлы из директории для загрузок не выполнялись, PHP-код в нём будет выполнен. Это можно попробовать на стенде http://fu.stands.course.secsem.ru - хотя сайт предназначен для загрузки изображений, если атакующий загрузит на него файл attack.php c кодом

<?php
    echo "5 * 1451 = ", 5 * 1451;
    echo " ls /: ";
    system("ls /");

и, получив ссылку на него (к примеру, /uploads/5c8e82bb8ad66/attack.php) обратится по ней, то в результате этот PHP-код выполнится и в ответе сервера будет произведение чисел 5 и 1451, а также список файлов в корневой директории сервера (функция PHP system работает так же как одноимённая функция библиотеки Си - то есть выполняет команду shell). В итоге атакующий получает выполнение произвольного кода на сервере (RCE).

Обход механизмов защиты

Даже когда создатель сайта знает о возможной опасности и использует механизмы защиты, они могут оказаться неэффективными.

Проверка типа файла на клиенте (т. е. в браузере) с помощью выставления типа атрибута accept у file input или с помощью JavaScript-кода неэффективна просто потому что атакующий может не использовать браузер (а использовать вместо него тот же Burp Suite или скрипт на Питоне).

Нередко запрещается загрузка "опасных" расширений , однако, во первых, черный список может быть неполным (кроме php в качестве расширения для PHP-файлов распознаётся, как правило, php3, php4, php5, pht, phtml) или проверка может тривиальным образом обходиться (например расширение pHP может обойти чёрный список, но быть воспринятым как расширение PHP-скрипта сервером). Также, из-за особенностей конфигурации сервера файл может быть проинтерпретирован как PHP-скрипт когда .php является не последним расширением (т. е. например attk.php.jpg).

Другим вариантом может быть проверка содержимого на то, относится ли оно к ожидаемому типу (скажем, изображению или видео). Однако, тут атакующему помогает то что PHP довольно толерантен к содержимому скриптов - PHP-код начинается с <?php (или просто <? если включена настройка short_open_tag) и может заканчиваться на ?> - всё что находится за пределами этих маркеров (эдакого "тэга") просто выдаётся в вывод as is без ошибок, какие бы байты там ни были. В результате, PHP-скрипт может быть вполне валидной картинкой и, если где-то в середине неё встретится <?php system("echo ATTACK HERE"); ?>, этот код выполнится. Здесь атакующему может помешать то что в бинарном файле может просто случайно встретиться маркер начала PHP-кода, то что идет после него скорее всего не будет валидным PHP-кодом и выполнение упадёт с ошибкой. Для борьбы с этим можно стараться брать медиафайлы поменьше (чтобы снизить вероятность того что в них найдется определённая последовательность байт), ну и проверять их. Другая вещь, которая может осложнить эту атаку ещё больше - преобразование файла на сервере (resize картинки, пережатие видео) - однако, успешно атаковать бывает можно и в этих случаях.

Но далеко не только это

Небезопасная загрузка файлов может быть далеко не только в PHP и упомянутых ASP.NET Web Forms/JSP/ASP, загрузить файлы с исходным кодом и добиться их выполнения бывает можно и в случае сайтов на Python и JavaScript и других платформ. Вообще, файлы с исходным кодом не являются единственным вектором атаки, другим вариантом является загрузка конфигурационных файлов, для того же PHP бывает можно загрузить конфигурационный файл .htaccess, который позволяет внести изменения в конфигурацию сервера для данной директории, что в конечном итоге также даёт выполнение произвольного кода (точно так же в случае ASP.NET бывает можно загрузить конфигурационный файл web.config).

К уязвимостям небезопасной загрузки файлов также можно относить уязвимости, связаные не с созданием загруженного файла на диске, а с его обработкой на сервере (или способом хранения). К примеру, изображение может загружаться на серевер и как-то обрабатываться (конвертироваться в другой формат например), после чего возвращаться клиенту или передаваться еще куда-то - и в софте, занимающимся обаботкой, может быть уязвимость. На диске на сервере оно может при этом не сохраняться вообще.

Еще инфу про уязвимости загрузки файлов можно найти в этих отличных слайдах от Артура Хашаева. Вот про уязвимый file upload на OWASP.