Предположим, что веб-приложение использует следующий код PHP для загрузки файлов (upload.php):
<?php
$target_path = "uploads/";
$target_path = $target_path . basename( $_FILES['uploadedfile']['name']);
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path))
{
echo "The file ". basename( $_FILES['uploadedfile']['name']).
" has been uploaded";
}
else
{
echo "There was an error uploading the file, please try again!";
}
?>
Данный сценарий работает вместе со следующей HTML-формой:
<form enctype="multipart/form-data" action="uploader.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="100000" />
Choose a file to upload: <input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
Когда PHP принимает POST-запрос с типом кодировки "multipart/form-data", создается временный файл со случайным именем в директории для временных файлов (т.е. /var/tmp/php4vlktb). PHP также заполняет глобальный массив $_FILES информацией о загруженном файле:
$_FILES['uploadedfile']['name']: Оригинальное имя файла на компьютере клиента.
$_FILES['uploadedfile']['type']: MIME-тип файла.
$_FILES['uploadedfile']['size']: Размер файла в байтах.
$_FILES['uploadedfile']['tmp_name']: Временное имя загруженного файла, под которым он сохранен на сервере.
Функция PHP move_uploaded_file переместит временный файл по заданному пользователем пути. В данном случае путь для перемещения файла находится в пределах корневой директории веб-сервера. Следовательно, файл будет доступен при использовании строки URL, похожей на следующую: http://www.example.com/uploads/temp-uploads/uploadedfile.jpg.
В этом простом примере не накладывается ограничений на тип загружаемых файлов, поэтому взломщик может загрузить файл с вредоносным кодом PHP или .NET, что приведет к компрометации сервера. В таком случае взломщик может просто загрузить вредоносные сценарии c99 shell или r57 shell и получить контроль над сервером.
Однако, даже если в целях безопасности проверка расширения файла производится перед загрузкой, взломщик может также обойти ее, использовав нулевой символ %00. Все что требуется сделать, это выбрать файл PHP для загрузки, не нажимая на кнопку "Загрузить". Путь к выбранному для загрузки файлу будет отображен и для *NIX-систем будет выглядеть как /Users/username/exploit.php, а для Windows-систем как c:\exploit.php. На этом этапе нужно вручную добавить к строке нулевой символ с каким-либо последующим расширением изображения, например, /Users/username/exploit.php%00.jpg или c:\exploit.php%00.jpg.
После нажатия на кнопку "Загрузить" сервер проверит расширение, посчитает, что производится загрузка изображения и выведет сообщение, подобное следующему: "Спасибо за загрузку вашего изображения!". Как только файл будет загружен, появится возможность выполнения вредоносного кода на сервере.
Другим популярным способом защиты от исполнения загруженных файлов является запрет на исполнение сценариев в директории загрузок с помощью файла .htaccess. Стандартный файл .htaccess для этой цели должен содержать следующий код:
AddHandler cgi-script .php .php3 .php4 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi
Options -ExecCGI
Код, приведенный выше, использует список запрещенных расширений и не особенно безопасен сам по себе. Взломщик может просто обойти такие проверки, загрузив файл с названием .htaccess, содержащий код, подобный следующему:
AddType application/x-httpd-php .jpg
Эта строка указывает Apache, что необходимо исполнять файлы изображений с расширением .jpg так, как будто это сценарии PHP. Теперь взломщик может загрузить файл с расширением .jpg, содержащий код PHP. Так как загружаемые файлы могут перезаписывать и перезаписывают существующие, злоумышленники могут без лишних сложностей заменить файл .htaccess своей модифицированной версией, позволяющей исполнять определенные сценарии для последующей компрометации сервера.