Кадр из м/ф "Монстры на каникулах"
Кадр из м/ф "Монстры на каникулах"

Во время очередного расследования наша команда Solar JSOC CERT наткнулась на неизвестный RAT. Но после проведения глубокого анализа стало понятно, что перед нами новая версия уже известного RAT (3.3a), который использует группировка APT31. Его первую версию эксперты по кибербезопасности описывали еще в 2021 году. За это время ВПО претерпело множество изменений и обзавелось новым функционалом. Его и разберем в статье. Дополнительно опишем особенности библиотеки wolfSSL, используемой злоумышленниками для реализации протокола TLS при взаимодействии с C2, а также возможные шаги для определения ее версии.

Для закрепления в системе и запуска RAT злоумышленники использовали службы. Схема запуска RAT, как и в прошлой версии, использует DLL Hijacking и приведена на рисунке:

 Опишем подробнее каждый этап.

Этап 1. Начальный запуск

Рассматриваемый RAT устанавливался на хосты уже после того, как злоумышленники получили доступ к учетным записями с административными правами. Так что для наделения бэкдора максимальными правами и закрепления в системе создавались службы, которые запускали легитимный переименованный файл MicrosoftEdgeUpdate.exe с правами LocalSystem.

В одной директории рядом с данным файлом всегда также располагались:

  • вредоносная DLL (загрузчик) msedgeupdate.dll;

  • зашифрованный RAT без конфигурации.

По результатам расследования мы собрали следующие варианты расположения и названий MicrosoftEdgeUpdate.exe:

C:\ProgramData\Microsoft\DRM\DRMs.exe

C:\ProgramData\Microsoft\DeviceSync\Data\DeviceSyncs.exe

C:\ProgramData\Microsoft\DeviceSync\DeviceSyncs.exe

C:\ProgramData\Microsoft\Crypto\Cryptos.exe

MicrosoftEdgeUpdate.exe представляет собой легитимный файл, имеющий действительную цифровую подпись Microsoft, который используется при установке и обновлении браузера Microsoft Edge. На рисунке представлены свойства файла MicrosoftEdgeUpdate.exe, переименованного в DeviceSyncs.exe:

После запуска легитимный переименованный MicrosoftEdgeUpdate.exe через LoadLibraryEx загружает вредоносную msedgeupdate.dll, которая располагается в том же каталоге.

Этап 2. Загрузчик

Вредоносная DLL msedgeupdate.dll – это неподписанная 32-разрядная библиотека, накрытая VMProtect 3.0-3.5. Дата компиляции, скорее всего, изменена вручную: 22.04.18 из FileHeader и 30.06.2021 из экспортной таблицы. Злоумышленники не старались подражать оригинальной библиотеке.

Экспортные функции

Легитимная DLL
Легитимная DLL
Вредоносная DLL
Вредоносная DLL

В качестве «пасхалок» злоумышленники использовали названия функций с номерами #1 и #16:

 #1 안녕하십니까 – «Всем привет» (в переводе с корейского).

#16 안녕 – «Здравствуйте» (в переводе с корейского).

Все экспортные функции не представляют интереса, так как выполняют одну процедуру, в которой выполняется четыре API-вызова:

Вредоносный код загрузчика располагается в DllEntryPoint, которая вызывается при выполнении LoadLibraryEx.

Из арсенала VMProtect использовался только модуль обфускации API-вызовов, так как после инициализации виртуальной машины выполнение переходит к DllEntryPoint в секции .text, в которой отсутствует виртуализация, но все API-вызовы обфусцированы.

Приведем высокоуровневое описание функционала загрузчика и дадим комментарии по:

1. Расшифровке RAT и записи конфига;

2. Инжекту RAT в C:\Windows\System32\dllhost.exe и его запуску.

Конфиг

Конфиг хранится в незашифрованном виде в загрузчике (во вредоносной DLL) в секции .data по RVA 0x770. Также во всех обнаруженных нами образцах совпадал raw address конфига – 0x11170:

Название параметра

Описание

int sleep_min

минимальное время сна в секундах перед очередной попыткой связи с C2

