Go Back   Carder.life > [ru] Forum for Russians > Хакинг. Программирование



Reply
 
Thread Tools Display Modes
  #1  
Old 03-15-2025, 03:38 AM

maker maker is offline
Junior Member
Join Date: Nov 2024
Posts: 10
Default



Попался мне недавно вредоносный doc файл, который рассылали с фишинговыми письмами. Я решил, что это неплохой повод поупражняться в реверс-инжиниринге и написать что-то новое на Хабр. Под катом — пример разбора вредоносного макроса-дроппера и не менее вредоносных dll.
Макрос
Sha256 от файла — https://www.virustotal.com/gui/file/...f1b519/details. В самом doc файле на первой странице находится картинка, сообщающая что этот файл защищён и объясняющая как включить макросы, ещё в файле есть две большие таблицы с числами. Числа записаны в десятичной форме, самые длинные — десятизначные, есть и положительные, и отрицательные.
Когда жертва разрешает исполнение макросов (есть те, у кого оно по умолчанию разрешено?), запускается цепочка действий, которая в конце концов выполняет функцию updatedb_network, которая изменяет текущую директорию на временную и создаёт в ней файл «icutils.dll», в который подряд записывает числа из первой таблицы как 32 битные integer со знаком. В резльтате получается корректная dll. Из этой dll импортируется функция clone:
Code:
Declare PtrSafe Function clone Lib "icutils.dll" _
(ByVal Saved As String, ByVal Time As Integer) As Boolean

И запускается с двумя параметрами:
Code:
R1 = Module1.clone("Cream", 0)

Если вызов clone возвращает False, то файл «icutils.dll» перезаписывается данными из второй таблицы и снова вызывается clone с такими же параметрами.
Забегая вперёд скажу, что первая dll 64 битная и не будет выполняться на 32 битных системах. Таким образом макрос подбирает правильную архитектуру бинарного кода.
Что интересно, в функции updatedb_network есть такой кусок кода, который никакого функционального назначения не имеет:
Code:
Sub updatedb_network()
...
Dim query_to_change As Variant
Set query_to_change = CurrentDb.QueryDefs("query_name")
query_to_change.SQL = "SELECT * FROM Table ORDER BY ID Asc"
query_to_change.SQL = "SELECT Field1, Field2 FROM Table ORDER BY ID Asc"
query_to_change.SQL = "SELECT Field1, Field2 FROM Table WHERE Field LIKE Fashion"
query_to_change.SQL = "SELECT Field1, Field2 FROM Table WHERE Field LIKE '" & something & "'"
...
End Sub

