Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
CobInt — это бэкдор, который активно использует группировка Cobalt/(Ex)Cobalt при атаках на российские компании. В статье мы по шагам выполним реверс CobInt и изучим полезные техники анализа этой малвари. www
Подробности про группировку, инструменты и TTP ищи по ссылкам:
В начале 2024 года наша команда по расследованию инцидентов (PT ESC Positive Technoligies) выявила применение малвари CobInt в инфраструктуре заказчика. Проанализировав логи EVTX на скомпрометированной машине, мы извлекли сильно обфусцированный вредоносный скрипт PowerShell.
Обфусцированный код PowerShell
В конце кода скрипта мы видим вызов единственной функции, которая нас больше всего и интересует.
Вызов функции
Код функции выглядит следующим образом:
If ([IntPtr]::size -eq 8){ $L7Q=aQGIf $d90qSK=1331}else{ $L7Q=tNKdTPG $d90qSK=1166}$YodSQB=[System.Convert]::FromBase64String((vXQvbyjhW $L7Q))RgEGGBTGL $YodSQB $d90qSK}
В этой функции на основе размера указателя определяется разрядность ОС, в зависимости от которой переменной $d90qSK
присваивается соответствующее значение. Оно определяет смещение к функции расшифровки шелл‑кода. Шелл‑код для определенной архитектуры декодируется из Base64, после чего передается в функцию RgEGGBTGL
вместе со смещением. Чтобы сохранить раскодированный шелл‑код на диск, сразу после вызова функции FromBase64String
добавим в код следующую строку:
[IO.File]::WriteAllBytes('shellcode.bin',$YodSQB)
Теперь убедимся, что шелл‑код сохранился корректно. Закидываем его в IDA, переходим по смещению 1331 (0x533)
в случае с x64 и преобразуем байты в код.
Код расшифровки по смещению 0x533
Код, который отвечает за расшифровку самого себя в памяти, выглядит следующим образом.
Код для дешифровки тела шелл‑кода
Нам нужно преобразовать шелл‑код в исполняемый файл. Я это делаю с помощью shellcode2exe:
shellcode2exe.bat 64 shellcode.bin cob_shellx64.exe
Теперь мы можем загрузить получившийся исполняемый файл в x64dbg. После загрузки жмем RUN, чтобы перейти к EntryPoint.
Интерпретация зашифрованных байтов шелл‑кода дебаггером
Код на текущий момент еще не расшифрован и представляет собой вольную интерпретацию мешанины из байтов, выданной дебаггером. Нужно перейти по известному смещению 0x533
+ ImageBase, то есть в нашем случае по адресу 0x401533
.
Точка входа для расшифровки кода
Мы видим уже знакомую функцию для расшифровки тела шелл‑кода. Выполнять код мы начнем с текущей строки. Для этого жмем Set RIP Here, после чего адрес Instruction Pointer
примет необходимое значение.
С помощью F8 нужно пройтись по инструкциям расшифровки и найти ту, которая отвечает за выход из цикла. Эта инструкция находится по адресу 0x401538
.
Инструкция для выхода из цикла расшифровки
Ставим брейк‑пойнт на следующей за jne
инструкции и нажимаем F9, чтобы вручную не ходить по каждой итерации цикла расшифровки. После остановки на нашем брейк‑пойнте проходим по коду немного дальше и прыгаем на начало уже расшифрованного кода в оригинальной EntryPoint.
Расшифрованный код в EntryPoint
На этом этапе мы можем сдампить PE в расшифрованном виде для удобства дальнейшего дебага. Я использую в этих целях плагин OllyDumpEx.
Дамп процесса
Код для расшифровки себя в памяти содержит следующий алгоритм:
0x4498D9DE
. rol xor_key, 1
). www
Интерпретация алгоритма расшифровки на Go с вшитым ключом
Настало время поковыряться в загрузчике. Открываем сдампленный файл в x64dbg. В коде встречается множество вызовов функции 401380
, в которую передаются различные Hex-значения. Обычно такой паттерн свойственен функциям, отвечающим за восстановление имен библиотек и функций из хеш‑значений. Как видно на скриншоте, первый вызов вернул имя функции LoadLibraryA
библиотеки Kernel32
, а также ее адрес в RCX.
Вызов unhashing-функции
Рассмотрим эту функцию подробнее. Обрати внимание на следующий код.
Инструкции для получения информации из PEB
Этот модуль сначала получает указатель на PEB (Process Environment Block) через смещение 0x60
в TEB (Thread Environment Block), доступ к которому осуществляется через регистр gs
. Затем код переходит по смещениям 0x18
и 0x20
. Чтобы понять, что это за смещения, можно обратиться к описанию структуры PEB или проверить их в WinDbg. Для этого:
bp $exentry
для установки брейк‑пойнта на EntryPoint; g
; Команда dt ntdll!_PEB @$peb
покажет структуру PEB со смещениями.
Структура PEB в WinDbg
Первое смещение указывает на структуру PEB_LDR_DATA
, которая содержит в себе сведения о загруженных модулях для процесса. Изучим ее повнимательнее, для этого в WinDbg введем команду dt -r1 _PEB_LDR_DATA
.
Структура PEB_LDR_DATA в WinDbg
Второе смещение указывает на двусвязный список InMemoryOrderModuleList
, который содержит загруженные модули для процесса. Каждый элемент в этом списке является указателем на структуру LDR_DATA_TABLE_ENTRY
. Смещение на него лежит в rbx
, поэтому берем адрес из rbx
и накладываем на него структуру _LDR_DATA_TABLE_ENTRY
.
Содержание структуры LDR_DATA_TABLE_ENTRY, на которую получен указатель
Таким образом, по смещению 0x50
код получает доступ к полю buffer
с именем модуля. Далее вызывается функция, в которую аргументами передается указатель на строку с именем модуля и длина строки.
Передача имени модуля и длины строки в функцию
Эта функция выполняет хеширование строки. Алгоритм выглядит следующим образом.
Алгоритм хеширования строки
Получив хеш, алгоритм проверяет полученное значение с переданным ранее аргументом. Если значения не совпадают, выполняется переход к следующему имени модуля. Если совпадают, то сохраняется указатель на адрес библиотеки, к которому прибавляются смещения.
Доступ к данным библиотеки по смещениям
Источник: xakep.ru