int SND_RCV_timeout_secs

timeout в секундах для отправки и получения данных с C2

int sleep_rand_range

размер диапазона генерации случайных значений в секундах. Генерируются значения в диапазоне [sleep_min, sleep_min+sleep_rand_range]

int new_c2_url_offset

int c2_url_offset

смещения от начала конфига

int c2_port

порт для подключения к C2

int c2_op_mode

режим работы RAT
Подробности в разделе «Функционал RAT».

int keylogger_output_filename_offset

int enc_payload_name_offset

int payload_dirpath_offset

int exe_filepath_offset

int loader_dll_filepath_offset

смещения от начала конфига

char new_c2_config_url[]

URL для загрузки новой конфигурации c2.
Подробности в разделе с описанием RAT

char c2_url[]

URL сервера управления

wchar_t keylogger_output_filename[]

имя файла для сохранения результатов работы keylogger

wchar_t enc_payload_name[]

имя зашифрованного файла нагрузки (также используется в качестве имени mutex для уникальности процесса RAT)

wchar_t payload_dirpath[]

каталог расположения зашифрованной нагрузки – всегда совпадает с каталогом exe-файла

wchar_t exe_filepath[]

полный путь до exe-файла, который загрузил вредоносную DLL

wchar_t loader_dll_filepath[]

полный путь до вредоносной DLL

Мы обнаружили две конфигурации с одним и тем же сервером управления, но с разными значениями других параметров:

Для удобства приводим сравнительную таблицу двух конфигураций:

Название параметра

Конфиг 1

Конфиг 2

sleep_min

0x1F4 (500)

0xB4 (180)

SND_RCV_timeout_secs

0x1F4 (500)

0x12C (300)

sleep_rand_range

0x64 (100)

0x3C (60)

new_c2_url_offset

0

c2_url_offset

0x30

c2_port

0x1BB (443)

c2_op_mode

0

keylogger_log_name_offset

0x41

enc_payload_name_offset

0x55

0x47

payload_dirpath_offset

заполняется DLL-загрузчиком

exe_filepath_offset

loader_dll_filepath_offset

new_c2_config_url

отсутствует

c2_url

api.inliines[.]com
(213.183.53[.]156)

keylogger_log_name

1Cdatakey

1c

enc_payload_name

1Cdatas

1cdata

payload_dirpath

заполняется DLL-загрузчиком

exe_filepath

loader_dll_filepath

Расшифровка RAT и запись конфига

Для расшифровки нагрузки используется xor с 5-байтовым ключом.

Первая часть xor-ключа хранится в виде последовательности четырёх dword:

Далее в конец к ней дописывается байт 0x0F (вторая часть xor-ключа). В цикле при выполнении операции xor используется наименее значимый байт dword. Таким образом, ключ можно представить в виде последовательности байт: 13184F290F. В первой версии RAT использовалась аналогичная схема хранения и дополнения xor-ключа.

Дополненный конфиг записывается в расшифрованную нагрузку поверх egg-строки "w4wg7x" (специальная строка-индикатор) в секции .data:

В кодировке UTF16-LE строка-индикатор выглядит так «㑷杷砷», но не переводится во что-то, имеющее смысл.

Как видно, сразу после расшифровки конфигурация RAT пустая. Далее с помощью WriteProcessMemory выполняется инжект RAT в C:\Windows\System32\dllhost.exe. Через SetThreadContext корректируется EntryPoint SUSPENDED-процесса и выполняется его запуск.

Этап 3. RAT

Ниже приведем описание структуры и функционала RAT.

Сравнение версий

Специалисты Solar JSOC CERT обнаружили две версии полезной нагрузки. Оба файла представляют собой 32-разрядные исполняемые файлы со следующими датами компиляции из Debug-секции.

21.07.2022 06:44:35 UTC
21.07.2022 06:44:35 UTC
22.07.2022 08:06:19 UTC
22.07.2022 08:06:19 UTC

