История одного вскрытия: как мы ревёрсили Hancitor

в 12:37, , рубрики: ida pro, Блог компании ВышТех, реверс-инжиниринг, троян, Учебный процесс в IT

История одного вскрытия: как мы ревёрсили Hancitor - 1

Для тех, кто уже наигрался с задачками crackme, мы подвезли свежего троянца. В дикой природе загрузчик Hancitor еще встречается в своей естественной среде обитания — спам-рассылках. В настоящее время он активно используется для прогрузки банковского трояна Panda, который является модификацией небезызвестного Zeus.

В один холодный летний вечер мы встретились с ним лицом к лицу, просматривая почтовый спам. Если любите смотреть, что там у вредоносов «под капотом», почитайте наш новый реверс-разбор.

Наш вредоносный документ выглядит так:

История одного вскрытия: как мы ревёрсили Hancitor - 2

По умолчанию запуск макросов блокируется, поэтому система предупреждает: «Макросы были отключены». Однако содержимое письма уверяет, что макросы следует включить. Что ж, давайте сделаем это. После нажатия на кнопку Enable Content («Включить») происходит инфицирование компьютера. А теперь посмотрим, что за макрос выполняется и как именно происходит заражение. Это можно сделать сразу в ворде, перейдя на вкладку View->VacrosView Macros.

История одного вскрытия: как мы ревёрсили Hancitor - 3

Можно поступить более профессионально – заюзать olevba из пакета oletools. Установить пакет можно там же по ссылке. Далее набираем olevba doc_file –c –decode >source.txt и получаем исходник макроса.

История одного вскрытия: как мы ревёрсили Hancitor - 4

По коду сразу хочется сказать, что троян относится к классу даунлоадеров. Скрипт просто выкачивает откуда-то малвару. Чтобы это доказать, давайте раскодируем base64 строки. Нам удобнее делать это сразу через hiew, чтобы сто раз не копипастить. Для этого мы используем специальный плагин, который описан тут. Вот что получилось:

История одного вскрытия: как мы ревёрсили Hancitor - 5

Это вредоносный скрипт, разбитый на две части и сохраненный, как 1.hta. В принципе нам особо не интересно, как он работает. Тут всё довольно банально. Единственный важный момент – определить URL, по которому происходит скачивание вредоносного файла. Давайте его попробуем найти, так как эта информация может быть полезна безопасникам. Они могут добавить сетевые правила на блокировку запросов по таким URL, что может спасти компанию от заражения.

Ха! Ссылок никаких нет. Но откуда тогда берётся файл 6.exe, который запускается 1.hta? Пришло время ещё раз взглянуть на наш доковский файл в hiew, но уже внимательнее:

История одного вскрытия: как мы ревёрсили Hancitor - 6

Да, в доковском документе встроен exe-шник. Да ещё и сильно упакованный. Поясним, что происходит. Макрос дропает вредоносный скрипт 1.hta в папку Temp и запускает его, а 1.hta запускает в свою очередь 6.exe из своей директории. Ясное дело, что 6.exe – это тот самый запакованный PE-шник, который мы видим на скрине. Но нам сейчас интересно, как 6.exe дропается в %temp%. А происходит это из-за интересной особенности в пакете Microsoft Office. Дело в том, что в любой OLE — документ может быть встроен любой другой файл в формате Ole10Native.

Если это так, то MS-Office при запуске сам дропает встроенный таким образом файл в папку %temp% под именем, указанным в заголовке структуры Ole10Native. Давайте глянем на этот объект. Нам помог плагин к FAR-manager – OLE2Viewer. Открываем в плагине наш вредоносный документ, переходим в каталог ObjectPool _1593522492 и видим вот что:

История одного вскрытия: как мы ревёрсили Hancitor - 7

Скопируем этот файлик (Ole10Native) и откроем в hiew.

История одного вскрытия: как мы ревёрсили Hancitor - 8

Тут мы видим под каким именем дропнется в папку %temp% наш OLE-объект – 5c.pif. А теперь вернёмся к нашему макросу. Его задача – запустить дропнутый exe-шник.

История одного вскрытия: как мы ревёрсили Hancitor - 9

На самом деле, если быть точным, то макрос запускает дропнутый файл не всегда через 1.hta, а ещё и так: Shell «cmd.exe /c  ping localhost -n 100 && » & Environ(«Temp») & "6.pif", vbHide

История одного вскрытия: как мы ревёрсили Hancitor - 10

Способ запуска, как мы видим, зависит от присутствия следующих процессов в системе: bdagent.exe и PSUAMain.exe. А зачем тогда нужен ping localhost -n 100? А это древнегреческий трюк для создания искусственной задержки, на всякий случай. Обычно его вариации используется для самоудаления.

На данном этапе мы разобрали схему работы вредоносного документа. Нам стало ясно, что сам документ не относится к ВПО типа Trojan-Downloader, как казалось на первый взгляд, а подходит к типу Trojan-Dropper. Вот что получилось при запуске:

История одного вскрытия: как мы ревёрсили Hancitor - 11

