По следам MS IE OBJECT tag exploit'а

         

Докапываемся до истины


Запускаем OllyDbg, в меню "options" выбираем пункт "just-in-time debugging" и в появившимся диалоговом окне нажимаем кнопки "make OllyDbg just-in-time debugger" и "confirm before attaching". Выходим из отладчика и загружаем IEdie2-3 в IE.

Через некоторое время появляется диалоговое окно с сообщением, что программа сделала что-то не так (см.рис. 8). "ОК" — завершает IE, "отмена" — запускает just-in-time отладчик.

Рисунок 8 сообщение о критической ошибке с предложением запустить just-in-time отладчик

Очутившись в отладчике, мы оказываемся в уже знакомой нам точке сбоя по адресу 75ACC4DAh (см. листинги 6, 9 и 10). Многократные запуски IE показывают, что сбои происходят в самых разных местах, но всегда после вызова функции GetDocPtr(), а иногда и внутри самой GetDocPtr(). Как вам нравится следующее?

EAX 00000000  EBX 000BA14C  ECX FFFFFFFF  EDX 00E50764  ESP 0006DB9C

EBP 0006DBCC  ESI 00E552B0  EDI 00E552B0  EIP 75A92128 mshtml.75A92128

75A9211D      8B41 10              MOV EAX,DWORD PTR DS:[ECX+10]

75A92120      8B49 1C              MOV ECX,DWORD PTR DS:[ECX+1C]

75A92123      F6C1 02              TEST CL,2

75A92126      74 03         JE SHORT mshtml.75A9212B

75A92128      8B40 0C              MOV EAX,DWORD PTR DS:[EAX+0Ch]    ; ß место сбоя

75A9212B      F6C1 01              TEST CL,1



75A9212E      74 03         JE SHORT mshtml.75A92133

75A92130      8B40 2C              MOV EAX,DWORD PTR DS:[EAX+2C]

75A92133      C3            RETN

00E552B0  00000000   стек

à 0006DB9C   75ACC4C8  RETURN to mshtml.75ACC4C8

00E552B4  00000000         0006DBA0   00E552B0

00E552B8  00000001         0006DBA4   75ACC889  RETURN to mshtml.75ACC889

00E552BC  FFFFFFFF         0006DBA8   00E552B0

00E552C0  00000000         0006DBAC   000BA054

00E552C4  00000000 ß дамп  0006DBB0   75A9BFD3  RETURN to mshtml.75A9BFD3

00E552C8  00000000         0006DBB4   00000004


00E552CC  FFFFFFFF         0006DBB8   00000007

00E552D0  00000000         0006DBBC   000BA054

00E552D4  00E55524         0006DBC0   00000001

00E552D8  00000652         0006DBC4   000B9EE8

00E552DC  00000000         0006DBC8   000B0001

Листинг 11 just-in- time отладчик показывает обрушение, произошедшие внутри GetDocPtr



Рисунок 9 just-in-time отладчик показывает обрушение, произошедшие внутри GetDocPtr

Нажав <Shift-F9> мы можем проигнорировать исключение и продолжить выполнение программы, только ни ей, ни нам лучше от этого не станет, ведь структуры данных превратились в бессмысленную мешанину байт и хрен его знает в какой момент они были разрушены.

Приходится реконструировать скелет динозавра буквально по "косточкам". Прежде всего нам необходимо выяснить куда указывал ECX в момент вызова GetDocPtr(). Смотрим на стек — на его вершине находится адрес возврата в материнскую процедуру 75ACC4C8h. Ходим сюда дизассемблером (или самим отладчиком по <CTRL-G>, 75ACC4C8h) и видим, что перед вызовом функции GetDocPtr регистр ECX был сохранен в регистре ESI:

.text:75ACC4C1             mov    esi, ecx

.text:75ACC4C3             call   GetDocPtr@CElement@@QBEPAVCDoc@@XZ;CElement::GetDocPtr()

.text:75ACC4C8             mov    si, [esi+6Ch]

Листинг 12 исследование материнской функции, вызывающей GetDocPtr