Если сравнить адреса секций, то видно, что у более поздней версии все адреса секций кроме .text смещены на 0x200. Bindiff оценил индекс похожести в 94% с индексом доверия в 99%, то есть файлы практически идентичны.

Если детально сравнивать файлы, то можно обнаружить, что было добавлено в более поздней версии.

По raw-адресу 0xCС00 (0x40D800 VA) в старой версии располагается функция:

В новой версии перед этой функцией добавили ещё одну по raw-адресу 0xCC20 (0x40D820 VA):

Добавленная функция стала вызываться в функциях выполнения команд сервера управления: в функции выполнения произвольной команды с сохранением результатов и в функции remote shell, основанной на pipe’ах:

Также размер .text секции был увеличен из-за выравнивания: с raw-адреса 0x80A00 или 0x80C00 начинается секция .rdata (байты 7E 12 0A …).

Структура RAT

Рассмотрим структуру расшифрованной полезной нагрузки. Помимо самого RAT в PE-файле содержится ещё два PE-файла:

keylogger.exe – очевидно, keylogger.

ConsentUXA.dll – это вредоносная DLL для получения снимков экрана.

Расскажем про них подробнее:

keylogger.exe

Имя файла (keylogger.exe) взято из параметра Name таблицы экспортов.

keylogger.exe – вредоносный файл, представляющий собой 32-разрядную версию keylogger, который по умолчанию записывает результаты своей работы в файл под названием «䝗㡗坐» (ascii-строка WGW8PW). Если пробовать перевести данные символы, то получается что-то непонятное вроде «тигр сидит за дверной занавеской»:

䝗 – «тигр» или «свирепый и отважный солдат»;

㡗 – «дверная занавеска / перегородка», «пушистый / пернатый», вид тонкого шелка;

坐 – «сидеть».

Возможно, нет смысла переводить данные символы, так как ascii-строка WGW8PW используется RAT в качестве egg-строки.

В процессе запуска, если у RAT есть права LocalSystem, то создается отдельный поток, который каждые три минуты выполняет проверку и для каждого нового физического пользователя создает дочерний процесс, в который инжектирует keylogger. Схема инжекта аналогична вышеописанной при запуске самого RAT. Перед самим инжектом в памяти в raw-образе keylogger.exe выполняется замена egg-строки WGW8PW на полный путь до файла keylogger_output_filename из конфига RAT:

Mutex "KeyLog" используется, чтобы функционировал только один экземпляр Keylogger. При запуске создается невидимое окно c названием SetEvent, классом Twnd и процедурой, которая обрабатывает сообщения типа WM_INPUT. Далее с помощью RegisterRawInputDevices окну разрешается получать сообщения WM_INPUT и с помощью GetRawInputData получаются нажатые клавиши, которые записываются в файл в формате:

 \r\n\r\n[%02d:%02d:%02d %4d-%02d-%02d]--[USER:<имя_пользователя>]--[<заголовок_активного_окна >]\r\n<нажатые клавиши>

Перед записью данные шифруются с помощью однобайтового xor 0x7F.

ConsentUXA.dll

Имя файла (ConsentUXA.dll) взято из параметра Name таблицы экспортов.

ConsentUXA.dll – вредоносная 32-разрядная библиотека, содержащая экспортные функции #1 ScreenCapture, #2 ScreenSpy.

Дата компиляции из File Header – 20.07.2022 01:31:32 UTC.

По названиям экспортных функций и некоторым участкам кода можно предположить, что за основу был взят код модуля ScreenSpy Gh0st RAT с репозитория от 31.07.2016.

В репозитории в файле server\2015Remote\2015Remote.rc указана версия 1.0.0.1, что отличается от более старых репозиториев Gh0st RAT:

Также имеются и другие репозитории-клоны с исходными кодами Gh0st RAT версии 3.6. Здесь в качестве примера приведен один из вариантов с самым похожим исходным кодом. Возможно, этот репозиторий 2016 года (от пользователя zibility) содержит более новую версию Gh0st.

Конструктор ScreenSpy в репозитории
ConstructBI в репозитории
CompareBitmap в репозитории

