Время прочтения: 7 мин.

Ранее, в материале про безопасное программирование, поднималась тема про технологию «iFrame» (прочитать на newtechaudit или на VC), в продолжении этой темы речь пойдет об уязвимости File Upload Vulns. Суть уязвимости заключается в загрузке пейлода на сервер, который при обращении к нему открывается и далее выполняется какой-либо алгоритм, код и т.д.

Описание уязвимости и векторы атаки

Если на веб-сайте есть функция загрузки файла, то сервер, на котором размещен этот сайт, может быть уязвим из-за неправильной обработки загружаемого файла. Более того, если на сервере размещены несколько веб-приложений, и какое-то из них уязвимо перед атакой File Upload, то остальные приложения в зоне риска. Конечно, много зависит от админа сервера – настройки хранилища, различные политики безопасности и т.д, но эти детали опустим, так как речь идет об «выпиливании» уязвимостей в коде, эксплуатация которых будет задачей явно не для новичка (никто не застрахован от злодея)

Реализация атаки проста — злоумышленнику не нужно обладать высокими навыками взлома систем для эксплуатации уязвимости, достаточно просто зайти на всеми любимый youtube, посмотреть, как генерируется shell (для любителей писать всё самому придется попотеть на пять минут дольше), загрузить полученное на сайт и открыть этот файлик на сайте. Поэтому данная атака имеет высокую степень угрозы.

Все примеры будут показаны на локальном сайте DVWA (DVWA – настраиваемое, уязвимое веб-приложение на PHP/MySQL, созданное для проверки своих навыков и инструментов в области безопасности). Есть несколько векторов атаки для реализации уязвимости с последующим получением сессии. В рамках этого материала будет рассмотренно три кейса:

  1. Case 1. На сайте не используются фильтры или их настройка не мешает загружать и открывать исполняемые файлы:
    в поле загрузки файла, загружается исполняемый файл, который при запуске открывает shell.
  2. Case 2. На сайте используются фильтры, не проверяющие конечное расширение файла и есть возможность запускать исполняемые файлы:
    в поле загрузки файла, загружается исполняемый файл с незапрещенным (валидным для сайта) расширением, перехватывается запрос и подменяется расширение исполняемого файла.
  3. Case 3. На сайте используются некорректные фильтры, есть возможность запускать исполняемые файлы:
    в поле загрузки файла, загружается файл с двойным расширением, в итоге он имеет валидное для сайта значение, который при запуске открывает shell.

Подготовка

Для реализации атаки нужно провести небольшую разведку (тесты, если угодно) — можно ли загрузить файл на сервер и после открыть его. Для этого, на сайте необходимо перейти на вкладку «Upload», в нужном поле загрузить картинку. Появляется сообщение об успешном выполнении загрузки.

Загруженный файл с именем image.jpg теперь на сервере, можно получить к нему доступ, перейдя по адресу «локальныйIP/dvwa/hackable/uploads/image.jpg».

(рис.1 проверка загрузки картинки)

Если все отработало, то можно идти далее. Пейлод должен иметь такое расширение, которое понимает установленный на сервере интерпретатор. Как писалось выше, в данном случае, на тестовом сервере используется интерпретатор PHP, поэтому расширение у файла будет соответствующее.

Реализация Case 1.

Сгенерирую пейлод, для этого в качестве примера используется утилита weevely (команда для генерации пейлода — weevely generate «пароль» «путь до файла и его имя»)

(рис.2 Генерация пейлода)

Далее загружу свой файл и перейду по адресу «локальныйIP/dvwa/hackable/uploads/shell.php». Меня встречает пустое окно без ошибок, это значит, что shell успешно загружен на сервер. Теперь можно попробовать подключиться к нему, для этого есть команда — weevely «путь до файла на сайте» «пароль, который указывали при генерации».

(рис.3 Case 1, успешное подключение)

Отлично, подключение прошло успешно. Небольшой, хороший совет для начинающих — во время аудита (тут же все собрались для только научных целей в белых шляпках) при загрузке какого-либо shell/бекдора и всяких прочих, хорошей практикой будет создание пароля для подключения, иначе сессию могут перехватить и тогда уже, возможно, придется отъехать в не самое приятное для жительства место пребывания.

Реализация Case 2.

Если я повышу уровень безопасности, то приложение начнет фильтровать тип файла. Поэтому проделав те же шаги, получаю ошибку. Для начала, переименую название файла shell в shell2.jpg. Но как он запустится, имея расширение jpg? Все просто, никак, поэтому нужно изменить расширение (немного колдовства). Загружаю пейлод на сервер (расширение все еще jpg) и перехватываю запрос для его изменения. Для этого в качестве примера используется BurpSuite. В запросе меняю расширение обратно на php.

(рис.4 Shell.jpg)
(рис.5 Shell.php)

Итого, после применения всех заклинаний, описанных выше, запрос имеет тип файла image и расширение php. Перехожу в консоль, подключаюсь к shell (файл shell2.php). Все отработало отлично.

(рис.6 Case 2, успешное подключение)

Реализация Case 3.

Повышаю защищенность сайта на 3 уровень. Теперь сайт проверяет расширение в запросе и в форме, проверяет тип файла, поэтому представленные техники не сработают. Теперь меняю в запросе shell на расширение php.jpg. Загружаю и пробую подключиться, все отработало.

(рис.7 Case 3, успешное подключение)

Пример безопасного кода

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

// Is it an image? Проверяем расширение, является ли файл картинкой
	if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
		( $uploaded_size < 100000 ) &&
		( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&
		getimagesize( $uploaded_tmp ) ) {

		// Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD). Удаляем метаданные с картинки
		if( $uploaded_type == 'image/jpeg' ) {
			$img = imagecreatefromjpeg( $uploaded_tmp );
			imagejpeg( $img, $temp_file, 100);
		}
		else {
			$img = imagecreatefrompng( $uploaded_tmp );
			imagepng( $img, $temp_file, 9);
		}
		imagedestroy( $img );

		// Can we move the file to the web root from the temp folder? Можем ли вы переместить файл из папки темп в корень сайта. Если удалось пересоздать файл, то да, иначе нет.
		if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
			// Yes!
			$html .= "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
		}
		else {
			// No
			$html .= '<pre>Your image was not uploaded/Ваше изображение не было загружено</pre>';
		}

		// Delete any temp files. Удаление темп файла.
		if( file_exists( $temp_file ) )
			unlink( $temp_file );
	}
	else {
		// Invalid file. Неверный файл.
		$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images/Ваше изображение не загружено. Используйте только файлы с расширением JPEG или PNG.</pre>';
	}

Рекомендации по защите и подведение итогов

Во-первых, нельзя позволять загружать исполняемые файлы. Если вам необходима такая функция, то не давайте вашему приложению запускать исполняемые файлы. Второе, проверяйте тип файла, он должен быть ожидаемым, т.е. если ваше поле должно загружать только картинку, значит тип файла должен быть с расширением картинки. Третье, пересоздавайте загруженный файл.