Сейчас осталось разобрать сам payload. Мы уже поняли, что наш троян упакован, поэтому мы должны его сначала распаковать. Обычно этот процесс делится на две стадии:
1. Снятие распакованного дампа + восстановление таблицы импорта.
2. Анализ и чистка глобальных переменных.

Второй этап нужен нам потому, что существует множество API-функций, которые в результате своей работы нам возвращают неповторяющиеся значения. К примеру, CreatHeap вернёт нам дескриптор кучи, который далее будет использоваться для работы. Очень часто в коде есть проверки типа: если дескриптор кучи == 0, то получить дескриптор кучи, иначе использовать уже инициализированный. Но на момент снятия дампа переменная, содержащая данный дескриптор, была уже им инициализирована и в тот момент дескриптор был валиден. Когда мы попробуем запустить снятый дамп, переменная с нашим дескриптором будет содержать старое значение, т.е не равное 0, а значит пройдёт проверку.

После этого, как только программа попытается использовать этот дескриптор, ОС бросит исключение, и программа вывалится с ошибкой. Так вот, чтобы такого не было, нужно эти переменные занулить. Обычно они находятся в секции данных, доступной на запись. Наверное вы предложите открыть в hex-редакторе данную секцию и всё затереть нулями? Вы отчасти правы, но не стоит совершать необдуманных действий. Есть такие трояны, которые производят проверку переменных не на 0, а на рандомный DWORD. И в зависимости от того, проходит проверка или нет, предпринимаются различные действия. За примерами далеко ходить не надо. Достаточно взглянуть на Cridex (в последнее время куда-то исчез с радаров, видимо, полностью проапгрейдился до EMOTETA).

Итак, давайте запустим наш семпл (на виртуальной машине, естественно). Ещё желательно, чтобы трафик, который будет выходить из виртуальной машины, шел через VPN.

Мы запускаем наш троянчик, и он «просто» висит в процессе. Замечательно! Обычно трояны «по-быстрому» делают свои дела, а затем сразу завершаются и самоудаляются. Поэтому приходится искать места в коде, отвечающее за эти действия, ставить на них бряк, а потом дампить. В нашем случае мы можем сделать это просто так.

Для дампа используем замечательную утилиту – Process Dump, которую можно взять тут. Эта утилита не только находит все скрытые исполняемые модули и дампит их, но ещё и сама восстанавливает таблицу импорта. Утилиту нужно запускать от имени администратора таким образом: pd /pid xxxx, где xxxx – id троянского процесса. После чего, утилита сдампит все модули процесса. Мы удалили лишние и вот что осталось:

История одного вскрытия: как мы ревёрсили Hancitor - 12

Имя исполняемого файла троянского процесса – 1.exe. Оказывается, по адресу 0x2C0000 находился распакованный троян. Откроем его в hiew:

История одного вскрытия: как мы ревёрсили Hancitor - 13

Просто радуется глаз! Теперь файл распакован, это отчётливо видно. Таблица импортов также распозналась. Давайте откроем его в IDA-PRO.

История одного вскрытия: как мы ревёрсили Hancitor - 14

Некоторые функции мы уже переименовали, пока разбирали сэмпл. Первое, с чего начинает работу троян, — определяет адрес загрузки своего модуля. И действительно: откуда ему знать по какому адресу его stub распаковал? Делается это старой проверенной техникой – отлистыванием страницы памяти назад по 0x1000 относительно текущего адреса до тех пор, пока не наткнёмся на байты «MZ». Это работает, потому что исполняемые модули всегда загружаются ОС по адресу, кратному 0x1000. Проверьте сами. В нашем случае установлен лимит на 100 отлистываний.

История одного вскрытия: как мы ревёрсили Hancitor - 15

Если кто не понял, где тут отлистывание назад, то вот где:
result += 0xFFFFF000 эквивалентно result -= 0x1000.

После получения адреса загрузки своего модуля распакованный троян получает адреса нужных для своей работы функций. Для начала ищутся адреса двух функций – LoadLibraryA и GetProcAddress. Зная адреса этих функций, можно с помощью них получить все остальные. Эти функции находятся в библиотеке kernel32. Её адрес получается с помощью чтения первого (нулевой — ntdll, первый kernelbase и тд) элемента кольцевого списка, описывающего все загруженные в порядке инициализации модули структурой _LDR_DATA_TABLE_ENTRY. Указатель на список вытаскивается из PEBа.

История одного вскрытия: как мы ревёрсили Hancitor - 16

Получив адрес kernel32.dll (начиная с  Windows 7 – kernelBase.dll), троян может вручную распарсить её таблицу экспорта и найти нужные две функции, что предсказуемо выполняется в подпрограмме sub_EF1E60.

История одного вскрытия: как мы ревёрсили Hancitor - 17

Теперь взглянем на функцию, которую мы назвали getHeap.

История одного вскрытия: как мы ревёрсили Hancitor - 18

