В конце 2023 года команда Solar 4RAYS в рамках проведения Compromise Assessment обнаружила атаку на один из органов исполнительной власти. Мы выявили образцы многоступенчатого ВПО, которое на финальной стадии разворачивает на целевой системе имплант, названный нами DFKRAT. Разработанный в шпионских целях вредонос позволяет атакующим, среди прочего, похищать данные из файловой системы жертвы. Мы провели расширенное исследование и нашли другие образцы, отличающиеся технически, но используемые той же группой злоумышленников на протяжении 2021–2023 годов против русскоязычных целей. В настоящий момент мы не можем отнести кластер этой активности к какой-либо известной группировке, поэтому обозначили его как NGC2180. В статье мы подробно опишем найденное ВПО и приведем индикаторы компрометации.

Краткое содержание отчета

  • Мы обнаружили шпионский имплант DFKRAT на системах одного из заказчиков в конце 2023 года.
  • В открытых источниках были найдены и другие версии этого ВПО и прослежена их эволюционная цепочка с 2021 года, кластер этой активности мы назвали NGC2180.
  • Первичный вектор заражения в случае ранних версий, по всей видимости — таргетированное фишинговое письмо, начиненное загрузчиком. В последних версиях вектор остался неизвестным.
  • Вредоносная активность осуществляется полезной нагрузкой, которая доставляется загрузчиками в более ранних версиях и дропперами, эксплуатирующими DLL side-loading, — в последних.
  • Ключевой функционал DFKRAT -— эксфильтрация файлов, поддержка интерактивного шелла, потенциальная загрузка дополнительного ВПО с сервера управления С2.
  • В качестве С2 для актуальной версии имплантов использовались скомпрометированные серверы Национального центра научных исследований в Греции и индонезийской компании.
  • Нам удалось найти и проанализировать фрагмент управляющего сервера.

В публичном пространстве не было найдено образцов последней версии вредоноса, обнаруженной нами на зараженных системах, однако мы нашли более ранние версии, отличающиеся менее продвинутой реализацией.

таймлайн основных событий кластера ngc2180
Таймлайн основных событий кластера NGC2180

II версия — конец 2023 года (актуальная)

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

  • легитимный исполняемый файл, импортирующий функцию из вредоносной библиотеки;
  • вредоносная библиотека, внедряющая шелл-код в легитимный процесс;
  • бинарный файл, содержащий шелл-код и конечную нагрузку в виде зашифрованного PE-файла.

C:\Program Files\CUAssistant\Download\
C:\Program Files\HP\Shared\bin\

Дроппер #1

В первом исследованном случае все файлы, принимающие участие в атаке, находились в директории C:\Program Files\CUAssistant\Download\:

  • ExportController.exe — легитимный исполняемый файл, импортирующий функцию из вредоносной библиотеки;
  • CoreFoundation.dll — вредоносная библиотека, внедряющая шелл-код;
  • config — бинарный файл, содержащий шелл-код и конечную нагрузку в виде зашифрованного PE-файла.

Цепочка запуска вредоносного компонента начинается с исполняемого файла ExportController.exe (MD5: 379b72a6fadeb462ec884ca868025b6c). Он является компонентом QuickTime (разработанная Apple мультимедийная платформа) и подписан цифровой подписью, отозванной еще в 2017 году:

Name Apple Inc.
Issuer VeriSign Class 3 Code Signing 2010 CA
Valid From 12:00 AM 07/29/2015
Valid To 11:59 PM 08/27/2017
Thumbprint 173A28539CA6DAB5AC8C3B995ABAA692F95C5FC4
Serial Number 2B 20 EB 33 80 79 2A B0 11 F6 62 C0 64 FD B4 73

Для запуска вредоносного функционала злоумышленники эксплуатируют технику DLL Side-Loading — так ExportController.exe импортирует и вызывает пару функций CFDataCreate и CFRelease из библиотеки CoreFoundation.dll. Обе функции не несут в себе никакого функционала, а их импорт носит символический характер для вызова функции DllEntryPoint, внутри которой и находится вредоносный код

MD5 bb64fcb014d913b92975fccd5fa3e9b1
SHA1 277624720ede4ed518ac7af599147c7f6bbdf7a4
SHA256 144572ee5214f7289d1f71a44d90eb4ba2ac7d48f6e6b5b166f8ca7fe89afe9c
File name CoreFoundation.dll
File type PE32 executable (DLL) Intel 80386
File size 139 Kb
Comp. timestamp 2023-08-05 06:53:19 UTC

Вредоносный код стартует созданием изначально легитимного процесса со следующей командной строкой:

C:\Windows\System32\dllhost.exe /Processid:{C59AY5E4-1B4C-90A8-0907-A28FABCA1221}