Следовательно, в момент сбоя регистр ESI указывает на структуру, из которой загружаются регистры ECX и EAX. Тройным нажатием <TAB> переходим в окно дампа, нажимаем <CTRL-G> и вводим регистр ESI или его непосредственное значение 00E552B0h (см. листинг 11 или рис. 9). Это и есть та структура данных, с которой мы уже сталкивались в дизассемблере, и которая, судя по карте памяти, лежит где-то в куче (на самом деле, OllyDbg не умеет работать с кучей и необходимо иметь определенный исследовательский опыт, чтобы выделить блоки динамической памяти из общей массы, soft-ice показал бы намного больше информации, но мы уже решили использовать Olly, так что не будем менять коней на переправе).



Команда MOV EAX, [ECX+10], которая должна возвращать указатель, возвратила ноль, в результате чего следующая за ней команда MOV EAX, [EAX+0Ch] оказалась источником сбоя. Это самое настоящее разрушение объекта CElement, но вот кто его разрушил и почему нам еще предстоит узнать. Во всяком случае, объект не был затерт строкой "AAA...AAA", иначе в дампе присутствовали бы соответствующие ей ASCII-коды 41h, а их там нет. Как это нет?! Куда подевалась наша строка? А вот сейчас найдем ее в памяти и узнаем!

Нажимаем <ALT-M> для вызова окна "memory", переходим в начало адресного пространства по клавише "home" и давим <CTRL-B> для поиска. Искать, конечно же, нужно в Unicode. Строка находится дважды. Первый раз в стеке по адресу 000С00F0h вместе с "<OBJECT type=" и всеми остальными строками, второй раз — в куче по адресу 00E51A60h (см. рис. 10), где следом за ней идет еще одна строка "AAA...AAA" и... больше ничего. Ага! Судя по всему, IE смог обработать только два объекта, после чего наступило переполнение, ведущее к исключению и аварийному завершению работы. Обратите внимание, что строка "AAA...AAA" (00E50600h) лежит в непосредственной близости от структуры данных, на которую указывает ECX – 00E552B0h однако, их разделяет порядочное количество байт и если переполнение происходит, то явно не здесь. Что ж, будем копать дальше! Тем более, что у нас есть замечательная возможность начать следствие до начала преступления, установив точку останова на...



Рисунок 10 поиск строки AAA...AAA в памяти

...постойте, а на что мы будем ее устанавливать?! Уж точно не на функцию GetDocPtr(), поскольку к моменту ее вызова данные _уже_ разрушены. Было бы замечательно брякнуться непосредственно на сам блок памяти и посмотреть кто его разрушает, но к несчастью он выделяется динамически и его адрес непредсказуем (тем более, как уже отмечалось, сбои происходят в различных местах).