Экспортная функция #1 ScreenCapture выполняет скриншот экрана. Результаты работы выводятся в STD_OUTPUT_HANDLE. Первые 4 байта – размер, далее – данные bmp-файла.

Экспортная функция #1 ScreenSpy постоянно делает скриншоты экрана, имитируя «слежку» в реальном времени. Результаты работы также отправляются в STD_OUTPUT_HANDLE. Используется так называемое сканирование экрана, когда для сравнения выбирается его часть. Перед отправкой выполняется сравнение частей с соответствующими частями предыдущего снимка. В STD_OUTPUT_HANDLE отправляются только измененные части (здесь подробно не будем останавливаться на формате измененных частей, исходный код есть на github). Формат аналогичен: первые 4 байта – размер, далее – измененные данные bmp-файла.

Библиотека wolfSSL

Все рассмотренные образцы общались с C2 через raw-сокеты и библиотеку с открытым исходным кодом wolfSSL, реализующую функции SSL. Стоит отметить, что в строках не было никаких прямых упоминаний этой библиотеки. При анализе кода в глаза бросились специфические коды возврата:

Коды возврата ошибок в wolfSSL имеют отрицательные значения. Также мы обнаружили yara-правило от исследователя Steven Miller, которое также подтвердило использование wolfSSL.

Определение версии

При определении версии библиотеки wolfSSL мы обнаружили опечатку, которая позволила определить интервал возможных версий и понять, когда вышеупомянутое yara-правило перестаёт работать. А позже одна функция и размер структуры WOLFSSL_CTX помогли точно определиться с версией.

Опечатка связана с жестко закодированной строкой DOWNGRD, которая используется в протоколе TLS 1.3. Впервые данная строка с опечаткой появилась в релизе 3.12.0 (08.08.2017) в файле src/internal.c: https://github.com/wolfSSL/wolfssl/releases/tag/v3.12.0-stable

Вместо значения DOWNGRD было использовано значение DOGNGRD (0x47 – "G", 0x57 – "W"). Скорее всего, обычная опечатка. Ее исправили спустя 3 года в версии 4.4.0 (22.04.2020):

Во всех обнаруженных нами образцах присутствовала строка с опечаткой DOGNGRD, поэтому версия используемой библиотеки wolfSSL варьируется от 3.12.0 до 4.4.0 не включительно.

Также мы обнаружили, что в версии 4.5.0 "сигнатуры" клиента и сервера, которые используются при формировании Finished-сообщений в протоколе TLS в файле wolfssl/internal.h, слегка изменились:

В конец сигнатур добавили байт 0x00. Это изменение «ломает» вышеупомянутое yara-правило, так как базовые строки правила

 $base = "CLNTSRVRclient finished" ascii wide

$base2 = "CLNTserver finished" ascii wide

в wolfSSL версии ниже 4.4.0 выглядят по-другому:

Для детектирования wolfSSL любых версий можем порекомендовать использовать жестко закодированные последовательности md5 inner pad и md5 outer pad, которые также используются при формировании Finished-сообщений и задаются в файле wolfssl/internal.c:

Константа PAD_MD5 определена в файле wolfssl/internal.h:

PAD_MD5        = 48,       /* pad length for finished */

 Таким образом, в yara-правило можно добавить следующие базовые строки:

// md5_inner_pad

$base3 = { 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 }

// md5_outer_pad

$base4 = { 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C 5C }

Позже мы обнаружили, что в функции wolfSSL_Init вызывается функция wc_InitRng(&globalRNG):

Так как статическая библиотека была скомпилирована с оптимизацией, мы видим вызов функции wc_InitRng, но в исходном коде в функции wolfSSL_Init вызывается wolfSSL_RAND_seed(NULL, 0), которая далее по цепочке делает вызов wc_InitRng:

wolfSSL_RAND_seed → wolfSSL_RAND_Init → wc_InitRng

Функция wolfSSL_RAND_seed стала вызываться в wolfSSL_Init, начиная с версии 4.1.0:

Таким образом, диапазон версий библиотеки wolfSSL сокращается:

4.0.0 < wolfSSL_version < 4.4.0

