Автор:
Владислав Лунин
Старший специалист группы исследования сложных угроз экспертного центра безопасности Positive Technologies
Владислав Лунин
Старший специалист группы исследования сложных угроз экспертного центра безопасности Positive Technologies
В третьем квартале 2024 года специалисты TI-департамента экспертного центра безопасности Positive Technologies (PT Expert Security Center, PT ESC) в рамках исследования угроз обнаружили серию атак, направленных на государственные структуры России. Связей с уже известными группами, использующими такие же техники, нам установить не удалось. Основной целью киберпреступников был шпионаж и закрепление в системе для развития последующих атак. Эту группировку мы назвали TaxOff из-за использования писем на правовые и финансовые темы в качестве приманок. В своих атаках злоумышленники использовали написанный минимум на C++17 бэкдор, который мы назвали Trinper из-за артефакта, используемого при связи с C2.
Начальный вектор заражения — через фишинговые письма. Мы обнаружили несколько таких: в одном была ссылка на «Яндекс Диск» с вредоносным содержимым, связанным с «1С», в другом — фальшивый установщик, связанный со специальным ПО для заполнения справок о доходах и расходах для госслужащих, которым необходимо подавать их каждый год. И ежегодно это ПО обновляется и становится целью для злоумышленников, распространяющих ВПО под видом обновлений.
Письмо содержало ссылку на «Яндекс Диск», в котором хранился файл «Материалы.img» с таким содержимым:
Другой вектор содержал специальное программное обеспечение «Справки БК» для заполнения справок о доходах и расходах госслужащих. Этот вектор уже использовался одной из групп для распространения бэкдора Konni в виде переименованного файла WEXTRACT.EXE.MUI, который отвечает за извлечение сжатых CAB-файлов. В нашем случае в них содержатся два других исполняемых файла: bk.exe — «Справки БК» (см. рисунок 2) и DotNet35.exe — бэкдор Trinper.
Аналогично CAB-файлу в разделе ресурсов RCData есть атрибуты последовательности запуска файлов, хранящихся в нем. Первый атрибут под названием RUNPROGRAM содержит инструкции по выполнению определенной программы или команды в самом начале и запускает bk.exe.
Второй атрибут — POSTRUNPROGRAM — также содержит инструкцию для запуска исполняемого файла, но уже после выполнения RUNPROGRAM. Таким образом, после выполнения bk.exe будет выполнен DotNet35.exe.
Прежде чем приступить к функциональному описанию бэкдора, для полноты понимания его работы также стоит описать его архитектуру, STL, паттерн проектирования, кастомную сериализацию и буферный кэш.
Как и любое другое многопоточное приложение, Trinper построен на одной из парадигм параллельного программирования, а именно на потоковом параллелизме. В этом подходе задачи разбиваются на последовательные этапы, каждый из которых может выполняться параллельно с другими, что и демонстрирует схема ниже.
Один из аспектов потокового параллелизма — это передача данных между потоками, которая реализуется за счет использования глобальных переменных. Их можно разделить на следующие группы:
Библиотека стандартных шаблонов — это набор согласованных обобщенных алгоритмов, контейнеров, средств доступа к их содержимому и различных вспомогательных функций в C++, которые активно используются бэкдором. Первичный признак присутствия рантайма STL — это сообщения об ошибках для различный контейнеров.
Строки — это объекты в виде последовательности символов. Символы могут быть как в кодировке ascii, так и в кодировке wide, что позволяет однозначно различать эти контейнеры. В std::string максимальная длина заранее определенного буфера не может превышать 15 байт, иначе будет выделен буфер в куче. В случае с std::wstring длина заранее определенного буфера не может превышать 7 байт. Этот рантайм — для сравнения длины хранимой строки и возможного последующего выделения динамического буфера, что позволяет однозначно определить один из используемых контейнеров.
Векторы — это контейнеры для последовательностей в виде массивов, которые могут меняться в размерах. Один из вариантов распознавания рантайма вектора — сравнение следующих друг за другом адресов памяти и дальнейшее присвоение одному из них нового значения. Потому что если указатель на первый элемент вектора будет равен указателю на последний элемент, например при добавлении нового элемента, то тогда вектору, прежде чем добавить его, придется изменить его текущий размер и выделить дополнительно память, и переопределить указатель на последний элемент.
Списки — это контейнеры последовательностей, которые позволяют выполнять вставку и стирание в любом месте последовательности с постоянным временем, а также итерацию в обоих направлениях. Один из вариантов распознавания рантайма двухсвязного списка: необходимо ориентироваться на сравнение выбранного буфера с последующим, чтобы определить, был ли достигнут конец при переборе элементов.
Карты — это ассоциативные контейнеры, в которых хранятся элементы, образованные комбинацией ключевого и сопоставленного значений в определенном порядке. Один из вариантов распознавания рантайма карты: карта должна знать, есть ли уже элемент с предоставленным ключом, прежде чем вставить новую пару или вернуть значение по ключу.
Неупорядоченные карты — это ассоциативные контейнеры, в которых хранятся элементы, образованные комбинацией ключевого значения и сопоставленного значения и позволяющие быстро находить отдельные элементы по их ключам. Одним из вариантов распознавания рантайма неупорядоченной карты является способ хранения ее элементов: в хеш-таблицах. Это значит, что для любого индекса элемента всегда будет вычисляться хеш-сумма, в независимости от операции.
Двусторонние очереди — это контейнеры последовательности с динамическими размерами, которые могут расширяться или сжиматься с обоих концов (как спереди, так и сзади). Одним из вариантов распознавания рантайма двусторонней очереди является доступ к элементам, находящимся в блоках. Их размеры кратны двум, поэтому для доступа используются побитовые операции, чтобы разбивать индекс на блок и смещение.
Умные указатели — управляют хранением указателя, предоставляя ограниченные возможности по сбору мусора и, возможно, разделяя это управление с другими объектами. Один из вариантов распознавания рантайма умного указателя — наличие атомарных операций; если число общих указателей на объект уменьшилось до нуля, то вызывается удаление управляющего блока.
std::filesystem предоставляет средства для выполнения операций над файловыми системами и их компонентами, такими как пути, обычные файлы и каталоги. Один из вариантов распознавания рантайма его использования — наличие функций с префиксом _std_fs_*, что свидетельствует об операциях с файловой системой.
Чтобы не создавать структуры вручную, нам нужно подключить заголовочные файлы. Для определения их расположения запускаем x86/x64 Native Tools Command Prompt for VS 20XX (в зависимости от разрядности исполняемого файла) и вводим команду echo %INCLUDE%. Копируем все пути и вставляем в Options > Compiler > Include directories. Прописываем также в качестве аргументов -target x86_64-pc-win32/i386-pc-win32 -x c++ , так как будут подключаться заголовочные файлы C++.
Так как в большинстве случаев тип элемента контейнеров задается во время компиляции, что означает, что просто так нельзя будет подключить, например, заголовочный файл вектора и ждать, что подключаться все типы элементов. Для этого нужно создавать отдельный заголовочный файл, в котором будет явно определен тип элемента контейнера.
Паттерн проектирования — повторяемая архитектурная конструкция в сфере проектирования программного обеспечения, предлагающая решение проблемы проектирования в рамках некоторого часто возникающего контекста. Такое редко встречается при анализе вредоносного кода и сигнализирует о том, что программист, который писал бэкдор, имеет хорошую квалификацию. Бэкдор использует шаблонный метод — это поведенческий паттерн проектирования, который определяет скелет алгоритма, перекладывая ответственность за некоторые его шаги на подклассы. Паттерн позволяет подклассам переопределять шаги алгоритма, не меняя его общей структуры.
Этот паттерн бэкдор применяет для создания подклассов команд, которые наследуются от базового класса и переопределяют его методы и поля.
Для хранения конфигурации, помимо шифрования, бэкдор использует кастомную сериализацию. Это необходимо для обеспечения гибкости, то есть поля могут иметь несколько значений в одном токене.
Так, например, если токен контейнера является отрицательным, то тогда в значении он имеет другой контейнер, который, в свою очередь, также может быть лишь частью последовательной вложенности контейнеров или однозначно определять значение токена, например хранить строку.
Буферный кэш — это структура данных, предназначенная для временного хранения данных, чтобы ускорить доступ к ним. Бэкдор использует кэширование для уменьшения времени доступа к часто используемым данным, минимизации задержек и повышения общей производительности программы.
В начале бэкдор десериализирует конфигурацию и получает оттуда имя, которое должно быть у него. Если оно отличается, то выполнение прекращается. В случае, если имена совпадают, бэкдор продолжает инициализацию и вызывает функцию для получения информации о компьютере жертвы, собирая ее в структуру VictimInfo такого вида:
struct struct_VictimInfo
{
DWORD magic;
struct_VictimData VictimData;
};
struct struct_VictimData
{
GUID guid;
BYTE pbSecret[16];
BYTE UserNameW[64];
BYTE hostname[32];
BYTE disks[32];
BYTE h_addrs[20];
DWORD KeyboardLayout;
BYTE dwOemId;
BYTE val_64;
BYTE dwMajorVersion;
BYTE dwMinorVersion;
BYTE Authority;
BYTE FileNameW[64];
BYTE AdaptersAddresses[6];
};
Поля структуры VictimInfo имеют такие назначения:
Member | Purpose | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
magic | Магическое число 0xB0B1B201 | ||||||||||||||||||||||||||||||
VictimData |
|
После заполнения структуры VictimInfo бэкдор создает и запускает в разных потоках на исполнение эти экземпляры классов:
Экземпляр класса CommHTTP в своем потоке парсит десериализованную конфигурацию, которую будет использовать для коммуникации с C2. Генерирует сессионный ключ для AES-128-CBC (при этом вектор инициализации равен нулю), импортирует публичный RSA-ключ и входит в цикл общения с C2, в котором реализовано:
Экземпляр класса BgJobFileCapture в своем потоке мониторит файловую систему, в цикле, перебирает все подключенные диски и рекурсивно ищет хранящиеся на дисках файлы с расширениями .doc, .xls, .ppt, .rtf, .pdf. Он также хранит результат работы в карте с ключом (имя файла) и значением (структура, содержащая информацию о файле, в том числе его содержимое).
Экземпляр класса BgJobKeylogger в своем потоке перехватывает нажатия клавиш и хранит в двусторонней очереди, а данные из буфера обмена находятся в неупорядоченной карте.
Конфигурация хранится в зашифрованном виде в секции .data. Дешифрование осуществляется однобайтовым ключом для обычной операции Xor.
Сама же дешифрованная и десериализованная структура конфигурации выглядит так:
struct struct_Config
{
DWORD sleep_time;
DWORD size;
std::wstring UserAgent;
std::wstring wstr_x86;
std::wstring wstr_x64;
std::vector<std::wstring> C2;
QWORD *public_key;
QWORD public_key_len;
struct_Commands Commands;
struct_TaskResults TaskResults;
};
struct struct_Commands
{
std::wstring Uri;
std::vector<std::string> Headers;
struct_CommandsResponse CommandsResponse;
struct_CommandsHeaders CommandsHeaders;
QWORD HelloMessage;
QWORD HelloMessageLen;
};
struct struct_CommandsResponse
{
std::string TagOpen;
std::string Encoder;
std::string TagClose;
};
struct struct_CommandsHeaders
{
std::string Header;
std::string TagOpen;
std::string Encoder;
std::string TagClose;
};
struct struct_TaskResults
{
std::wstring Uri;
std::vector<std::string> Headers;
struct_TaskResultsData TaskResultsData;
struct_TaskResultsHeaders TaskResultsHeaders;
};
struct struct_TaskResultsData
{
std::string TagOpen;
std::string Encoder;
std::string TagClose;
};
struct struct_TaskResultsHeaders
{
std::string Header;
std::string TagOpen;
std::string Encoder;
std::string TagClose;
};
Поля структуры Config имеют такие назначения:
Member | Purpose | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
sleep_time | Время ожидания для экземпляра класса CommHTTP в цикле общения с C2 | ||||||||||||||||||||||||||||||||
size | Размер используемого буферного кэша | ||||||||||||||||||||||||||||||||
UserAgent | User-Agent, используемый при коммуникации с C2 | ||||||||||||||||||||||||||||||||
wstr_x86 | Широкая строка x86 не используется | ||||||||||||||||||||||||||||||||
wstr_x64 | Широкая строка x64 не используется | ||||||||||||||||||||||||||||||||
C2 | Адреса C2 | ||||||||||||||||||||||||||||||||
public_key | Публичный ключ для шифрования информации о жертве и сессионном ключе | ||||||||||||||||||||||||||||||||
public_key_len | Длина публичного ключа | ||||||||||||||||||||||||||||||||
Commands | Структура, используемая для получения команд
| ||||||||||||||||||||||||||||||||
TaskResults | Структура, используемая для отправки результатов работы команд
|
Все общение с C2 осуществляется экземпляром класса CommHTTP с помощью вызовов сетевых функций библиотеки WININET.DLL. Информация о компьютере жертвы и сессионном ключе шифруется публичным ключом RSA, кодируется с помощью Base64 и отправляется C2 в заголовке Config.Commands.CommandsHeaders с приветственной строкой Config.Commands.HelloMessage в данных запроса. Команды, полученные от C2 в ответе, заключены между маркерами Config.Commands.CommandsResponse.TagOpen и Config.Commands.CommandsResponse.TagClose и закодированы с помощью Base64. Результат работы задач шифруется сессионным ключом AES-128-CBC, кодируется с помощью Base64 и заключается между маркерами Config.TaskResults.TaskResultsData.TagOpen и Config.TaskResults.TaskResultsData.TagClose в данных запроса к C2.
Так, например, ниже демонстрируется запрос бэкдора к C2 для получения команд. В качестве приветствия в данных используется строка mid=76&mod=TRINP, также можно заметить, что заголовок User-Agent не отображает корректно содержимое, полученное из Config.UserAgent. Это связано с ошибкой передачи значения заголовка в функцию InternetOpenW. Дело в том, что InternetOpenW пытается конвертировать строку для User-Agent из широкой кодировки в кодировку ascii, но из-за неправильно переданного указателя на значения из конфигурации происходит неправильная конвертация, и на выходе формируется неотображаемая строка.
Как уже упоминалось ранее, команды вызываются не за счет вызова конкретной функции, а за счет создания экземпляров классов и добавления их в обертку умного указателя, который, в свою очередь, будет добавлен в двойную очередь на исполнение, а после будет получен оттуда и вызван в цикле основного потока. Описание команд представлено в таблице ниже.
ID | Command | Description |
---|---|---|
0x1213C7 | Inject | Инъекция кода в процесс |
0xF17E09 | WriteFile | Запись в файл |
0xF17ED0 | ReadFile | Чтение из файла |
0xC033A4D | Cmd | Выполнение команды с помощью cmd.exe |
0x6E17A585 | GetRunningTasks | Получение текущих запущенных команд |
0xECEC | Exec | Обратный шелл |
0xCD | Cd | Смена директории |
0x108 | JobConf | Добавление команды как фоновой |
0xD1E | Die | Завершение работы бэкдора |
0x6177 | KillTask | Завершение работающей команды |
0xC04F | SetCommConfValue | Обновление конфигурации |
Группировка TaxOff применяет актуальные темы в качестве приманок для привлечения внимания и обмана пользователей. В своих атаках эти киберпреступники используют сложный многопоточный бэкдор Trinper, который позволяет им устанавливать устойчивый доступ к скомпрометированным системам, эффективно управлять несколькими задачами одновременно и выполнять различные вредоносные действия без значительного влияния на производительность системы. Многопоточность обеспечивает высокую степень параллелизма, позволяя бэкдору оставаться скрытым и эффективным, собирая данные, выполняя эксфильтрацию информации, устанавливая дополнительные модули и поддерживая связи с С2. Таким образом, сочетание использования актуальных тем и сложного многопоточного бэкдора делает атаки группировки TaxOff особенно опасными и трудными для обнаружения и предотвращения. Это подчеркивает необходимость постоянного повышения осведомленности пользователей о киберугрозах и внедрения многоуровневых мер безопасности для защиты от таких сложных атак.
ФАЙЛ | MD5 | SHA-1 | SHA-256 |
---|---|---|---|
Материалы.img | fdeb5b2771785dc412227904127e1cae | 6e7bf3ef4e53efea9a7b0446f498545e8dc517dc | dd3a609b7beb35fb2527e7ca1450ad40569b3ffbf67d84811fcf8ff09096d823 |
История поисков.html | e4da6bd811eb3b5adc4ec29fa859c08c | e810613df0dbb5d8634e7e5321f5b14c62ccfcf6 | 00f433c593204eaa1facb18d1a0dec4caee06915bbc8a51ad6bf47bf9e865fe8 |
BK_new2.2.EXE | 7815db832ef5124935d9b53445a72f49 | d45c3392011070e7e827dd3f8d6797725384b1b3 | f699c309f0d2547a85f6623dc74cc452a1471cd77af2360116447244043ee0dd |
DCIM.lnk | 468f4b71eac65391d3d59466e21ec379 | 9a083844696dd8ccce9a6f11d3a9f1227ea639ba | 93b07ba651fb6dbebaaadb39cf45ddfea7af9d3943458a5630aa588080dcf335 |
Trinper | |||
drive.google.com | 463d8f6e597fc7c2acdb3f5a3bae37b6 | 8dfecf3417b8f2ab96a3591c93223d6802690fe3 | 2a0c6a66774cc535f51e1a12d81ba6aa346934aa542291cee0c57f3bc9373a8e |
PhotoScreenSaver.scr | 19354fc1fb24d2eb08de0d46d464b16b | 62e27a7e392a48d6cf14040c6fe59dabb8df44a7 | 6d4fac9e4c36face9e0d0a7fdec1cc1403b3188ecf5c24f1ac6c32981f9c72b2 |
SearchApps.exe | 62739a86a227ad89fa6c57f5c2335220 | f5815561dfc63ad12f96a3e86e0f40cd39622373 | 7e82b3f1be69d34684a4aa4823ef0d5ae864db3501fae5a0c3697bcd28df5cef |
DotNet35.exe | f590d65dce86589b0e0d507cfeef9f68 | c3012a66acaea8801446ee61f8213a663eb7a76a | e93c1a0696b59a58e2444eb69ddf165eed71ad159624674a7fe6c91e9852443a |
185.158.248.91 |
193.37.215.111 |
server.1cscan.net |
usfna.global.ssl.fastly.net |
usfnb.global.ssl.fastly.net |
usfnc.global.ssl.fastly.net |
cfn.global.ssl.fastly.net |
fna.global.ssl.fastly.net |
fnb.global.ssl.fastly.net |
consult-asset-feed.global.ssl.fastly.net |
consult-vendor-free.global.ssl.fastly.net |
consult-zero-ads.global.ssl.fastly.net |
rule PTESC_apt_win_ZZ_TaxOff__Backdoor__Trinper{
strings:
$s1 = "Task"
$s2 = "TaskCd"
$s3 = "TaskCmd"
$s4 = "TaskExec"
$s5 = "TaskGetRunningTasks"
$s6 = "TaskInject"
$s7 = "TaskDie"
$s8 = "TaskJobConf"
$s9 = "TaskKillTask"
$s10 = "TaskReadFile"
$code1 = {E8 ?? ?? ?? ?? 44 38 60 ?? 75 ?? 66 39 58 ?? 72 ?? 48 8B 40 ?? 8B 08 EB ??}
$code2 = {48 89 4C 24 ?? 48 8B 44 24 ?? 0F B6 40 ?? 85 C0 75 ?? 48 8B 44 24 ?? 0F B7 40 ?? 83 F8 ?? 7D ?? 33 C0 EB ?? 48 8B 44 24 ?? 48 8B 40 ?? 8B 00 C3}
condition:
((uint16(0) == 0x5a4d) and (all of($s*)) and (any of($code*)))
}
Initial Access | ||
T1566.002 | Phishing: Spearphishing Link | Группировка TaxOff использовала фишинговые письма с ссылкой на вредоносные файлы |
Execution | ||
T1204.002 | User Execution: Malicious File | Группировка TaxOff использовала файлы приманки, чтобы запустить бэкдор Trinper |
Defense Evasion | ||
T1055.012 | Process Injection: Process Hollowing | Группировка TaxOff использовала бэкдор Trinper для инъекций кода в процессы |
Credential Access | ||
T1187 | Forced Authentication | Группировка TaxOff использовала фальшивую форму авторизации |
T1056.001 | Input Capture: Keylogging | Группировка TaxOff использовала бэкдор Trinper для перехвата нажатия клавиш |
Discovery | ||
T1083 | File and Directory Discovery | Группировка TaxOff использовала бэкдор Trinper для сбора информации о файловой системе |
Collection | ||
T1115 | Clipboard Data | Группировка TaxOff использовала бэкдор Trinper для получения содержимого буфера обмена |
T1056.001 | Input Capture: Keylogging | Группировка TaxOff использовала бэкдор Trinper для перехвата нажатия клавиш |
Command And Control | ||
T1071 | Application Layer Protocol | Группировка TaxOff использовала http (https) для связи бэкдора Trinper с C2 |
T1132.001 | Data Encoding: Standard Encoding | Группировка TaxOff использовала бэкдор Trinper для кодирования полученной информации в Base64 |
T1573.001 | Encrypted Channel: Symmetric Cryptography | Группировка TaxOff использовала бэкдор Trinper для шифрования отправляемой информации с помощью AES-256 |
T1573.002 | Encrypted Channel: Asymmetric Cryptography: | Группировка TaxOff использовала бэкдор Trinper для шифрования отправляемой информации с помощью RSA |
T1090.004 | Proxy: Domain Fronting | Группировка TaxOff использовала фронтирование домена для связи с бэкдором Trinper |
Exfiltration | ||
T1020 | Automated Exfiltration | Группировка TaxOff использовала бэкдор Trinper для автоматической эксфильтрации результатов работы полученных от исполнения команд |
T1041 | Exfiltration Over C2 Channel | Группировка TaxOff использовала бэкдор Trinper для эксфильтрации данных на C2 |
apt_win_ZZ_TaxOff__Backdoor__Trinper |
Suspicious_Connection |
RunAs_System_or_External_tools |
Run_Executable_File_without_Meta |
Suspicious_Directory_For_Process |
BACKDOOR [PTsecurity] Trinper (APT TaxOff) sid: 10012123 |
SUSPICIOUS [PTsecurity] Suspicious HTTP header Trinper (APT TaxOff) sid: 10012124, 10012125 |