Уж не знаю, чтобы бы мы стали делать, ни будь в нашем распоряжении отладочных символов, но ведь они есть! Мы знаем, что блок памяти с падучей структурой данных инициализируются конструктором класса CElement, к которому принадлежит функция GetDocPtr(), поэтому, мы должны найти конструктор, установить на него точку останова и следить за всеми создаваемыми объектами. Возвращаемся в IDA Pro, давим <Ctrl-Page Up> для перехода в начало листинга, нажимаем <ALT-T> (поиск в листинге) и пишем "__thiscall CElement::CElement" (так объявляется конструктор по правилам языка Си++). Не проходит и минуты, как IDA Pro находит его по адресу 75AA321Bh (вообще-то отождествить конструктор можно и без отладочных символов, см. "фундаментальные основы хакерства", электронную копию которых можно бесплатно скачать с ftp://nezumi.org.ru, но на это требуется время, которого у нас нет, а в битве за exploit'ы каждая секунда играют роль, чтобы захватить управление уязвимыми машинами раньше всех остальных, создать огромную армию дронов и почувствовать себя Чингисханом):

.text:75AA321B ; public: __thiscall CElement::CElement(enum ELEMENT_TAG, class CDoc

*)

.text:75AA321B ??0CElement@@QAE@W4ELEMENT_TAG@@PAVCDoc@@@Z proc near

.text:75AA321B             push   esi

.text:75AA321C             mov    esi, ecx

.text:75AA321E             call   ??0CBase@@QAE@XZ ; CBase::CBase(void)

.text:75AA3223             mov    eax, [esp+arg_4]

.text:75AA3227             mov    dword ptr [esi],CElement@@6B@;const CElement::`vftable'

.text:75AA322D             mov    [esi+10h], eax

.text:75AA3230             inc    dword ptr [eax+8]

.text:75AA3233             call   ?_IncrementObjectCount@@YGXXZ;_IncrementObjectCount()

.text:75AA3238             mov    eax, [esi+18h]

.text:75AA323B             mov    ecx, [esp+arg_0]

.text:75AA323F             xor    ecx, eax

.text:75AA3241             and    ecx, 0FFh

.text:75AA3247             xor    ecx, eax

.text:75AA3249             mov    eax, esi



.text:75AA324B             mov    [esi+18h], ecx

.text:75AA324E             pop    esi

.text:75AA324F             retn   8

.text:75AA324F ??0CElement@@QAE@W4ELEMENT_TAG@@PAVCDoc@@@Z endp

Листинг 13 дизассемблерный текст конструктора объекта CElement

Переключаемся на отладчик, переходим в окно CPU, давим <CTRL-G>, вводим адрес конструктора "75AA321B", устанавливаем точку останова на начало функции и перезапускаем отладчик по <Ctrl-F2>. Причем, точка останова должна быть не программной (та, что ставится по <F2>), а непременно аппаратной (подводим курсор к строке 75ACC4C0h, нажимаем <Shift?F10>, в появившимся контекстом меню выбираем breakpoint à hardware, on execution). Поскольку, MSHTML.DLL загружается динамически, программная точка останова (представляющая собой машинную инструкцию INT 03h с опкодом CCh) безжалостно затирается системным загрузчиком и потому не срабатывает.

К своему стыду, OllyDbg не сохраняет аргументы командой строки отлаживаемого процесса при его перезапуске, поэтому IE уверенно стартует с домашней страницы и exploit приходится загружать вручную через "файл à открыть à обзор à IEdie2-3.html". На этот раз IE уже не грохается, а мирно вываливается в отладчик по точке останова!

Конструктор вызывается множество раз и чтобы проследить за процессом инициализации каждого из объектов необходимо перейти в окно дампа и сказать <CTRL-G>, "ECX", где ECX – регистр в котором конструктору передается указатель на объект для конструирования.

Начинаем трассировать программу, двигаясь словно саперы по минному полю и обращая внимание на малейшие нюансы оперативного окружения. Оказывается, что конструктор выполняет только первичную инициализацию и над объектом работает множество функций, каждая из которых может оказаться источником разрушения. Чтобы сузить круг поиска сосредоточимся на одном-единственном поле, расположенном по смещению 10h от начала объекта (именно отсюда функция GetDocPrt считывает инвалидный указатель, приводящий к сбою).


Как показывает трассировка, его инициализация осуществляется еще в конструкторе и делает это пара команд: MOV EAX,[ESP+ARG_4]/MOV [ESI+10H],EAX. Все ясно! Надо установить условную точку останова по этому адресу, срабатывающую если EAX указывает на инвалидный регион. Наблюдая за разрушенным блоком, можно прийти к заключению, что поле, расположенное по смещению 10h, принимает произвольные значения от 00h до ~100h.

Поскольку, OllyDbg условные аппаратные точки останова еще не поддерживает, приходится прибегать к помощи могущественного soft-ice. Запускаем IE с "домашней страницы", вызываем soft-ice нажатием на <CTRL-D>, переключаем контекст командой "ADDR IEXPLORE", устанавливаем условную точку останова по исполнению "BPM 75AA322D X IF EAX < 100", выходим из отладчика и открываем в IE наш подопытный "iedie2-3.html". soft-ice ни фига не всплывает, а IE все гавно грохается. Вот сволочь! Значит, ошибка сидит не в конструкторе и не в вызывающей его функции. Это переполнение, настоящее переполнение, но очень хитрое переполнение и, чтобы его запеленговать, необходимо изготовить специальный инструмент — свой собственный отладчик или плагин для OllyDbg или soft-ice, который бы выполнял следующие действия:

q       устанавливал аппаратную точку останова на конструктор CElement::CElement и запоминал указатель, передаваемый ему через регистр ECX;

q       при выходе из конструктора отбирал у первой страницы блока памяти все атрибуты доступа (PAGE_NOACCESS);

q       отслеживал исключения, возникающие при обращении к странице и следил за полем 10h, разрешая запись только действительных указателей;

q       при обнаружении попытки записи недействительного указателя — передавал управление отладчику, сигнализируя об ошибке тем или иным способом;

Описанная технология позволяет следить за огромным числом блоков памяти, практически без снижения производительности.Написать и отладить плагин можно буквально за вечер, ну максимум за два. Считайте это своим домашним заданием или... ждите, когда в сети появятся готовые боевые exploit'ы.


Содержание раздела