Для более точного определения версии мы воспользовались размером структуры WOLFSSL_CTX. Отдельно отметим, что размер структуры меняется в зависимости от различных параметров, задаваемых при компиляции библиотеки wolfSSL. Мы проводили сравнение размеров структур при использовании параметров по умолчанию для разных версий wolfSSL. Скорее всего, злоумышленники при компиляции также использовали параметры по умолчанию (смещения многих значений структуры WOLFSSL_CTX в RAT совпадали со смещениями в оригинальной библиотеке, скомпилированной с параметрами по умолчанию).

Таблица размеров структуры WOLFSSL_CTX с параметрами по умолчанию:

Версия wolfSSL

Размер WOLFSSL_CTX

v4.1.0-stable

0xF4

wolfRand-RC2

0xF8

v4.2.0-stable

0xFC

v4.2.0c

0xFC

v4.3.0-stable

0xFC

Размер структуры WOLFSSL_CTX в RAT – 0xF4:

В итоге из таблицы размеров делаем вывод, что в RAT с большой долей вероятности используется wolfSSL версии v4.1.0-stable (23.06.2019).

Использование в RAT

Для взаимодействия с C2 RAT использует raw-сокеты и методы библиотеки wolfSSL. В данном разделе кратко приведем основные параметры wolfSSL.

Используется TLS 1.2 и режим SSL_VERIFY_NONE, при котором RAT не будет проверять сертификат C2 при подключении:

 У RAT отсутствуют какие-либо встроенные сертификаты.

 Для работы wolfSSL последовательно вызываются все описанные в статье методы (wolfSSL_Init, wolfSSL_CTX_new, wolfSSL_new, wolfSSL_set_fd, wolfSSL_connect), кроме тех, которые связаны с сертификатами.

Для получения и отправки данных соответственно используются wolfSSL_read (в самом RAT используется кастомный метод mw_wolfSSL_readall) и wolfSSL_write.

Классы, STL, RTTI-артефакты

Все методы для взаимодействия с C2 и использования функционала wolfSSL реализованы в классе TCPSocket, который наследуется от базового класса TSocket с виртуальными методами.

Сам RAT написан на C++ и активно использует методы и контейнеры STL. Например, контейнеры и методы std::string, std::wstring, std::vector, std::list, std::shared_ptr, std::tuple, std::unique_ptr, std::ofstream, std::ifstream, std::filebuf.

Имеются RTTI-артефакты, которые содержат кастомные типы данных RAT. Пример одной полной RTTI-строки:

std::_LaunchPad<std::unique_ptr<std::tuple<void (__cdecl*) std::shared_ptr<TSocket>,NETHEAD,MByteArray*),std::shared_ptr<TSocket>,NETHEAD,MByteArray *>

Данная RTTI-информация используется в методах, которые создают std::unique_ptr, который содержит tuple из параметров для функции создания потока. Из длинной RTTI-строки выделим самый полезный фрагмент:

std::_LaunchPad<std::unique_ptr<std::tuple<void (__cdecl*)(std::shared_ptr<TSocket>,NETHEAD,MByteArray*),std::shared_ptr<TSocket>,NETHEAD,MByteArray *>

В нем видны кастомные типы NETHEAD (метаданные для C2) и MByteArray. Также при создании соответствующих структур данных в IDA всегда стоит обращать внимание на код инициализации параметров, так как контейнер tuple в различных реализациях может располагать в памяти параметры в обратном порядке, что и наблюдалось в наших образцах:

Остальные интересные RTTI-строки мы вынесли в раздел «Indicators of Compromise». Возможно, они пригодятся для сравнения при появлении новых релизов RAT. А они точно будут. По коду видны кастомные типы данных, классы, наследование, динамический полиморфизм. А по коду версии 3.3a видно, что идёт активная разработка и доработка RAT.

Функционал RAT

Ниже опишем самые интересные функции RAT, которыми обладают далеко не все представители данного класса ВПО.

Failover C2