После чего открывается файл config, который располагается в директории с исполняемым файлом и его содержимое инжектится в предварительно созданный процесс с помощью функций:

  • ZwAllocateVirtualMemory
  • ZwWriteVirtualMemory
  • ZwQueueApcThread
  • ZwResumeThread

Данный способ не создает дополнительного потока, а переводит созданный при инициализации процесса поток на необходимый адрес.

Интересной особенностью данного дроппера также является прямая работа с вызовами функций WinAPI, которые импортируются напрямую из ntdll.dll. Работая в 32-битном режиме, он сначала находит номер нужного API вызова, а затем через WinAPI вызывает эту функцию по ID следующей инструкцией:

call dword ptr fs:[0C0h]; wow64cpu!X86SwitchTo64BitMode

Для поиска номера нужной функции на первом этапе cоставляется таблица из адресов функций и их захешированных имен (используются только те, названия которых начинаются с Zw). Далее таблица сортируется по адресу функций, а последовательный номер в ней является номером искомой функции. Код алгоритма можно найти в Приложении I.

Внедренное таким образом содержимое файла config представляет собой шелл-код, который расшифровывает себя при помощи ключа 0x9708767C, используя следующий алгоритм:


key = 0x9708767C 
_DWORD *encrypted_shellcode;
for (int i = 0; i < shellcode_length; i++) { 
	encrypted_shellcode[i] ^= key; 
	key += encrypted_shellcode[i]; 
 } 

Далее управление передается на расшифрованную часть, которая содержит в себе PE-файл полезной нагрузки (MD5: 8ce681f1c9ddf69a4ad4d53de57e404f) и код, который загружает его в память и передает управление на Entrypoint.

Дроппер #2

Во втором исследованном случае все файлы, участвовавшие в атаке, находились в директории C:\Program Files\HP\Shared\bin\:

  • cdosys.dll.exe — легитимный исполняемый файл, импортирующий функцию из вредоносной библиотеки;
  • d3d10_1.dll — вредоносная библиотека, внедряющая шелл-код;
  • Dfk — бинарный файл, содержащий шелл-код и конечную нагрузку в виде зашифрованного PE-файла.

Работа вредоноса начинается с запуска cdosys.dll.exe (MD5: d7881fc5e3f93b39d3e84ccf988cc392) и дальнейшей эксплуатации метода DLL Side-Loading. Файл является компонентом ManyCam — софта для стриминга видео от компании Visicom Media, его оригинальное имя в составе легитимного ПО — hw_feature_tester.exe. Он подписан соответствующей валидной цифровой подписью:

Name Visicom Media Inc.
Issuer DigiCert Trusted G4 Code Signing RSA4096 SHA384 2021 CA1
Valid From 12:00 AM 01/19/2022
Valid To 11:59 PM 01/18/2025
Thumbprint 1CF5550BA6165341E28D773228634AE84CECBF23
Serial Number 0E 8A C3 0E E7 30 1E BF FE 46 B4 FE 3E D0 76 EB

В процессе работы файл загружает вредоносную библиотеку d3d10_1.dll и вызывает одну из экспортных функций D3D10CreateDevice1. Сама библиотека предназначена для загрузки шелл-кода в память вновь созданного целевого процесса.

MD5 1da3ce8c4267bf982e43472a08fa2ca1
SHA1 88c5c02581b4b7fd3897d440f41d2bada9b6e704
SHA256 a7aa8a58a7f78b56623ed6475cb952ce82aec84e6ceebb2a8ffeea76edd3b486
File name d3d10_1.dll
File type PE32 executable (DLL) Intel 80386
File size 284 Kb
Comp. timestamp 2013-02-20 12:18:18 UTC

По всей видимости, в этом случае NGC2180 воспользовались техникой Timestomping, а именно: подделали дату компиляции вредоносной библиотеки, чтобы ввести в заблуждение потенциальных исследователей и системы защиты.

Примечательно, как атакующие воспользовались уже существующими возможностями выбранного ими исполняемого файла. В зависимости от аргументов командной строки, с которыми осуществлялся запуск cdosys.dll.exe, менялся и поток исполнения:

  • сdosys.dll.exe
    Создает и запускает службу WinInit (display name WinInit Start Service).
    binPath= C:\Program Files\HP\Shared\bin\cdosys.dll.exe check
    Таким образом обеспечивается персистентность импланта на хосте жертвы;
  • cdosys.dll.exe check
    Создает дочерний процесс cdosys.dll.exe check -s;
  • cdosys.dll.exe check -s
    Запускает основной функционал дроппера.