Возможно он тут для придания видимости полезной работы для тех, кто быстро пролистает код, увидит какие-то строки на SQL и подумает, что всё ОК? Не знаю. Так же большинство функций и переменных имеют случайные или не относящиеся к реальному назначению имена (как, например, updatedb_network, которая ни с БД, ни с сетью не взаимодействует). Хотя есть, например функция dump_payload, которая сохраняет 4 байта в icutil.dll. Но в любом случае, сразу должно насторожить наличие функции Document_Open, её произвольно переименовать авторы ВПО не могут (правда вместо неё могут использовать другую автоматически запускаемую функцию).
Итак, функционал макроса более-менее понятен, пора выгружать dll и переходить к их анализу.
Первая dll
Первая dll (sha256 https://www.virustotal.com/gui/file/...f114/detection) 64 битная.
В списке импортируемых функций есть функции CreateProcessW (запуск программы), CreateRemoteThread (создание потока в другом процессе), VirtualAllocEx (выделение блока памяти в другом процессе), WriteProcessMemory (запись в память другого процесса), что сразу наводит на мысли об инъекции кода в другой процесс. Теперь посмотрим что именно она делает с помощью IDA Free и Ghidra.
Основная точка входа просто возвращает 1, ничего больше не делает. Вторая экспортируемая функция — clone, именно она вызывается макросом и содержит вредоносный код.
Параметры, с которыми она вызывается, вроде ни на что не влияют. Приложение расшифровывает два блока данных. Первый блок данных длиной 0x78 со следующим содержимым (уже расшифрованный):
https://pastebin.com/raw/Jyujxy7z\x00\x00\x00\x00\x00\x00\x00\xf2i\xe0\x1d\ x95h\xbc\x03\xe4#\xe0\x1d<\x04\xe0\x1d\xe6\x00\xde \x01\xa4\x17\xbc\x03x\x01\xe0\x1d\xe2\x16x\x07Qy\x bc\x03@Fx\x07Df\xbc\x03\x89a\xde\x01q\x11\xe0\x1d| Ix\x07D@\xbc\x03\x8a\x01\xde\x01^9\xde\x01\xf2i\xe 0\x1d\x95h\xbc\x03\xe4#\xe0\x1d\xab
Второй блок данных имеет длину 0x1D4C и содержит исполняемый код.
Кроме того в структуру длиной 0x90 байт записывается указатель на модуль kernel32, результат выполнения функции GetTickCount() и адрес функции ZwDelayExecution() из kernel32.
После чего создаётся процесс (CreateProcessW) “cmd.exe”. С помощью VirtualAllocEx в нём выделяются два буфера: с разрешениями RW длиной 0x108 и с разрешениями RWX длиной 0x1D4C. В RW буфер копируется приведённый выше блок с данными и вышеупомянутая структура длиной 0x90. В структуру так же записывается указатель на расшифрованный блок данных (в адресном пространстве дочернего процесса (cmd.exe)). В RWX буфер копируется (WriteProcessMemory) расшифрованный блок данных с кодом.
Потом в процессе cmd.exe создаётся поток (CreateRemoteThread) с точкой входа в начале RWX буфера, в качестве аргумента передаётся указатель на RW буфер. На этом функция clone завершается, действие продолжается в процессе cmd.exe.
Интересно, что в функции clone есть вроде как недостижимый кусок кода, который импортирует (LoadLibraryW) библиотеку "WorkPolyhistor".
Инжектированный в cmd.exe код
Он выполняет следующие действия:
находит адреса нужных функций из kernel32.dll (её адрес получается от родительского процесса)
загружает библиотеки ntdll, Ole32, User32
находит адреса нужных функций в этих библиотеках.
Интересно, что для бОльшей части функций в коде нет имени функции, а только CRC32 от имени (перебираются все имена функций из загруженной библиотеки, пока не найдётся функция с нужным CRC32 от имени). Возможно это защита от получения списка импортируемых функций утилитой strings, правда странно, что код, который хранится в зашифрованном виде имеет такую защиту, в то время как сама dll импортирует функции просто по именам. Всего обнаруживаются следующие функции:
kernel32:
GetProcAddress
LoadLibrary
GlobalAlloc
GetTempPath
GetFileAttributesW
CreateProcessW
GlobalFree
GlobalRealloc
WriteFile
CreateFileW (находится по имени)
WriteFile
CloseHandle
GetTickCount
ReadFile
GetFileSize
ntdll:
RtlCreateUnicodeStringFromAsciiz
ZwDelayExecution
ZwTerminateProcess
swprintf
ole32:
CoInitialize (находится по имени)
CoCreateInstance (находится по имени)
msvcrt:
rand
srand
Далее процесс с помощью GetTempPath получает путь к временной директории, создаёт в ней файл с названием вида 26342235.dat, где имя файла — десятичная запись TickCount, полученного от родительского процесса и итерируется на каждой новой попытке (т.е. если не удалось скачать пэйлоад с первой попытки, то на вторую попытку будет создан файл с именем 26342236.dat). После этого загружается библиотека wininet и в ней находятся указатели на следующие функции:
InternetCloseHandle (crc32: 0xe5191d24)
InternetGetConnectedState (crc32: 0xf2e5fc0c)
InternetOpenA (crc32: 0xda16a83d)
InternetOpenUrlA (crc32: 0x16505e0)
InternetReadFile (crc32: 0x6cc098f5)
С помощью InternetGetConnectedState проверяется есть ли сеть, если нет — приложение вызывает функцию по несуществующему адресу и падает (такая защита от определения адреса откуда получается пэйлоад с помощью изолированной от сети машины. Это единственный случай, когда приложение завершается нештатно, в остальных — делаются 3 попытки, после чего cmd.exe завершается с помощью ZwTerminateProcess). Если сеть есть, то с помощью найденный функций пэйлоад скачивается с переданного из родительского процесса URL (https://pastebin.com/raw/Jyujxy7z) и сохраняется в созданный ранее файл с расширением .dat.
Далее пэйлоад считывается из .dat файла, декодируется (base64), расшифровывается с помощью XOR с CRC32 от URL, проверятеся, что первые 2 байта расшифрованных данных — 'MZ', если да — результат сохраняется в файл с таким же именем, но расширением .exe
Код для расшифрования на Python
Code:
from binascii import crc32
from base64 import b64decode
def decrypt_payload(payload_b64: bytes, url: str):
payload_bin = b64decode(payload_b64.decode())
key = str(crc32(url.encode())).encode()
decrypted = bytearray()
for i, b in enumerate(payload_bin):
decrypted.append(b ^ key[i % len(key)])
return bytes(decrypted)

С помощью функции CreateProcessW сохранённый файл запускается. Если всё удачно — процесс cmd.exe завершается с помощью ZwTerminateProcess. Если что-то пошло не так (кроме отсутствия сети), то всё повторяется заново, максимум делаются 3 попытки, имена dat и exe файлов каждый раз увеличиваются на 1.
Вторая dll
Вторая dll (sha256 https://www.virustotal.com/gui/file/...dfca/detection) 32 битная.
В ней основной вредоносный функционал реализован в функции clone. Она тоже расшифровывает 2 буфера. Первый буфер имеет размер 0x78 (120) байт, в него расшифровываются и записываются такие данные (в расшифрованном виде):
https://pastebin.com/raw/Jyujxy7z\x00\x00\x00\x00\x00\x00\x00\x1e(\xf0\x0e\ xc5r\xc0;\x12)\xc0;Jr\xc0;Y4\xbc\x03/Mx\x07\x038\xde\x01\x9e\x05\xe0\x1d#\x08\xbc\x03\x eeU\xf0\x0e\x18{x\x078\x1a\xf0\x0e\xccg\xf0\x0eze\ xde\x01\x89&\xe0\x1d\xf6\x1f\xe0\x1d
Видно, что в начале находится такой же URL, как и в x64 версии.
Второй буфер размером 0x4678 байт выделяется с RWX разрешениями. В него расшифровывается код, после чего из него вызывается функция со смещением 0x4639 от начала буфера.
Подробно разбирать этот код я не стал. Он так же находит функции по CRC32, запускает notepad.exe, инжектирует туда код, который скачивает пэйлоад с того же URL на pastebin.
Пэйлоад с pastebin
Расшифрованный пэйлоад с pastebin — это 32битный exe файл (sha256 https://www.virustotal.com/gui/file/...155f/detection) Подробно разбирать его я пока не стал в силу нехватки времени.
Заключение
В целом вредонос на меня произвёл впечатление довольно плохо написанного, с большим количеством ошибок (не называю их здесь намеренно). Как будто писали на скорую руку.
dll находит в памяти функции, которые потом нигде не используются. Наверно этот код, так же как макрос, регулярно переписывается злоумышленниками, каждый раз когда он начинает детектироваться антивирусами, в результате чего в коде остаются такие артефакты.
Так же интересно, что «роняемой» на диск x64 версии dll имена «опасных» импортируемых функций ничем не замаскированы (можно хоть strings их увидеть), а в коде, который расшифровывается в памяти и на диск не ложится, они находятся по CRC32 от имени, а не просто по именам.
Пэйлоад с pastebin через несколько дней был удалён.
P.S. КДПВ взята отсюда https://twitter.com/BroadAnalysis/st...54224475631616
Reply

Tags
NULL


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is On

Forum Jump




All times are GMT. The time now is 02:59 PM.