Имеется функционал смены C2 (failover C2), который получает новый адрес C2. Срабатывает, если в конфиге задан параметр new_c2_url. А также в двух случаях:

  • до основного цикла общения с C2, если в конфиге вообще не указан C2;

  • в основном цикле общения с C2, если не удалось подключиться.

Через wolfSSL GET-запросом с new_c2_url загружаются данные во временный файл (имя файла генерируется с помощью функции tmpnam_s, например, %temp%\uwg.0), который затем считывается в память. После этого временный файл удаляется. Берется строка между жестко закодированными строками YTc1YzI1Y2ZmNTg4NDFj и YjVhZDI1MGUzNjJmMThkZmI. Расшифровывается сначала по алгоритму base64, затем через xor 9. Схема хранения и «дополнения» xor-ключа аналогична описанной в разделе «Расшифровка RAT и запись конфига».

В итоге получается строка следующего формата:

new_c2_URI\tnew_c2_port\tnew_operation_mode_value, где

 new_c2_URI – URI нового C2.

new_c2_port – порт для подключения к новому C2.

new_operation_mode_value – принимает значения 0, 1, 2.

 0 – создать shared_ptr<TCPSocket>.

1 – использовать существующий shared_ptr<TCPSocket>.

2 – запустить поток отстука на C2 – каждую минуту на C2 отправляется только заголовок (без блока данных), содержащий команду 0x70000019.

В конфигах проанализированных нами образцов параметр new_c2_url отсутствовал.

Authenticated Proxy

RAT может соединяться с C2 через http proxy-серверы с использованием HTTP Tunneling. Также поддерживаются прокси с аутентификацией на основе механизма GSSAPI SPNEGO. Пример одного из запросов RAT в процессе аутентификации:

CONNECT <c2_url>:<c2_port> HTTP/1.1 \r\n

User-Agent: My Service Endpoint 1.0\r\n

Host: <c2_url>\r\n

Proxy-Connection: Keep-Alive\r\n

Pragma: no-cache\r\n

Proxy-Authorization: Negotiate <base64_encoded_client_token>\r\n

Content-Length: 0\r\n\r\n

User-Agent: My Service Endpoint 1.0 был замечен в следующем репозитории: https://github.com/airfer/NtlmProxy/blob/2c83aa83f29e9f24f9efcd0c2a0e4850328d07e0/ntlm_proxy.c#L320

Возможно, какая-то его часть использовалась при разработке кода, ответственного за аутентификацию на прокси.

Взаимодействие с C2

Первым пакетом на C2 отправляется информация о системе и параметры RAT. В качестве ID клиента все так же используется MD5-хеш строки MAC-адреса и имени хоста (конкатенируются без разделителей), как было в версии 1.0. С системы собираются те же данные, которые дополняются большим количеством параметров RAT. После ожидается ответ от C2.

Сначала получается заголовок, затем, при необходимости, загружаются данные. Все пакеты (заголовок и данные) перед отправкой через wolfSSL полностью дополнительно шифруются по алгоритму RC4 с ключом 0x9CEC78BD. При получении сначала выполняется расшифровка по алгоритму RC4 с тем же ключом.

Данные, собираемые с жертвы

 Каждый параметр разделяется символом табуляции 0x9 – "\t".

Название параметра

Описание

BYTE client_id[16]

MD5(MAC+hostname)

char hostname[]

Имя хоста

char username[]

Имя пользователя

char IP_decimal_str[]

IP в виде десятичного числа.
Для получения IP – перевести в hex
и считать как little endian

char version[]

версия RAT ("3.3a")

char cpu_arch[]

"32" или "64"

char OSMajorVersion[]

Данные ОС

char OSMinorVersion[]

char BuildNumber[]

char PlatformID

2 (VER_PLATFORM_WIN32_NT)

BYTE ProductType

Тип хоста

1 (VER_NT_WORKSTATION)

2 (VER_NT_DOMAIN_CONTROLLER)

3 (VER_NT_SERVER)

char CreateProcessType[]

Значение отображает тип WinAPI вызова для создания процессов.

"system" – CreateProcessA.

"user" – CreateProcessAsUserA.

