Содержание статьи
www
Подробности про группировку, инструменты и TTP ищи по ссылкам:
PowerShell loader
В начале 2024 года наша команда по расследованию инцидентов (PT ESC Positive Technoligies) выявила применение малвари CobInt в инфраструктуре заказчика. Проанализировав логи EVTX на скомпрометированной машине, мы извлекли сильно обфусцированный вредоносный скрипт 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 (
в случае с x64 и преобразуем байты в код.
Расшифровка тела шелл-кода
Код, который отвечает за расшифровку самого себя в памяти, выглядит следующим образом.
Нам нужно преобразовать шелл‑код в исполняемый файл. Я это делаю с помощью shellcode2exe:
shellcode2exe.bat 64 shellcode.bin cob_shellx64.exe
Теперь мы можем загрузить получившийся исполняемый файл в x64dbg. После загрузки жмем RUN, чтобы перейти к EntryPoint.
Код на текущий момент еще не расшифрован и представляет собой вольную интерпретацию мешанины из байтов, выданной дебаггером. Нужно перейти по известному смещению 0x533
+ ImageBase, то есть в нашем случае по адресу 0x401533
.
Мы видим уже знакомую функцию для расшифровки тела шелл‑кода. Выполнять код мы начнем с текущей строки. Для этого жмем Set RIP Here, после чего адрес Instruction
примет необходимое значение.
С помощью F8 нужно пройтись по инструкциям расшифровки и найти ту, которая отвечает за выход из цикла. Эта инструкция находится по адресу 0x401538
.
Ставим брейк‑пойнт на следующей за jne
инструкции и нажимаем F9, чтобы вручную не ходить по каждой итерации цикла расшифровки. После остановки на нашем брейк‑пойнте проходим по коду немного дальше и прыгаем на начало уже расшифрованного кода в оригинальной EntryPoint.
На этом этапе мы можем сдампить PE в расшифрованном виде для удобства дальнейшего дебага. Я использую в этих целях плагин OllyDumpEx.
Код для расшифровки себя в памяти содержит следующий алгоритм:
- XOR первых четырех байтов (Little Endian) с зашитым ключом
0x4498D9DE
. - Побитовый сдвиг влево на один бит для ключа (
rol
).xor_key, 1 - XOR следующих четырех байтов с модифицированным ключом и так далее.
Исследование загрузчика
Unhashing functions
Настало время поковыряться в загрузчике. Открываем сдампленный файл в x64dbg. В коде встречается множество вызовов функции 401380
, в которую передаются различные Hex-значения. Обычно такой паттерн свойственен функциям, отвечающим за восстановление имен библиотек и функций из хеш‑значений. Как видно на скриншоте, первый вызов вернул имя функции LoadLibraryA
библиотеки Kernel32
, а также ее адрес в RCX.
Рассмотрим эту функцию подробнее. Обрати внимание на следующий код.
Этот модуль сначала получает указатель на PEB (Process Environment Block) через смещение 0x60
в TEB (Thread Environment Block), доступ к которому осуществляется через регистр gs
. Затем код переходит по смещениям 0x18
и 0x20
. Чтобы понять, что это за смещения, можно обратиться к описанию структуры PEB или проверить их в WinDbg. Для этого:
- загружаем исполняемый файл в WinDbg;
- вводим команду
bp
для установки брейк‑пойнта на EntryPoint;$exentry - запускаем исполнение командой
g
; - перемещаемся нажатием F8 к рассматриваемому участку кода.
Команда dt
покажет структуру PEB со смещениями.
Первое смещение указывает на структуру PEB_LDR_DATA
, которая содержит в себе сведения о загруженных модулях для процесса. Изучим ее повнимательнее, для этого в WinDbg введем команду dt
.
Второе смещение указывает на двусвязный список InMemoryOrderModuleList
, который содержит загруженные модули для процесса. Каждый элемент в этом списке является указателем на структуру LDR_DATA_TABLE_ENTRY
. Смещение на него лежит в rbx
, поэтому берем адрес из rbx
и накладываем на него структуру _LDR_DATA_TABLE_ENTRY
.
Таким образом, по смещению 0x50
код получает доступ к полю buffer
с именем модуля. Далее вызывается функция, в которую аргументами передается указатель на строку с именем модуля и длина строки.
Эта функция выполняет хеширование строки. Алгоритм выглядит следующим образом.
Получив хеш, алгоритм проверяет полученное значение с переданным ранее аргументом. Если значения не совпадают, выполняется переход к следующему имени модуля. Если совпадают, то сохраняется указатель на адрес библиотеки, к которому прибавляются смещения.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»