![]() |
https://habrastorage.org/webt/ee/hd/...me1nvupy4.jpeg Попался мне недавно вредоносный doc файл, который рассылали с фишинговыми письмами. Я решил, что это неплохой повод поупражняться в реверс-инжиниринге и написать что-то новое на Хабр. Под катом — пример разбора вредоносного макроса-дроппера и не менее вредоносных dll. Макрос Sha256 от файла — https://www.virustotal.com/gui/file/...f1b519/details. В самом doc файле на первой странице находится картинка, сообщающая что этот файл защищён и объясняющая как включить макросы, ещё в файле есть две большие таблицы с числами. Числа записаны в десятичной форме, самые длинные — десятизначные, есть и положительные, и отрицательные. Когда жертва разрешает исполнение макросов (есть те, у кого оно по умолчанию разрешено?), запускается цепочка действий, которая в конце концов выполняет функцию updatedb_network, которая изменяет текущую директорию на временную и создаёт в ней файл «icutils.dll», в который подряд записывает числа из первой таблицы как 32 битные integer со знаком. В резльтате получается корректная dll. Из этой dll импортируется функция clone: Code: <pre class="alt2" dir="ltr" style=" margin: 0px; padding: 6px; border: 1px solid rgb(0, 0, 0); width: 640px; height: 50px; text-align: left; overflow: auto; background: rgb(37, 37, 37) none repeat scroll 0% 0%; border-radius: 5px; font-size: 11px; text-shadow: none;">Declare PtrSafe Function clone Lib "icutils.dll" _ (ByVal Saved As String, ByVal Time As Integer) As Boolean</pre> И запускается с двумя параметрами: Code: <pre class="alt2" dir="ltr" style=" margin: 0px; padding: 6px; border: 1px solid rgb(0, 0, 0); width: 640px; height: 34px; text-align: left; overflow: auto; background: rgb(37, 37, 37) none repeat scroll 0% 0%; border-radius: 5px; font-size: 11px; text-shadow: none;">R1 = Module1.clone("Cream", 0)</pre> Если вызов clone возвращает False, то файл «icutils.dll» перезаписывается данными из второй таблицы и снова вызывается clone с такими же параметрами. Забегая вперёд скажу, что первая dll 64 битная и не будет выполняться на 32 битных системах. Таким образом макрос подбирает правильную архитектуру бинарного кода. Что интересно, в функции updatedb_network есть такой кусок кода, который никакого функционального назначения не имеет: Code: <pre class="alt2" dir="ltr" style=" margin: 0px; padding: 6px; border: 1px solid rgb(0, 0, 0); width: 640px; height: 194px; text-align: left; overflow: auto; background: rgb(37, 37, 37) none repeat scroll 0% 0%; border-radius: 5px; font-size: 11px; text-shadow: none;">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</pre> Возможно он тут для придания видимости полезной работы для тех, кто быстро пролистает код, увидит какие-то строки на 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: <pre class="alt2" dir="ltr" style=" margin: 0px; padding: 6px; border: 1px solid rgb(0, 0, 0); width: 640px; height: 194px; text-align: left; overflow: auto; background: rgb(37, 37, 37) none repeat scroll 0% 0%; border-radius: 5px; font-size: 11px; text-shadow: none;">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)</pre> С помощью функции 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 </img> |
All times are GMT. The time now is 05:50 AM. |
Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2025, vBulletin Solutions, Inc.