char exe_filepath[]

Значение параметра exe_filepath конфига

char end

"0". После данного символа знак табуляции не добавляется

Общая структура пакетов, отправляемых на C2

Название параметра

Описание

Заголовок (NETHEAD)

dword cmd_id

id команды, например, 0x70000001

dword reserved1

0

dword cmd_return_code

0 или код возврата выполненной команды

dword data_size

размер данных

Данные

Результаты выполнения команд. Данные также могут вовсе отсутствовать, например, при выполнении команд без сохранения вывода или обмене ping-сообщениями.

Структура первого пакета, отправляемого на C2 (зленым – заголовок, красным – данные):

Общая структура пакетов, получаемых от C2

Название параметра

Описание

Заголовок (NETHEAD)

аналогичен отправляемым данным

Данные

int arg1_size

размер первого аргумента

int arg2_size

__int64 offset

размер второго аргумента

смещение
в файле

int is_2nd_arg_present

1 – второй аргумент присутствует.

0 – в противном случае

char arg1[arg1_size]

аргументы выполняемых команд

char arg2[arg2_size]

Таблица выполняемых команд

Команда

Описание

0x70000001

Собрать и отправить system info

0x70000002

Получить список содержимого указанного каталога

0x70000003

Используется WinAPI SHFileOperationW.

3 – копирование файлов;

4 – перемещение файлов;

5 – перемещение каталогов;

6 – удаление файлов.

Для команд 3-5 используется два аргумента с поддержкой wildcards.

arg1 – source.

arg2 – destination.

В команде 6 – только arg1

0x70000004

0x70000005

0x70000006

0x70000007

В отдельном потоке считать данные по заданному смещению для указанного файла и отправить их на C2.

arg1 – имя файла.

arg2 – смещение

0x70000008

В отдельном потоке перезаписать указанный файл данными от C2

Сначала на C2 отправляется размер указанного файла, далее идёт загрузка данных с C2 и их запись в файл

0x70000009

В отдельном потоке запустить remote shell, работающий
на pipe’ах. Перед созданием shell на C2 дублируется полученный заголовок (без данных)

0x7000000B

Получить информацию о логических дисках в формате

drive_label\tdrive_type\tdrive_size\tfree_space\n

0x7000000C

Создать каталог

0x7000000F

Завершить дочерний процесс keylogger

0x70000010

Выполнить cmdline с помощью CreateProcess, используя два аргумента:

arg1 – application name;

arg2 – application arguments.

Отправить на C2 код возврата в заголовке

0x70000011

Выполнить cmdline и отправить результат на C2

0x70000012

Включить режим, при котором будет использоваться CreateProcessAsUser (по умолчанию используется)

0x70000013

Включить режим, при котором будет использоваться CreateProcessA

0x70000014

Создать поток, который будет перенаправлять весь трафик
на заданный хост и порт. Перенаправление выполняется через raw-сокеты (send, recv).

arg1 – структура данных для перенаправления

0x70000015

Создать поток, в котором запускается ScreenSpy (слежка
в реальном времени).

В текущий каталог дропает ConsentUXA.dll, делает его скрытым и создает процесс rundll32.exe ConsentUXA.dll ScreenSpy <c2_arg1_bits_per_pixel>.

Данные сжимаются через deflate и отправляются на C2, dll удаляется.

arg1 – bytes per pixel

0x70000016

Сделать скриншот.

В текущий каталог дропает InitializeCriticalSection.dll, делает его скрытым. Перенаправляет потоки ввода/вывода будущего процесса
в пайпы и создает процесс rundll32 InitializeCriticalSection.dll ScreenCapture <c2_arg1_bits_per_pixel>.

Данные сжимаются через deflate и отправляются на С2, dll удаляется.

arg1 – bytes per pixel

0x70000017

Отправить содержимое файла keylogger на C2

0x70000018

Удалить файл keylogger

0x70000019

Ping-сообщение без данных. Этой командой обменивается RAT с C2 в потоке отстука. Данная команда игнорируется
в потоке обработки команд

0x70000021

