Шелл-код Agent Tesla. Изучаем новые приемы реверса малвари в Ghidra

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
В сегодняшней статье я покажу, что делать с извлеченным из вредоноса шелл‑кодом: будем его запускать и отлаживать. Разберемся, как работать с функцией WinAPI, если она вызывается по хешу, а в процессе отладки расшифруем полезную нагрузку и научимся извлекать инфу из обфусцированных файлов .NET.

Мы продолжим работу, начатую в моей прошлой статье, где мы начали реверсить Agent Tesla и вытащили шелл‑код из инсталлятора NSIS.

ЗАГРУЖАЕМ ШЕЛЛ-КОД В ДИЗАССЕМБЛЕР​

В прошлый раз мы договорились использовать Ghidra, продолжим традицию и в этой статье. Итак, загружаем файл c шелл‑кодом в дизассемблер и видим, что начало шелл‑кода не дизассемблировалось: перед нами просто необработанные байты. Это не нужно игнорировать, потому что неправильное начало анализа может поломать кучу остального проанализированного кода.
Результат загрузки шелл-кода, первые байты не дизассемблировались


Не страшно, просто ставим курсор на начало байтовой строки и выбираем в контекстном меню Disassemble, дальше Ghidra сделает все за нас.

Исправили дизасм шелл-кода


После того как привели анализ в порядок, видим с самого начала шелл‑кода переход в функцию. Идем в нее и смотрим в начало — там видим портянку кода, где данные помещаются на стек. Приводим эти данные в удобочитаемый вид (приводим числа в char в контекстном меню), видим появившееся название файла djdqvq.sra. Вспоминаем, что это один из трех файлов, которые находились в NSIS-инсталляторе. Стало быть, шелл‑код работает с этим файлом.

Шелл-код работает с djdqvq.sra


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

Энтропия djdqvq.sra


Ладно, пока к этому файлу не подступиться, не разобрав шелл‑код, поскольку именно он — ключик к этому файлу. Возвращаемся в Ghidra и листаем уже открытую функцию вниз, видим работу с большими числами.

Вызовы WinAPI по их хешу


Наметанный глаз сразу заметит, что это похоже на вызовы WinAPI по хешу — одну из техник сокрытия использования WinAPI в коде. Кроме того, если посмотреть по ссылкам, есть масса похожего кода с передачей хеша в функцию FUN_00000073, то есть эта функция еще и вызывается по всему коду. Сомнений в назначении FUN_00000073 нет, но есть проблема: статическим анализом это не победить (писать скрипт, имплементирующий алгоритм формирования хеша, не предлагать), нужно идти в отладчик.

Вызовы FUN_00000073 с передачей хеша в качестве аргумента находятся по всему коду



ЗАГРУЖАЕМ ШЕЛЛ-КОД В ОТЛАДЧИК​

Отладчик помогает разобраться в коде по ходу его работы, но как заставить работать дамп шелл‑кода? Это же, мягко говоря, не обычный исполняемый файл, и, если загрузить этот дамп прямо в отладчик, он скажет «фи» и выдаст ошибку. Не беда, есть несколько способов отладки шелл‑кода:
  • написать тулзу, которая прямо в своем коде вызывает шелл‑код, хранящийся как байтовый массив;
  • использовать уже готовый инструмент, который создаст из шелл‑кода PE-файл, а дальше его можно будет загрузить и отладить как обычный файл;
  • использовать тулзу, которая загружает шелл‑код в память и исполняет его в своем контексте, а отладчиком мы присоединяемся к ее процессу.
Независимо от выбора в итоге мы должны в итоге оказаться в отладчике, в начале шелл‑кода.


Декодируем хеши WinAPI​

Отладчик x86dbg (x64dbg) — замечательный инструмент, достойный преемник всеми любимого OllyDbg. Почему бы нам не заставить его работать на нас, автоматизируя процесс декодирования хешей? Давай попробуем, но для этого придется немного покопаться в листинге кода и найти некоторые закономерности.

Разумеется, нам интересна функция вызова хендлов WinAPI по их хешу, в Ghidra она называется FUN_00000073 — это значит, что RAW-смещение составляет 0x73 байта от начала шелл‑кода.

Чтобы найти эту функцию в x86dbg, нужно к начальному адресу памяти, по которому у нас загружен шелл‑код, прибавить 0x73. Например, у меня шелл‑код загрузился по адресу 0x00FB0000, следовательно, чтобы поставить точку останова на начало функции, в x86dbg нужно ввести команду bp 0x00FB0000 + 0x73 и запустить выполнение либо долистать до этого адреса вручную и нажать F2.

Разумеется, при вызове функции на стеке будут лежать ее аргументы, которые можно видеть в окне обзора стека в x86dbg. Там мы можем увидеть передаваемые значения хешей и файл системной библиотеки, откуда берется адрес получаемой функции.

Если позволить выполниться этой функции до конца (до ret), то можно увидеть, что в EAX лежит результат ее работы — адрес WinAPI ExitProcess. Если поставить бряк на то место, где мы сейчас стоим (инструкция ret функции FUN_00000073, ее последняя инструкция), и пустить отладчик дальше, то в EAX мы увидим следующее декодированное имя WinAPI.

Получается, когда мы стоим на смещении RAW 0xC9 (у меня он 0x00FB0000 + 0xC9 = 0x00FB00C9), то в EAX будет адрес декодированной WinAPI.

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