При запуске основного функционала вызовом CreateProcessW создается легитимный процесс с валидной цифровой подписью C:\Windows\System32\winrshost.exe (Host Process for WinRM's Remote Shell plugin). Далее открывается файл Dfk для инжекта его содержимого в созданный процесс. Внедрение выполняется с помощью следующих вызовов:

  • NtAllocateVirtualMemory;
  • NtWriteVirtualMemory;
  • GetThreadContext;
  • SetThreadContext;
  • ResumeThread.
MD5 dc9f192bb1f6db275feab8e8cfef28b9
SHA1 c899f4ae89f38ce5c8d25a2878cef43980930f2b
SHA256 144572ee5214f7289d1f71a44d90eb4ba2ac7d48f6e6b5b166f8ca7fe89afe9c
File name Dfk
File type Shellcode
File size 13.13 Kb
Файл Dfk имеет следующую структуру:
	
struct Dfk {
	char shellcode[1136];
	char egg[10] =  { 0x02, 0x06, 0x12, 0x16, 0x22, 0x26, 0x32, 0xA8, 0xB6, 0x3B };
	int rc4_key_size = 0x400; // 1024 bytes
	char rc4_key[rc4_key_size]; // Ключ RC4 для расшифровки полезной нагрузки
	int pe_file_size = 0x2c00;
	char pe_file[pe_file_size]; // Полезная нагрузка
}

Перед записью нагрузки расшифровываются первые 1136 байт шелл-кода кастомным алгоритмом (код расшифровки можно найти в Приложении I). Далее с помощью функции NtWriteVirtualMemory происходит запись содержимого Dfk в созданный процесс и передача управления на расшифрованный шелл-код.

В процессе анализа мы заметили интересную особенность: вызов CreateProcessW без установленного флага 0x00000004 (CREATE_SUSPENDED). Таким образом, управление на шелл-код передается в running-потоке и исполняется в штатном режиме, что противоречит документации вызова GetThreadContext из MSDN:

Сам шелл-код расшифровывает уже PE-файл, следующий далее в структуре файла Dfk, с помощью алгоритма RC4 и зашитого ключа длиной 1024 байт

343638440F253B4F60541D332E5C44522B324A466033310D5338405250251E63522A2B42180C14474A16181B5610353B5B163345333B51534A2B400E1D5A4A4D4B26474441272C3E5E1C1F1A4E3722565951543B0C3B5A4541424516524B5237235B480E4A2A114D5360450E353B16551B45291B3A4A521B145B1512620B57441B3B33360C3D3D2F46294340142F1D3111240F47232A0D2322234A11160C214E192A12203060344D220C1D32571830251047315360362B1E3A1B2622333B12634248123A283B4E3A48452E5A3B525427190B2112390E0D3F522A22291A4D32281C2A45352B1C604D3B455E2D0E185D554E233A1C2C513E3D160C3020101E155D34

На последнем этапе осуществляется подготовка к исполнению расшифрованного файла:

  • Резолв необходимых API-вызовов по хешу для дальнейшей загрузки PE в память (LoadLibraryExA, GetProcAddress, VirtualAlloc);
  • Выделение RWX секции в памяти текущего процесса под загрузку PE;
  • Маппинг секций PE-файла на выделенную память;
  • Заполнение таблицы импорта;
  • Передача управления на entrypoint PE-файла (MD5: 2e131ee69a4eee238a5353f34a81faed).

Финальная стадия — полезная нагрузка DFKRAT

Как мы отмечали ранее, первый и второй дроппер заметно отличаются друг от друга используемыми техниками, но в конечном счете оба разворачивают на системе один и тот же имплант, названный нами DFKRAT. Его образцы в обоих исследованных нами случаях имели минимальные различия, поэтому рассмотрим функционал DFKRAT на примере одного из них:

MD5 2e131ee69a4eee238a5353f34a81faed
SHA1 da491c0e1dfdf632c7e03b223d975f1b5c84c63f
SHA256 173ef7cc57507c21606926fbd2f2d8ea1c65fc911585ca857a2865d890be4eff
C2 hxxps://mail.inn.demokritos[.]gr/yui/2.7.0/fonts/foot.jsp
File type PE32 executable Intel 80386
File size 11 Kb
Comp. timestamp 2023-10-12 06:20:23 UTC

После запуска импланта в памяти целевого процесса устанавливается мьютекс с именем «loasd6asdg6». Если такой мьютекс уже существует, то вредонос завершает работу.

Далее вычисляется уникальный ID жертвы, использующийся при коммуникации с управляющим сервером (С2). Для этого в ключ реестра (HKLM | HKCU):SOFTWARE\Classes\CLSID:CLSID записывается значение, вычисляемое вызовом QueryPerformanceCounter. Данная ветка реестра является перенаправляемой, поэтому при запуске исследуемых сэмплов на системах с архитектурой процессора отличной от x86 актуальной веткой реестра будет: HKCU:SOFTWARE\Classes\WOW6432Node\CLSID:CLSID.

Конечный ID жертвы вычисляется по формуле:

victim_id = HKCU:SOFTWARE\Classes\WOW6432Node\CLSID:CLSID + 0x4C

Вредонос коммуницирует с С2 с помощью POST-запросов, которые формируются по следующему шаблону:

%s?name=%x&id=%08x

Где id – ранее вычисленный ID жертвы, а параметр name характеризует тип запроса к С2 и может принимать следующие значения:

name Название Описание
1000 QueryControl Первичное соединение
1111 WriteHost Получить команду
1112 WriteSDat Отправить результат команды

Перед отправкой на С2 тело запроса шифруется алгоритмом RC4 на ключе Y%523^%$3fttT3f. Кроме того, используется специфичное значение User-Agent, которое также захардкожено в импланте - Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.04472.106 Safari/537.36.

Screenshot 2024-02-05 at 14.25.34.png
Адрес С2, RC4 ключ и User-Agent, захардкоженные в DFKRAT

Перед описанием протокола общения с С2, рассмотрим особенность реализации обертки над функцией Sleep. При расчете времени ожидания используются криптографически случайное значение длиной 4 байта – rnd, генерируемое WinAPI CryptGenRandom(), и два значения, передающиеся аргументами функции-обертки, min_lim и max_lim:

Sleep(min_lim + rnd % (max_lim - min_lim))

Итоговая продолжительность “сна” при такой реализации будет варьироваться в интервале (max_lim, min_lim):

mw_random_sleep.PNG
Функция-обертка Sleep

Данная реализация функции Sleep используется для ухода от систематичности обращений (запросы через одинаковые промежутки времени) к С2, которая характерна для различных семейств ВПО. По-видимому, такая техника является попыткой замаскировать сетевую активность импланта от защитных решений.

Для инициализации первичного соединения с С2 имплант отправляет запрос со значением параметра name = 1000:

  • Если сервер не вернул данные, то вредонос засыпает на 5-15 минут перед повторной попыткой инициализации.
  • Если же данные были получены, то вредонос переходит к отправке следующего типа запроса.

Запрос с параметром name = 1111 информирует сервер о готовности получать команды:

  • При успешном соединении с сервером, имплант парсит полученные данные.
    В ином случае вызывается Sleep протяженностью 30-60 секунд, после чего запрос повторяется.
  • Если же после четырех таких попыток соединение с сервером не установлено, то имплант засыпает на 10-20 минут и возвращается к этапу инициализации соединения.
  • Если сервер доступен и данные корректно распарсились, то имплант выполняет команду, отправляет результат на С2 и ожидает 1-3 секунды новых данных от сервера. Если сервер не отвечает, он возвращается к этапу инициализации соединения.

Запрос с параметром name = 1112 предназначен для отправки на С2 результата выполненной команды.

Протокол взаимодействия импланта и С2 имеет следующую структуру данных, которая справедлива как для соединений server → host, так и для host → server:

Структура тела запроса
Структура тела запроса
  • cmd_number – уникальный идентификатор команды
  • cmd_data_size – размер зашифрованных полезных данных
  • encr_cmd_data – зашифрованные алгоритмом RC4 полезные данные (имя и содержимое прочитанного\записанного файла, ввод\вывод интерактивного шелла, время ожидания для команды Sleep).

Список команд, которые способен выполнять имплант:

cmd_number Описание
0x2121 Запустить интерактивный шелл (cmd.exe)
0x2122 Выгрузить файл с сервера в директорию %PUBLIC%
0x2123 Загрузить файл на север
0x2124 Ждать указанное время
0x2125 Завершить работу (ExitProcess)

Отдельно отметим реализацию команды ожидания (0x2124). Ее выполнение происходит за счет функции Sleep, но вызывается она не один раз с установленным временем сна, а каждую секунду до тех пор, пока заданное количество секунд не истечет. Такая реализация имеет смысл, если процесс сна будет прерываться или состояние контролируется какой-либо переменной и необходимо не блокировать поток. Но в данном случае никаких параметров, кроме счетчика оставшегося времени, не проверяется.

Как видно из списка возможных команд, имплант предоставляет злоумышленнику расширяемые возможности по манипуляциям в атакуемой системе (от эксфильтрации пользовательских данных до потенциальной загрузки с С2 другого ВПО).

I версия – 2022

В ходе расследования мы обнаружили несколько исторических образцов. По всей видимости, они относятся к атакам, произошедшим в 2021-22 годах, которые мы атрибуцируем к тому же кластеру NGC2180. Речь идет о версии импланта DFKRAT, отличающейся от описанной выше, а также о другом механизме его доставки на целевую систему, а именно с помощью загрузки с удаленного сервера.

Загрузчик

В этом случае нам удалось восстановить практически весь процесс kill-chain, который начинается с документа-приманки, по-видимому, присланного жер