Создать поток с remote shell через screen buffer. Перед созданием shell на C2 дублируется полученный заголовок (без данных). С C2 идёт обмен информацией о координатах курсора, введенных символах и их атрибутах. Эти данные сжимаются через deflate и отправляются на C2

Заключение

Выше мы описали загрузчик, структуру и новые возможности обновленной версии RAT, поделились подробностями библиотеки wolfSSL, которые позволяют определить ее версию, и показали особенности детектирования различных версий yara-правилами.

Мы убеждены, что новая версия RAT находится в активной разработке, так как видны значительные отличия по сравнению с версией 1.0. В новой версии многое добавлено (wolfSSL, keylogger, ScreenSpy, новые команды), но видны и «старые» приемы. Например, способ хранения xor-ключей, использование egg-строк, аналогичный алгоритм генерации id клиента и другие.

У нас недостаточно данных, чтобы утверждать, что данный RAT дорабатывался или эксплуатировался все той же группировкой APT31. Но, судя по схожести в тактиках и техниках злоумышленников, за использованием данного RAT стоят APT-группировки азиатского региона. В частности:

  • схемы запуска – DLL Hijacking;

  • каталоги размещения – C:\ProgramData;

  • инструментарий – в рамках инцидента злоумышленники также использовали бэкдор PlugX;

  • заимствование кода компонентов из Gh0st RAT;

  • другие.

Indicators of compromise

Контрольные суммы приводятся в формате SHA256.

 Загрузчик вместе с соответствующей зашифрованной полезной нагрузкой RAT

msedgeupdate.dll

7259da2c7c36dab389fd3ab56d053fef8aa581fab84dbb36327e271e52917906

1Cdatas

064a158a475d275c6be6d79b86c32f33e1723837020e05e0df96bbd2767ca1ca

msedgeupdate.dll

2676ff82303dd30c23608d55c3d5214e18319d584763afe8ec058ca6ab149d51

1cdata

267c0a4ffb1a70028f841e0acf960dfdb87971129043baa2869f61e2a74a96d4

 PE-файлы, встроенные в RAT

keylogger.exe

b3418389642603379450f51b0c6028d1dbaa44bada61857d9d9cf0f564b84e64

ConsentUXA.dll

2f54780cb0f04cfe7304d7863a71449a5defe55e8a27ca2d1a1476f1ebdd55c9

C2

api.inliines[.]com (213.183.53[.]156)

1.1. RTTI-строки

std::_LaunchPad<std::unique_ptr<std::tuple<void (__cdecl*)(std::shared_ptr<TSocket>,NETHEAD,MByteArray*),std::shared_ptr<TSocket>,NETHEAD,MByteArray *>

std::_LaunchPad<std::unique_ptr<std::tuple<void (__cdecl*)(std::shared_ptr<TSocket>),std::shared_ptr<TSocket> >

std::_LaunchPad<std::unique_ptr<std::tuple<unsigned long (__stdcall*)(void)>

std::_LaunchPad<std::unique_ptr<std::tuple<unsigned long (__stdcall*)(std::shared_ptr<TSocket>,void *),std::shared_ptr<TSocket>,void *>

std::_LaunchPad<std::unique_ptr<std::tuple<unsigned long (__stdcall*)(TSocket *,unsigned int),TSocket *,unsigned int>

std::_LaunchPad<std::unique_ptr<std::tuple<unsigned long (__stdcall*)(NETHEAD,int,char *),NETHEAD,int,char *>

std::_LaunchPad<std::unique_ptr<std::tuple<unsigned long (__stdcall*)(NETHEAD,int),NETHEAD,int>

std::_LaunchPad<std::unique_ptr<std::tuple<unsigned long (__stdcall*)(NETHEAD,__int64,std::basic_string<char,std::char_traits<char>,std::allocator<char> >),NETHEAD,__int64,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >

std::_LaunchPad<std::unique_ptr<std::tuple<unsigned long (__stdcall*)(NETHEAD),NETHEAD>

Автор: Антон Каргин, инженер технического расследования "РТК-Солар".

Комментарии (0)