Защита от внедрения PHP-кода в картинки (JPEG, GIF, PNG)

На «Хабре» всплыла тема атаки, старой как мир — заливаем на сайт картинку, у которой внутри (в поле комментариев) есть ПХП-код, потом всякими трюками этот код запускаем. Валидатор картинок на сайте такую картинку пропускает — это вполне валидная картинка.

Во всех форматах принятых в вебе, есть поле, позволяющее вставить такие данные. В JPEG есть специальные секции APPn и COM, в PNG — iTXt, tEXt и можно создавать произвольные секции, в GIF — блоки расширений, в SVG можно использовать обычный XML-комментарий.

В качестве абсолютного метода защиты на «Хабре» предлагается пропустить картинку через какую-либо графическую программу, чтобы убедиться, что все дополнительные данные исчезли. Мне кажется, плохой способ — медленный, а в случае JPEG это ещё и скажется на качестве.

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

Мне кажется, я придумал более удачный метод защиты.

В загруженный файл можно добавить первой секцией (сразу после заголовка) свой собственный кусок ПХП-кода с вызовом __halt_compiler эта специальная конструкция останавливает интерпретатор в этой точке и всё остальное содержимое файла дальше не интерпретируется:
# смотрим что внутри файла
bolk@Bolk ~ $ hexdump -Cn128 sample.jpg 
00000000  ff d8 ff e0 00 10 4a 46  49 46 00 01 01 00 00 01  |......JFIF......|
00000010  00 01 00 00 ff fe 00 1a  3c 3f 70 68 70 20 5f 5f  |........<?php __|
00000020  68 61 6c 74 5f 63 6f 6d  70 69 6c 65 72 28 29 3b  |halt_compiler();|
00000030  ff db 00 43 00 02 02 02  02 02 01 02 02 02 02 03  |...C............|
00000040  02 02 03 03 06 04 03 03  03 03 07 05 05 04 06 08  |................|
00000050  07 09 08 08 07 08 08 09  0a 0d 0b 09 0a 0c 0a 08  |................|
00000060  08 0b 0f 0b 0c 0d 0e 0e  0f 0e 09 0b 10 11 10 0e  |................|
00000070  11 0d 0e 0e 0e ff db 00  43 01 02 03 03 03 03 03  |........C.......|

# запускаем
bolk@Bolk ~ $ php sample.jpg
????JFIF??
Т.е. на экран выводится бинарный заголовок JPEG, первая секция, а потом интерпретатор дальше просто не идёт. Будет дальше ход хакера или нет, он не проинтерпретируется.
2 августа 2012 16:36

Artjom Kurapov (kurapov.name)
4 августа 2012, 19:11

Остаётся открытым вопрос как этот самый код внедрить в нужное место, "после заголовка"

aktuba (инкогнито)
4 августа 2012, 19:16

А не проще заливать картинки в папку, в которой запрещена обработка php?

bolk (bolknote.ru)
5 августа 2012, 07:47, ответ предназначен Artjom Kurapov (kurapov.name):

Остаётся открытым вопрос как этот самый код внедрить в нужное место, «после заголовка»
Это же просто совсем. Код что ли написать? :)

bolk (bolknote.ru)
5 августа 2012, 07:49, ответ предназначен aktuba

А не проще заливать картинки в папку, в которой запрещена обработка php?
Есть масса уязвимостей при которых удаётся выполнить только локальный код. Что-то вроде:

$file = 'pages/' . $_GET['page'];
if (is_file($file)) include $file;

При этом мы можем залить на такой хостинг картинку в коде и подключить её через указание somepage.php?page=../../userimages/hack.jpg

Artjom Kurapov (kurapov.name)
5 августа 2012, 12:21, ответ предназначен bolk (bolknote.ru):

Это же просто совсем. Код что ли написать? :)
Да, для меня это не тривиально.. Или это вне зависимости от формата можно где-то с десятого-двадцатого символа просто врезать произвольный текст?

bolk (bolknote.ru)
5 августа 2012, 13:29, ответ предназначен Artjom Kurapov (kurapov.name):

Примерно такой алгоритм, ага.

http://ibnteo.klava.org/ (инкогнито)
5 августа 2012, 22:59

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

bolk (bolknote.ru)
5 августа 2012, 23:21

Подскажете как настроить сервер таким образом? И обратите внимание на этот вот мой коментарий: http://bolknote.ru/2012/08/02/~3703#n35672

Василий Топоров (инкогнито)
6 августа 2012, 14:11

Правильно ли я понимаю, что практически любой сайт, на котором можно заливать картинки, потенциально уязвим? Если мы просто используем стандартные функции php для работы с графикой и файлами).

Karsonito (инкогнито)
6 августа 2012, 14:17, ответ предназначен Василию Топорову

Все зависит от реализации и настроек сервера.
Способов взломать много, нужно смотреть конкретный пример.

bolk (bolknote.ru)
6 августа 2012, 15:23, ответ предназначен Василию Топорову

Правильно ли я понимаю, что практически любой сайт, на котором можно заливать картинки, потенциально уязвим? Если мы просто используем стандартные функции php для работы с графикой и файлами).
Нет, конечно. Всё зависит от сочетания факторов. Например, см. мой комментарий выше: http://bolknote.ru/2012/08/02/~3703#n35672 Если есть возможность залить какие-то файлы и такой промах со стороны программиста — уязвимость есть. Или может быть некорректно настроен Апач (там некоторые настройки по-умолчанию могут дать уязвимость).

masterspammer (masterspammer.livejournal.com)
7 августа 2012, 07:03

Я правильно понял, что эта защита от двух разных но схожих дыр?

1. запуск кода на выполнение вебсервером (если его настройки удаётся обмануть).

2. включение кода картинки как модуля/библиотеки (если удалось залить "злой" код только в виде картинки).

bolk (bolknote.ru)
7 августа 2012, 07:53, ответ предназначен masterspammer.livejournal.com:

Да, правильно.

НЕГЕНГ (инкогнито)
26 апреля 2013, 10:13

<html>

Евгений Степанищев (bolknote.ru)
26 апреля 2013, 12:28, ответ предназначен НЕГЕНГ

Ещё один тупой.

Авакян Овсеп (инкогнито)
14 ноября 2014, 13:07

Спасибо автору за статью. Метод интересный ) Сам столкнулся с такой проблемой, но решить ее хочу на уровне веб сервера. Если с Apache все более-менее понятно (там можно использовать настройки .htaccess), то как быть с Nginx? Кто-нибудь писал конфигурацию для запрета выполнения кода из определенного каталога?

Евгений Степанищев (bolknote.ru)
14 ноября 2014, 14:17, ответ предназначен Авакяну Овсепу

Написать ему там пустой location.

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)