Входим в режим редактирования условия брейк‑пойнта, установленного на смещении 0xC9, и задаем условия действий.

Редактируем точку останова x86dbg и учим его декодировать WinAPI



В качестве самописца будет выступать лог отладчика. Основные поля, которые мы заполнили:
  • Log Text со значением { a:eax };
  • Command Text со значением bp eax;run;.
Значение a:eax говорит отладчику записывать в лог содержимое EAX, трактовать его как адрес и получать информацию о нем точно так же, как это происходит в окне отображения регистров, когда происходит останов на ret в функции вызова WinAPI по хешу. Вторая строчка просто говорит отладчику ставить брейк‑пойнт, то есть останавливаться в конце этой функции.

Я видел, как люди пишут декодер хешей WinAPI на Python в виде скрипта для IDA Pro, чтобы разматывать алгоритм хеширования, а затем реализуют его в скрипте. Техника, которую я демонстрирую здесь, намного проще, но имеет тот же эффект, потому что при такой декодировке тоже можно будет разметить листинг дизассемблера, только вручную.

Исследуем djdqvq.sra​

Теперь, трассируя шелл‑код, мы можем начать расшифровку оставшегося файла djdqvq.sra, который приберегли с самого начала.

Внимательно смотрим на трассу выполнения (лог отладчика), помечаем интересные функции, чтобы при повторном заходе к ним вернуться. Одна из таких функций — lstrcatW, она собирает две строки в одну. Если посмотреть на стек, то сразу увидим ее аргументы.

Аргументы функции lstrcatW, отображаемые в окне стека x86dbg


Понятно, что дальше идет код копирования файла djdqvq.sra из установщика во временный каталог (по сформированному функцией lstrcatW пути), который виден на скриншоте. Дальше идет его открытие при помощи CreateFileW. Так как дело происходит в извлеченном шелл‑коде, конечно, будет ошибка, потому что файл не скопируется.

Чтобы наша отладка не сломалась, нужно просто скопировать файл djdqvq.sra во временную папку самостоятельно (естественно, все делаем в виртуальной машине, отключенной от интернета). Не забываем, что djdqvq.sra зашифрован, поэтому далее идет вызов VirtualAlloc, выделяющий память для расшифрованного кода. Теперь останавливаемся на инструкции ret функции VirtualAlloc.

Выделение буфера памяти для расшифрованного кода из djdqvq.sra



Дальше стандартный алгоритм действий:
  1. Извлекаем функцией VirtualAlloc из регистра EAX адрес выделенной памяти.
  2. Ставим точку останова по доступу на начало выделенной области памяти.
  3. После установки брейк‑пойнта отпускаем программу на выполнение. Основная цель здесь — отловить момент, когда полезная нагрузка будет расшифрована или начнет выполняться. Нужно быть готовым к тому, что точка останова сработает несколько раз. В памяти ждем появления осмысленного кода или заголовка исполняемого файла PE (Portable Executable).
  4. В конце дампим полезную нагрузку в файл силами x86dbg (Dump Memory To File — так же, как мы сбрасывали шелл‑код в предыдущей статье).
После прохождения этих шагов загрузим полученный файл в DiE.

Расшифрованный djdqvq.sra в DiE


DiE сразу же анализирует весь файл, и мы наблюдаем такую картину:
  • файл ничем не запакован, энтропия нормальная;
  • имеется ресурс, который был распознан как нечто с заголовком PE (!);
  • ресурс написан на .NET;
Раньше DiE не сканировал ресурсы, такая возможность появилась в последних версиях. Всегда используй последние версии: помимо новых багов, в них могут быть интересные возможности! Но что, если у тебя старый DiE? То, что в файле производится работа с ресурсами, можно определить, например, по импортируемым API-функциям.

При помощи Process Hacker вытаскиваем ресурс, который оказывается исполняемым файлом .NET. В принципе, тут есть два подхода: попробовать декомпилировать ресурс и надеяться, что он не обфусцирован (иначе еще придется разбираться с обфускацией), либо попытаться динамически вытащить инфу из этого файла, используя стороннее ПО. Естественно, выберем второй путь: он быстрее и проще, а время, как известно, деньги.

GarbageMan​

Классические вирусы обычно пишут на C/C++, ассемблере и прочих низкоуровневых языках. Но это не обязательно делает их наиболее эффективными. Малварь, написанная на .NET, может доставить кучу проблем, потому что методы обфускации этого кода стали весьма могучими.

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

Чтобы как‑то решать такие проблемы, была создана замечательная программа под названием GarbageMan. Она нам и поможет в анализе файла. Дело в том, что рабочий Heap и стек .NET-приложения представляют собой настоящий кладезь информации о типах данных, связанности объектов друг с другом и прочих вещах. При помощи GarbageMan мы будем ее извлекать и исследовать.

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

Загрузим наш .NET-файл в GarbageMan и наделаем несколько снимков его работы с промежутком в несколько сот миллисекунд.

Используемая почта, почтовый сервер и порт

Список интересующих Agent Tesla сервисов


У меня получилось извлечь список сервисов, с которыми взаимодействует Agent Tesla, его почту, почтовый сервер и много другой информации. Неплохо за минуту работы приложения!

ВЫВОДЫ​

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

Автор @xtahi0nix aka Nik Zerof
источник xakep.ru
 
Сверху Снизу