Исследование Badger BRC4
Сегодня проанализируем шелл-код Badger Brute Ratel C4, файл. Проведем динамичесий и статический анализ вредоносного кода, восстановим алгоритм декодирования функций (API Hashing), расшифруем полезную нагрузку и конфигурацию модуля.
Больше информации об информационной безопасности, реверсу малвари и расследованию инцидентов в канале https://t.me/threathunt_pedia.
Используемые утилиты:
- Blobrunner - инструмент для отладки шелл-кода.
- SpeakEasy - эмулятор, предназначенный для эмуляции вредоносных программ ядра и пользовательского режима Windows.
- Ghidra - это платформа обратного проектирования программного обеспечения (SRE)/
- IDA Pro - инструмент для анализа двоичного кода.
- x64Debug - бинарный отладчик с открытым исходным кодом для Windows
- Die.
Исследование шелл-кода
Для статического анализа шелл-кода будем использовать Ghidra. Испортируем файл и выберем компилятор.
Выбираем x86 архитектуру, 64 разрядный процесс и компилятор gcc. Если код не преобразовался, то нажмем горячую клавишу D
или Правая клавиша мыши->Disassemble
.
Командами PUSH
в стек складывается большой объем данных. Спустимся по коду ниже и разберем дальнейшие действия шелл-кода.
Начнем проводить динамический анализ шелл-кода, чтобы подробнее разобрать реализию, запустим blobrunner.
blobrunner64.exe 4f88738e04447344100bb9532c239032b86e71d8037ccb121e2959f37fff53cf.unknown
Помним, что мы отлаживаем x64 разрядный шелл-код, поэтому нам необходимо в x64dbg поставить точку останова на потоке с идентификатором, указаном в строке Created Thread:
утилиты Blobrunner. Присоединяемся к созданному процессу, ставим точку останова и начинаем отлаживать код.
аходим в функцию 13a69d944d7
(Горячая клавиша F7 в x64dbg).
Спускаемся ниже и видим вызов функции call 13a69d93e67
, на вход ей поступает значение 0x3e192526
. Зайдем в данную функцию.
В данной функции реализована следующая логика. Получение адреса динамической библиотеки ntdll.dll, далее адреса всех функций экспорта, следующим этапом имя экспортируемой функции подается на вход call 13a69d94557
для расчета хэш-значения.
Полученное хэш-значение сравнивается со значением 0x3e192526
. Разберем алгоритм хэширования и напишем его алгоритм на Python3. Для этого зайдем в функцию 13a69d94557
Код реализации хэширования представлен ниже.
Имя экспортированной функции из динамической библиотеки по байтово складывается с общей суммой, которая преобразуется по алгоритму ror13.
def hash_api_brc4(name_api): sum = 0 for i in name_api: c = ord(i) sum = ((ror(sum,0xd,max_bits) & 0xffffffffffffffff) + c) &0xffffffffffffffff return hex(sum)
В Ghidra найдем все хэш-значения и преобразуем их. Горячей клавишей ;
можно добавить комментарий к строке в дизассемблере.
kernel32.dll->GetProcAddress 0x7c0dfcaa kernel32.dll->LoadLibraryA 0xec0e4e8e kernel32.dll->WaitForSingleObject 0xce05d9ad ntdll.dll->LdrGetDllHandleEx 0xec6b915d ntdll.dll->LdrGetProcedureAddress 0xe54cc407 ntdll.dll->NtAllocateVirtualMemory 0xd33bcabd ntdll.dll->NtFlushInstructionCache 0x534c0ab8 ntdll.dll->NtProtectVirtualMemory 0x8c394d89 ntdll.dll->RtlAllocateHeap 0x3e192526 ntdll.dll->RtlFreeHeap 0xda12b8 ntdll.dll->NtAllocateVirtualMemory 0xd33bcabd ntdll.dll->RtlAllocateHeap 0x3e192526 ntdll.dll->RtlFreeHeap 0xda12b8
Значение хэш-суммы 0x3e192526 соответствует функции RtlAllocateHeap.
В функции 13a69d944d7
реализован алгоритм выделения кучи с помощью функции RtlAllocateHeap и копирование полезной нагрузки размером 0x39410 из стека в выделенную оласть памяти.
Во втором выполнении функции13a69d944d7
копируется закодированная Base64 конфигурация модуля размером 0x13c в выделенную область кучи.
Далее проанализируем функцию `call 13A69D94587`, в которой реализован основной функционал по расшифрованию и запуску основной нагрузки.
В данной функции происходит получение адресов функций: RtlFreeHeap, NtProtectVirtualMemory, LdrGetHandleEx, LdrGetProcedureAddress.
Спустимся ниже и увидим еще одно константное значение 0x6a4abc5b, которое поступает на вход функции `...f97`. Расмотрим его подробнее.
Код функции хэширования имени DLL библиотеки.
В данной функции реализован алгоритм хэширования имени DLL библиотеки. Напишем его на Python3.
def dll_hashing(name_dll): sum = 0 for i in name_dll: c = ord(i) if c >= 0x61: c -= 0x20 sum = ((ror(sum,0xd,max_bits) & 0xffffffffffffffff) + c) &0xffffffffffffffff sum = ((ror(sum,0xd,max_bits) & 0xffffffffffffffff)) &0xffffffffffffffff return sum
Функция 0x6a4abc5b является хэш значением динамической библиотеки KERNEL32.DLL.
Далее с помощью функции LdrGetDllHandleEx загружает динамическую функцию Kernel32.dll.
Следующим этапом начинается процесс расшифрования полезной нагрузки Badger BRC3 в функции call 13a69d948a7
.
Увидев код ниже, который представляет собой алгоритм расширения ключа, можно сделать вывод, что нагрузка зашифрована RC4.
В качестве ключа передаются последние 8 байтов скопированной полезной нагрузки. Ключ равен 23 64 66 26 2F 61 2E 65
. Расшифруем полезную нагрузку.
В итоге получили PE файл с удаленной MZ сигнатурой.
Далее полученный код загружается в процесс методом ReflectiveDLLInjection.
Код расшифрования файла конфигурации расположен в основной DLL Badger. Но не много опишу его тут.
Конфигурация расположена в Base64 данных, зашифрованных по алгоритму RC4. Ключом является предпоследние 8 байтов расшифрованной полезной нагрузки DLL Badger BRC4. Последние 8 байт это зашифрованный ключ расшифрования полезной нагрузки, тот что мы получили на предыдущем шаге.
Расшифруем с помощью утилиты CyberChef.
В конфигурации каждый параметр отделен вертикальной чертой. В выявленном нам конфигурации указан управляющий сервер deve.dread.ie
. Так конфигурация хранится В памяти процесса конфигурация хранится в зашифрованном blob-объекте (RC4+base64), но ключ шифрования для RC4 указан в основной нагрузки DLL Badger BRC4.
На данном этапе мы с вами разобрали загрузчик основной нагрузки DLL Badger BRC4. Нам удалось разобрать алгоритм хэширования API функций, который схож с реализацией в CobaltStrike. Расшифровали полезную нагрузку, которая представляет из себя DLL без заголовка MZ, а также расшифровали конфигурацию вредоноса.