Тут мы наблюдаем как раз ту ситуацию, которую описывали выше. На момент дампа переменная hHeap содержала значение 600000h. Поэтому GetProcessHeap не вызовется. Вместо этого программа перейдёт на метку loc_EF11DD, где вызовется HeapAlloc с невалидным дескриптором, что даст нам ошибку. Поэтому, берём hex редактор и зануляем это число. Подобных мест мы насчитали шесть.

Далее у нас начинается самое интересное. Троян генерирует уникальный ID «клиента», на основании серийного номера жёсткого диска и MAC адреса. Информация получена здесь:

История одного вскрытия: как мы ревёрсили Hancitor - 19

Также получаем следующее: IP адрес, версия ОС, сетевое имя и имя пользователя. На основании всего этого формируется HTTP-запрос к админке, адрес которого мы пока не знаем. В строках его нет (даже в распакованном виде). А нет его там, потому, что он зашифрован в конфиге. Его адрес можно вытащить из кода:

История одного вскрытия: как мы ревёрсили Hancitor - 20

Конфиг весит 0x2008 байт и имеет такой формат: первые 8 байт – RC4 ключ, 0x2000 байт – зашифрованные данные.

История одного вскрытия: как мы ревёрсили Hancitor - 21

То, что используется алгоритм шифрования RC4, становится понятно из следующего листинга:

История одного вскрытия: как мы ревёрсили Hancitor - 22

Обратите внимание, что первые 8 байт сами по себе не являются ключом к RC4. Ключом является хеш SHA1 от этих байт. Также нужно обратить внимание на флаг 0x280011 к функции CryptDeriveKey. В MSDN есть оговорка по поводу данного флага:

История одного вскрытия: как мы ревёрсили Hancitor - 23

Из неё становится понятно, что старшие 16 бит этого флага задают размер ключа в битах. Т.е в байтах размер ключа такой: (0x280011 >> 16) / 8 = 5. Поэтому ключом будут являться первые пять байт от хеша от первых восьми байт конфига. Сдампим конфиг и напишем скрипт на python, который расшифрует нам его. Скрипт выглядит так:

История одного вскрытия: как мы ревёрсили Hancitor - 24

Результатом его работы стал файл config.rc4. Откроем его в hiew:

История одного вскрытия: как мы ревёрсили Hancitor - 25

Мы видим расшифрованный список админок. Первое слово – «19nep07» — номер билда. Под него отведено 16 байт. Далее идёт список URL админок, разделённых "|".

Итак, первое обращение к админке будет иметь такой формат:
GUID=3068075364164635648&BUILD=19nep07&INFO=WIN-56G04BL06SL @ WIN -56G04BL06SLReverse&IP=35.0.127.52&TYPE=1&WIN=6.1(x32

История одного вскрытия: как мы ревёрсили Hancitor - 26

Потом сформированный запрос отправляется первой в списке админке.

История одного вскрытия: как мы ревёрсили Hancitor - 27

Далее считывается ответ от админки, конечно, если она еще живая. Ответ должен быть в кодировке base64. Если это не так, то берётся следующая админка из списка. Иногда живые админки возвращают странный ответ:

История одного вскрытия: как мы ревёрсили Hancitor - 28

Знакомо, не правда ли? Да это же те самые числа! На самом деле одному автору известно, почему админка их начинает возвращать! В нормальном виде возвращается команда. К сожалению, с полной уверенностью нельзя сказать, какой будет формат ответа, так как трафика нет, все админки дохлые. Раскодированный файл дополнительно расксоривается на 0x7A:

История одного вскрытия: как мы ревёрсили Hancitor - 29

Ответ должен содержать команду. Если её нет, происходит обращение к следующей админке. Код команды кодируется как «x:», где x – буква кодирующая определённую комманду. Всего их 7: ‘r’,’l’,’e’,’b’,’d’,’c’,’n’. Рассмотрим команды «b» и «r».

В обработчиках этих команд есть одна и та же функция. Мы ее назвали, как GetExe. Вот как она выглядит:

История одного вскрытия: как мы ревёрсили Hancitor - 30

Думаю тут всё ясно. Троян совершает http-запрос и ему возвращается в ответе исполняемый файл в сжатом виде, который потом расжимается. После чего возможны вариации. В случае с командой «b», происходят следующие три действия:

1. Создание процесса svchost в замороженном виде

История одного вскрытия: как мы ревёрсили Hancitor - 31

2. Инжект в адресное пространство процесса скачанного модуля

История одного вскрытия: как мы ревёрсили Hancitor - 32

3. Передача управления на заинжекченный модуль

История одного вскрытия: как мы ревёрсили Hancitor - 33

В случае с командой «r», происходят следующие три действия:
1. Скачивание исполняемого файла вызовом функции GetExe.
2. Сохранение скачанного файла в темповскую папку под случайным именем.
3. Запуск дропнутого файла.

История одного вскрытия: как мы ревёрсили Hancitor - 34

Done.

P.S. В следующий раз мы можем сделать разбор либо «Панды», либо какого-нибудь шифровальщика. Напишите в комментариях, какие вредоносы вас больше всего интересуют (в исследовательских целях, конечно же).

Автор: HackerU

Источник

* - обязательные к заполнению поля