Positive Technologies
PT Expert Security Center

ExCobalt: GoRed — техника скрытого туннеля

ExCobalt: GoRed — техника скрытого туннеля

Введение

В ходе реагирования на инцидент у одного из наших клиентов командой PT ESC CSIRT был обнаружен ранее неизвестный бэкдор, написанный на языке Go, который мы атрибутировали к группе ExCobalt.

ExCobalt — это киберпреступная группа, сосредоточенная на кибершпионаже и некоторые члены которой активны по крайней мере с 2016 года и, предположительно, состояли в небезызвестной группе Cobalt. Группа Cobalt атаковала кредитно-финансовые организации с целью кражи денежных средств. Отличительной особенностью группы Cobalt было использование инструмента CobInt, который и начала применять группа ExCobalt c 2022 года.

За последний год специалисты PT ESC фиксировали атаки и проводили расследования инцидентов, связанные с группой ExCobalt, в российских организациях из следующих секторов:

  • Металлургия
  • Телекоммуникации
  • Горная промышленность
  • Информационные технологии
  • Государственные учреждения
  • Разработка ПО


В данной статье мы расскажем о новом инструменте группы — GoRed, его эволюции, а также о некоторых тактиках, техниках и процедурах, которые использовала группа в своих атаках.

Начало исследования

В ходе расследования одного из инцидентов, который был зафиксирован в марте 2024 года на одном из Linux-хостов нашего клиента, был обнаружен файл с названием scrond, накрытый UPX (Ultimate Packer for eXecutables).

В данных этого распакованного сэмпла, который написан на языке Go, были найдены пути пакетов, содержащие подстроку red.team/go-red/. Этот факт позволяет предположить, что этот сэмпл является проприетарным инструментом с названием GoRed.

Поисследовав сайт, нам не удалось найти каких-то значимых связей с вредоносной активностью. И поэтому можно предположить, что домен red.team найденный в строках GoRed является локальным репозиторием с утилитами для тестирования на проникновение.

111.png
Рисунок 1. Внутренние пакеты

Возвращаясь к бэкдору GoRed, можно выделить следующий основной функционал:

  • Возможность подключения оператора и выполнения команд, как у других С2-фреймворков, наподобие Cobalt Strike, Sliver и так далее.
  • Использование RPC-протокола для коммуникации GoRed с С2-сервером.
  • Использование DNS-/ICMP-туннелирования, WSS и Quic для коммуникации оператора с GoRed.
  • Возможность получения учетных данных со скомпрометированных систем.
  • Сбор различной информации со скомпрометированных систем, например данных об активных процессах, имя хоста, список сетевых интерфейсов, структура файловых систем и так далее.
  • Разведка в сети жертвы с помощью различных команд.
  • Сериализация, шифрование, архивирование и отправка собранных данных на специальный сервер, предназначенный для хранения скомпрометированных данных.


Полное техническое описание GoRed см. в разделе «Анализ GoRed».

Первая версия GoRed и другие обнаруженные инструменты злоумышленников

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

Похожим образом в другом инциденте в октябре 2023 года мы обнаружили на открытых директориях инфраструктуры злоумышленников дополнительные инструменты. Краткое название и описание инструментов, найденных в ходе расследований, представлены в таблице ниже:

2586CVE-2022-2586: Уязвимость в ядре Linux, связанная с подделкой указателей в функции io_uring. Использовалась злоумышленниками для повышения привилегий и выполнения произвольного кода на уязвимой системе.
3156.zipCVE-2021-3156: Уязвимость в программе sudo, известная как Baron Samedit. Она позволяет локальному пользователю выполнить произвольный код с правами суперпользователя, обходя стандартные меры безопасности.
4034Pwnkit — эксплойт для локального повышения привилегий через эксплуатацию уязвимости CVE-2021-4034. Уязвимость в утилите pkexec. Она позволяет локальным пользователям повысить свои привилегии до root, что может привести к полной компрометации системы.
tracemeЭксплойт для локального повышения привилегий через CVE-2019-13272. Уязвимость в Linux-ядре, в компоненте ptrace. Она позволяет пользователям с привилегиями CAP_SYS_PTRACE повысить свои привилегии до root, что может использоваться для выполнения произвольного кода на системе.
bitrix.zipАрхив с vote_agent.php и html_editor_action.php с RCE-эксплойтами для битрикса. Распространялись ранее в телеграм-каналах в июне 2022 года, а также упоминались в массовом дефейсе веб-сайтов РФ в мае 2023 года https://habr.com/ru/companies/ruvds/articles/739898/
colПервая версия бэкдора GoRed — v0.0.1
fsfscan - https://github.com/shadow1ng/fscan/
get_wp-commentin.txtОбфусцированный php
runrun — запускает заданный исполняемый файл от root. Предназначен для FreeBSD. Аналог sudo exec <заданный файл>
setset — назначает root владельцем файла и дает ему права rwx (чтение, запись, исполнение). Предназначен для FreeBSD. Аналог chown <файл> 0; chmod <файл> 777.
installУстановщик вредоносного модуля для Apache 2.4
install[drop]install[drop] — содержит дроппер с бинарным файлом, запакованным UPX, который записывает в /usr/local/games/w, chown root:root, chmod 4755
install[drop][upx] /usr/local/games/winstall[drop][upx] /usr/local/games/w — пытается исполнить следующую команду от root пользователя:
(setuid(0); setgid(0); /bin/sh -c ...))
kk — содержит следующий набор базовых сетевых утилит и заменяет имя процесса на [kthr]:
curlурезанный curl
socksdпрокси (socks4/5 с поддержкой user+pass)
shellbind shell
hostрезолв hostname через 8.8.8.8
hashmd5/sha1/sha256
gzinflate/deflate
netcat-
kitsh-скрипт для установки kitsune. В данном случае использовалась переменная MEGATSUNE вместо KITSUNE. В качестве С2 использовались amd64.rpm-bin.link и pkg.dpkg-source.info
lock.zipЧасть репозитория вайпера-шифровальщика locker c read.me-файлом на русском языке
lockerСам локер из lock.zip без конфигов
mMettle - https://github.com/rapid7/mettle
revbind shell, общий код с shell из k, меняет имя процесса на [kr]
sfБинарный файл под BSD
sparkSpark RAT
w.txtWSO php-вебшелл —
https://github.com/ndbrain/WSO
wefВариация клиентской части Reverse SSH https://github.com/NHAS/reverse_ssh
y.txtphp p0wny-shell
knife/kКак и в случае с k, был найден с именем k на открытой директории, но на другом домене спустя несколько месяцев. Функционал является схожим, что позволяет говорить, что это является просто следующей версией. Является инструментом для управления скомпрометированным Linux-сервером. Имеет разнородный функционал, команды для выполнения задаются в командной строке. Обладает следующим функционалом:
syncОбработчика команды нет, происходит segfault при ее вызове
curl <url>Загружает содержимое с заданного url
socksdЗапускает socks-сервер
netcatОбработчика команды нет, происходит segfault при ее вызове
host <host>Пытается разрешить имя заданного хоста через dns-сервер 8.8.8.8:53
hash <имя файла>Вычисляет хеш заданного файла (доступны md5, sha1, sha256)
gz <имя файла>Архивирует заданный файл
shell <опции> <ip-адрес:порт>Удаленный шелл. Команда запускается со следующими возможными опциями: -l — слушать заданный адрес, -c — самостоятельно осуществить соединение к заданному адресу

Помимо инструментов, полученных с директорий серверов злоумышленников, группа ExCobalt использовала следующие инструменты: 

  • Mimikatz
  • procdump
  • SMBExec
  • Metasploit
  • rsocx

Модифицированные версии стандартных утилит

Помимо вышеприведенных инструментов, обнаруженных на открытых директориях, в нескольких инцидентах мы также встречали модифицированные версии стандартных утилит Linux, которые впервые мы обнаружили в ноябре 2023 года.

Назначение модифицированных утилит можно разделить на две категории:

1. ps и htop — для сокрытия активного процесса core модуля gsocket, а также связанных с ним процессов из вывода терминала.

В качестве примера возьмем ps.

Модификация кода была произведена в функции simple_readproc.

Оригинальный код выглядит следующим образом:

2.png
Рисунок 2. Оригинальный код утилиты ps

Был добавлен следующий код:

3.png
Рисунок 3. Добавленный код в утилиту ps

В функции is_target_proc происходит следующая проверка на равенство имени с зашитыми именами вредоносных процессов:

4.png
Рисунок 4. Проверка в функции is_target_proc

И если проверка проходит, то вредоносный процесс с именами, представленными на рисунке ниже, не будет отображен в терминале.

5.png
Рисунок 5. Имена вредоносных процессов

2. ss и netstat — для сокрытия активного сетевого соединения core модуля gsocket из вывода терминала.

В качестве примера возьмем netstat.

Первая модификация кода была произведена в функции main.

Оригинальный код выглядит следующим образом:

6.png
Рисунок 6. Оригинальный код утилиты netstat функции main

Для начала был добавлен код, который считывает все C2 из секции данных в глобальную структуру:

7.png
Рисунок 7. Считывание С2 в глобальную структуру

Вторая модификация в функции tcp_do_one, начиная отсюда:

8.png
Рисунок 8. Оригинальный код утилиты netstat функции tcp_do_one

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

9.png
Рисунок 9. Проверки на имя вредоносного процесса и вредоносного соединения

Связь с ExCobalt

В ноябре 2023 года мы рассказывали об атаках ExCobalt на российские компании.

В вышеупомянутом репорте мы упоминали домен lib.rpm-bin.link, при энумерации директорий которого было получено множество инструментов, описанных выше, в том числе col — первая версия GoRed.

Помимо этого, в одном из инцидентов, который произошел в марте 2024 года, мы наблюдали активность зараженных хостов, которые связывались с серверами злоумышленников, — get.rpm-bin.link и leo.rpm-bin.link. Помимо этого, GoRed использовал static_TransportConfig, в котором были следующие C2:

  • leo.rpm-bin.link
  • sula.rpm-bin.link
  • lib.rest
  • rosm.pro


В мае 2023 года исследователи из Bi.Zone выпускали разбор атак Sneaking Leprechaun и их инструментарий пересекается с найденными на открытых директориях файлами, которые описаны выше.

Также коллеги из Ростелеком Солар выпустили в мае 2024 года исследование про кластер активности Shedding Zmiy, которое тоже соотносится с группой ExCobalt. В кейсе 7 этого исследования описывается та же атака и сэмпл стиллера GoRed с С2 — pkg.collect.net.in, который они обозначили как Bulldog Backdoor.

Анализ GoRed

Прежде чем перейти к анализу актуальной версии бэкдора GoRed, приведем ретроспективный анализ эволюции бэкдора.

Найденные версии

Все найденные нами версии приведены в таблице ниже.

Версия

Описание

v0.0.1
  • Считается первой.
  • Собирает информацию о жертве.
  • Исходники обфусцированы утилитой garble.
0.0.9
  • Отладочная сборка (включено логирование действий GoRed).
  • Расширен встроенный конфиг.
  • Собирает информацию о жертве.
  • Исходники обфусцированы утилитой garble.
v0.0.9
  • Расширен встроенный конфиг.
  • Собирает информацию о жертве.
  • Исходники обфусцированы утилитой garble.
v0.0.13
  • Удалена обфускация с помощью утилиты garble.
  • Добавлен поток управления через command line.
  • Добавлен функционал reverse shell (через протоколы wss, dns).
  • Собирает только процессы.
  • В отличие от предыдущих версий добавлена структура конфига.
v0.0.23-10-g4528ef3
  • Добавлен сбор сетевых интерфейсов.
  • Добавлена функциональность работы в режиме маяка (команда gecko).
  • Добавлены протоколы для подключения оператора через dns, icmp, quic, wss.
  • Добавлен кодек cbor для работы через протокол RPC.
  • Добавлен режим прокси.
  • Добавлена возможность обновления GoRed.
  • Добавлена возможность мониторинга файловой системы (команда birdwatch).
  • Добавлено icmp-туннелирование.
v0.1.3-4-g68с293dДанная версия упоминается только в докладе Солар, мы ее не встречали.
v0.1.3-62-g4843e53
  • Расширена функция сбора данных жертвы (команда collector).
  • Добавлен транспортный конфиг.
v0.1.4Изменений, в сравнении с предыдущей версией, — не обнаружено.

В нашей статье мы проведем разбор актуальной версии v0.1.4.

Внутренние пакеты

Сначала опишем структуру внутренних пакетов и их назначение для понимания функциональных возможностей бэкдора GoRed, в данных которого были найдены пути пакетов, содержащие подстроку red.team/go-red/.

Пакет

Назначение

red.team/go-red/config/Получение внутреннего и транспортного конфигов.
red.team/go-red/bb/Обработка команд оператора.
red.team/go-red/birdwatch/Мониторинг файловой системы.
red.team/go-red/gecko/Протокол общения GoRed c C2.
red.team/go-red/backend/Установка соединения с сервером для эксфильтрованных данных.
red.team/go-red/collector/Сбор информации о системе.
red.team/go-red/util/Различные вспомогательные утилиты.
red.team/go-red/packer/Упаковка данных.
red.team/go-red/proxy/Работа в режиме прокси.
red.team/go-red/revshell/Работа в режиме обратного шелла.
red.team/go-red/dns/Реализация dns-туннелирования.
red.team/go-red/icmptunnel/Реализация icmp-туннелирования.

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

control_flow_ru.png
Рисунок 10. Схема потока управления

1 — Начало выполнения

Поток управления базируется на command line (далее cli). Но, прежде чем передать управление cli, будут проинициализированы несколько команд, описанных далее:

  • команда service.
  • подкоманда gecko.
11.png
Рисунок 11. Инициализация команды и подкоманды

Первой будет проинициализирована команда service, которая осуществляет закрепление в системе. Структура команды GoRed для cli выглядит следующим образом:

struct cli_Command
{
    string Name;
    _slice_string Aliases;
    string Usage;
    string UsageText;
    string Description;
    string ArgsUsage;
    string Category;
    PTR_cli_BashCompleteFunc BashComplete;
    PTR_cli_BeforeFunc Before;
    PTR_cli_AfterFunc After;
    PTR_cli_ActionFunc Action;
    PTR_cli_OnUsageErrorFunc OnUsageError;
    _slice__ptr_cli_Command Subcommands;
    _slice_cli_Flag Flags;
    cli_FlagCategories flagCategories;
    bool SkipFlagParsing;
    bool HideHelp;
    bool HideHelpCommand;
    bool Hidden;
    bool UseShortOptionHandling;
    string HelpName;
    _slice_string commandNamePath;
    string CustomHelpTemplate;
    cli_CommandCategories categories;
    bool isRoot;
    cli_separatorSpec separator;
};


С точки зрения идентификации выполняемых команд самые интересные поля данной структуры следующие:

  • Name — имя команды.
  • Usage — описание команды.
  • Action — функция, которая будет выполнена при вызове команды.
  • Subcommands — подкоманды для текущей команды.


Далее будет проинициализирована структура самой cli в переменную app.

12.png
Рисунок 12. Инициализация структуры app для cli

Структура app выглядит следующим образом:

struct cli_App
{
    string Name;
    string HelpName;
    string Usage;
    string UsageText;
    string ArgsUsage;
    string Version;
    string Description;
    string DefaultCommand;
    _slice__ptr_cli_Command Commands;
    _slice_cli_Flag Flags;
    bool EnableBashCompletion;
    bool HideHelp;
    bool HideHelpCommand;
    bool HideVersion;
    cli_CommandCategories categories;
    cli_FlagCategories flagCategories;
    PTR_cli_BashCompleteFunc BashComplete;
    PTR_cli_BeforeFunc Before;
    PTR_cli_AfterFunc After;
    PTR_cli_ActionFunc Action;
    PTR_cli_CommandNotFoundFunc CommandNotFound;
    PTR_cli_OnUsageErrorFunc OnUsageError;
    PTR_cli_InvalidFlagAccessFunc InvalidFlagAccessHandler;
    time_Time Compiled;
    _slice__ptr_cli_Author Authors;
    string Copyright;
    io_Reader Reader;
    io_Writer Writer;
    io_Writer ErrWriter;
    PTR_cli_ExitErrHandlerFunc ExitErrHandler;
    map_string_interface_ Metadata;
    PTR_func_map_string_string ExtraInfo;
    string CustomAppHelpTemplate;
    string SliceFlagSeparator;
    bool DisableSliceFlagSeparator;
    bool UseShortOptionHandling;
    bool Suggest;
    bool AllowExtFlags;
    bool SkipFlagParsing;
    bool didSetup;
    cli_separatorSpec separator;
    _ptr_cli_Command rootCommand;
};


И здесь также самыми информативными для идентификации команд полями являются следующие:

  • Name — имя текущей команды.
  • Action — функция, которая будет выполнена.
  • Commands — подкоманды текущей команды.


А также будет инициализировано поле Commands для структуры app.

13.png
Рисунок 13. Инициализация структуры контекста для cli

После идет получение поля Logging из структуры embedded_Config, которая будет описана в разделе ниже. После этого поток управления переходит к cli.

14.png
Рисунок 14. Передача управления cli

2 — Закрепление в системе

Первая команда, которая будет выполняться, — service. Она осуществляет закрепление в системе. В качестве параметров может принимать следующие значения:

Параметр

Назначение

no-serviceПросто переходит к выполнению команд cli.
uninstallУдаление сервиса.
restartПерезапуск сервиса.

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

  • BB_WS
  • BB_QUIC
  • BB_ICMP
  • BB_DNS
  • BB_START_DELAY

3 — Инициализация режима маяка

После поток управления будет передан команде gecko. Данная команда является входной точкой для работы GoRed в режиме маяка.

15.png
Рисунок 15. Режимы работы

В качестве параметров может принимать следующие значения:

Параметр

Назначение

wssИспользование в качестве протокола коммуникации оператора с GoRed.
quic
icmp
dns
backgroundБудет ли команда добавлена как фоновая.
start-delayЗадержка при коммуникации с C2.

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

16.png
Рисунок 16. Инициализация функционала режима маяка

Для идентификации жертвы сначала генерирует ее ID, который является хеш-суммой MD5 от MAC-адресов и имени компьютера (как в случае с Hellhounds: операция Lahat). Полученная хеш-сумма будет добавлена в одно из полей структуры client, в которой хранятся все нужные данные для общения с С2.

17.png
Рисунок 17. Получение ID жертвы

4 — Установка первоначального соединения

После инициализации GoRed необходимо установить соединение с C2. Количество попыток для соединения реализуется за счет пакета backoff.

18.png
Рисунок 18. Установка количества попыток для подключения к С2

Поток исполнения стартует функцию для регистрации функционала режима маяка, а после идет инициализация команд для cli.

19.png
Рисунок 19. Запускает функционал режима маяка

Для регистрации используется протокол RPC, данные из структуры model_Beacon будут отправлены на сервер, для аутентификации на котором будут использованы данные из структуры model_Auth.

struct model_RegisterBeaconRequest
{
    model_Beacon Beacon;
    model_Auth Auth;
};

struct model_Beacon
{
    string ID;
    string Hostname;
    string ClientIPs;
    _slice_string Tags;
    string OS;
    string Username;
};

struct model_Auth
{
    string Token;
    uuid_UUID ClientID;
    string Transport;
};


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

Поле

Назначение

BeaconСтруктура, содержащая информацию о жертве.

Поле

Назначение

IDID жертвы.
HostnameИмя хоста жертвы.
ClientIPsIP жертвы.
TagsТег жертвы.
OSОперационная система жертвы.
UsernameИмя пользователя жертвы.
AuthСтруктура, содержащая данные для аутентификации.

Поле

Назначение

TokenТокен для аутентификации.
ClientIDID клиента.
TransportИспользуемый протокол.

После регистрации GoRed запускает команду для мониторинга файловой системы — birdwatch.

20.png
Рисунок 20. Запуск фоновой команды birdwatch

Затем поток исполнения устанавливает период отстука GoRed в режиме маяка к C2.

21.png
Рисунок 21. Установка периода heartbeat

После поток исполнения запускает команду для мониторинга файла паролей, хранящихся в /etc/shadow/.

22.png
Рисунок 22. Запуск фоновой команды creds-watcher

И, в конце концов, поток исполнения инициализирует все доступные команды и входит в режим heartbeat.

23.png
Рисунок 23. Запускает heartbeat

5 — Режим получения и исполнения команд

На финальном этапе инициализации GoRed входит в режим прослушивания команд от оператора, которые он инициализировал ранее. Может выполнять как системные команды, так и встроенные. Также присутствует функция установки команды как фоновой.

24.png
Рисунок 24. Функция установки команды как фоновой

6 — Общение в режиме маяка

Общение GoRed в режиме маяка с C2 осуществляется через RPC-протокол.

25.png
Рисунок 25. Функционал RPC

Для общения по RPC-протоколу он регистрирует свой кодек.

26.png
Рисунок 26. Функционал собственного кодека для RPC

В зарегистрированном кодеке при отправке происходит сериализация данных через cbor и шифрование через AES-256-GCM (поле Secret в embedded_Config), а при получении происходит обратный процесс.

27.png
Рисунок 27. Функция преобразования данных для эксфильтрации

Конфиги

Внутри GoRed содержится 2 блока конфигураций — встроенная и транспортная.

Встроенная конфигурация

Является конфигурацией самого GoRed. Он закодирован base64, также сериализирован msgpack.

28.png
Рисунок 28. Встроенный конфиг

Для версий v0.0.23-10-g4528ef3 — v0.1.4 структура встроенной конфигурации следующая:

struct embedded_Config
{
	string Logging;
	string Token;
	uuid_UUID UserID;
	uuid_UUID ClientID;
	string ClientKey;
	string Version;
	_slice_string Tags;
	_slice_string Args;
	string Secret;
	_ptr_url_URL BackendAddress;
	_ptr_url_URL ProxyAddress;
};


Назначения полей встроенной конфигурации следующие.

Поле

Назначение

Logging

Осуществляется ли логирование действий и в каком формате.

  • Не осуществляется.
  • В файл .log, который находится во временной директории.
  • Напрямую в консоли для оператора.
TokenСгенерированный JWT для RPC.
UserIDUUID для JWT в поле payload. При использовании RPC.
ClientIDУникальный идентификатор для эксфильтрованных данных жертвы.
ClientKeyКлюч, который нужен для генерации JWT (HS256) при эксфильтрации данных.
VersionВерсия GoRed.
TagsТег жертвы.
ArgsАргументы GoRed.
SecretКлюч для шифрования или дешифрования AES256-GCM, передаваемых или получаемых данных через RPC.
BackendAddressАдрес отдельного сервера для эксфильтрации данных.
ProxyAddressСписок адресов прокси при эксфильтрации данных.

Структура конфигурации для версий 0.0.9 — v0.0.13.

struct embedded_Config
{
    bool Debug;
    string Token;
    uuid_UUID ClientID;
    string ClientKey;
    string Version;
    _slice_string Tags;
    string Secret;
    string BasicAuthLogin;
    string BasicAuthPass;
    _ptr_url_URL BackendAddress;
    _ptr_url_URL ProxyAddress;
};


Назначения полей этой версии встроенной конфигурации следующие.

Поле

Назначение

Debug

Осуществляется ли логирование действий и в каком формате.

  • Не осуществляется.
  • В файл .log, который находится во временной директории.
  • Напрямую в консоли оператора.
BasicAuthLoginЛогин, используемый для аутентификации при использовании команды curl.
BasicAuthPassПароль, используемый для аутентификации при использовании команды curl.

Встроенная конфигурация для версии v0.0.1.

struct embedded_Config
{
    bool Debug;
    uuid_UUID ClientID;
    string ClientKey;
    string Version;
    _ptr_url_URL BackendAddress;
};


Скрипт для получения полей встроенной конфигурации, на языке Go.

package main

import (
    "encoding/base64"
    "fmt"
    "net/url"

    "github.com/google/uuid"
    "github.com/vmihailenco/msgpack/v5"
)

type embedded_Config struct {
    Logging        string
    Token          string
    UserID         uuid.UUID
    ClientID       uuid.UUID
    ClientKey      string
    Version        string
    Tags           []string
    Args           []string
    Secret         string
    BackendAddress* url.URL
    ProxyAddress* url.URL
}

const config = `...`

func main() {
    var item map[string]any
    data, _ : = base64.StdEncoding.DecodeString(config)
    err : = msgpack.Unmarshal(data, &item)
    if err != nil{
    	panic(err)
    }
    fmt.Print("Logging: ")
    fmt.Println(item["Logging"])
    fmt.Print("Token: ")
    fmt.Println(item["Token"])
    fmt.Print("UserID: ")
    fmt.Println(uuid.UUID(item["UserID"].([]byte)))
    fmt.Print("ClientID: ")
    fmt.Println(uuid.UUID(item["ClientID"].([]byte)))
    fmt.Print("ClientKey: ")
    fmt.Println(item["ClientKey"])
    fmt.Print("Version: ")
    fmt.Println(item["Version"])
    fmt.Print("Tags: ")
    fmt.Println(item["Tags"])
    fmt.Print("Args: ")
    fmt.Println(item["Args"])
    fmt.Print("Secret: ")
    fmt.Println(item["Secret"])
    fmt.Print("BackendAddress: ")
    fmt.Println(string(item["BackendAddress"].([]byte)))
    fmt.Print("ProxyAddress: ")
    fmt.Println(item["ProxyAddress"])
}


Скрипт для получения полей встроенной конфигурации, на языке Python.
 

import msgpack

import base64

s = base64.b64decode('...')

config = msgpack.unpackb(s, raw = False)

print(config)

Транспортная конфигурация

Транспортная конфигурация имеет следующий вид.

29.png
Рисунок 29. Транспортный конфиг

Структура транспортной конфигурации следующая.

struct static_TransportConfig
{
	static_Transport Revsh;
	static_Transport RPC;
	static_Transport Proxy;
};

struct static_Transport
{
	_ptr_static_Address WS;
	_ptr_static_Address QUIC;
	_ptr_static_Address ICMP;
	_ptr_static_Address DNS;
	_ptr_static_Address TCP;
};

struct static_Address
{
	string Domain;
	string BackupIP;
	signed __int64 Port;
	string Proto;
};


Поля транспортной конфигурации следующие.

Поле

Назначение

RevshАдреса для подключения оператора по обратному шеллу.
RPCАдреса для отстука GoRed в режиме маяка через RPC.
ProxyАдреса для использования GoRed в режиме прокси.

Поля транспортной конфигурации следующие.

Поле

Назначение

DomainДомен для подключения.
BackupIPIP для подключения, в случае если невозможно зарезолвить домен.
PortПорт для подключения.
ProtoПротокол подключения.

Протоколы общения

У GoRed есть несколько протоколов для общения с оператором.

Протокол

Реализация

wsРеализует подключение через протокол websockets.
quicРеализует подключение через протокол quic.
icmpРеализует подключение через icmp-туннелирование.
dnsРеализует подключение через dns-туннелирование.

DNS

DNS-туннелирование в GoRed может быть как через base64, так и через base32. Данная опция выбирается на этапе компилирования.

30.png
Рисунок 30. Туннелирование трафика через base32

Пример домена, который использовался в атаке, 8E1A4QB4OGA66RPJCHL72DJGCKRMIOR8CDN3EDJBDOOAEQ3FEDQ5UQB4OGA66RP.JCHL6EDJGCKRMIOR8CDN3EDJBDLJG.rosm[.]pro.

Фоновые команды

Фоновые команды — это команды, которые работают постоянно, некоторые из них можно добавлять или убирать из фоновых, в зависимости от условий в таблице ниже.

Команда

Описание

birdwatch

Команда для отслеживания новых файлов в каталогах. Фоновая по умолчанию.

  • list — подкоманда для вывода списка путей, которые отслеживаются на наличие новых файлов.
  • unwatch — подкоманда для прекращения мониторинга путей для новых файлов.
creds-watcherКоманда для отслеживания паролей. Фоновая по умолчанию.
revsh-host

Команда для работы в режиме обратного шелла. Становится фоновой при ее выполнении.

  • Использование websockets в качестве протокола коммуникации оператора с GoRed.
  • Использование quic в качестве протокола коммуникации оператора с GoRed.
  • Использование icmp в качестве протокола коммуникации оператора с GoRed.
  • Использование dns в качестве протокола коммуникации оператора с GoRed.
rev-proxyКоманда для работы в режиме обратного прокси через SOCKS5. Становится фоновой при ее выполнении.
rev-fwdКоманда для работы в режиме обратной переадресации портов. Становится фоновой при ее выполнении.

Установка соединения в режиме rev-proxy и rev-fwd

Прежде чем выступать в роли сервера, GoRed должен проинициализировать вшитый сертификат в формате X.509 (как в случае с Hellhounds: Операция Lahat. Часть 2).

31.png
Рисунок 31. Получение сертификата и информации о хосте

Также необходимо собрать информацию о хосте жертвы, выполнив функцию CollectHostInfo с рисунка выше. Тем самым получив структуру (за исключением поля Addr), представленную ниже.

struct proto_HostInfo
{
    string Addr;
    string OS;
    string Username;
    string Hostname;
    _slice_string IPs;
};


Поля структуры имеют следующие назначения.

Поле

Назначение

AddrЦелевой адрес для подключения, который передается в качестве параметра для команды.
OSОперационная система жертвы.
UsernameИмя пользователя жертвы.
HostnameИмя хоста жертвы.
IPsIP-адреса жертвы.

Инициализация структуры для идентификации GoRed, который выступает в роли сервера.

32.png
Рисунок 32. Инициализация структуры

Проинициализированная структура выглядит следующим образом.

struct proto_BinInfo
{
    uuid_UUID ClientID;
    string Token;
    _slice_string Tags;
};


Поля структуры имеют следующие назначения.

Поле

Назначение

ClientIDДля идентификации жертвы.
TokenСгенерированный JWT для RPC.
TagsТег жертвы.

Получив структуры proto_HostInfo и proto_BinInfo, которые используются в сообщении «приветствия», отправляемого GoRed на C2, адрес которого он получил из транспортного конфига. Структура «приветствия» выглядит следующим образом.

struct proto_MsgGreeting
{
    proto_ConnectionMode Mode;
    string MachineID;
    proto_BinInfo BinInfo;
    _ptr_proto_HostInfo HostInfo;
};


Поля структуры имеют следующие назначения.

Поле

Назначение

Mode

Режимы работы могут быть следующие:

  • 2 — режим rev-proxy.
  • 3 — режим rev-fwd.
MachineIDID компьютера жертвы.
BinInfoСтруктура, содержащая информацию о конфиге GoRed.
HostInfoСтруктура, содержащая информацию о жертве.

Схема отправки приветствия для регистрации в качестве сервера выглядит следующим образом.

33.png
Рисунок 33. Взаимодействие с С2

В ответ на «приветствие» сервер отправляет сообщение со следующей структурой.

struct proto_MsgGreetingResponse
{
    string Greeting;
    _ptr_proto_BinInfo BinInfo;
    _ptr_proto_HostInfo HostInfo;
};


Если поле Greeting содержит строку welcome, то соединение считается успешным и GoRed начинает работать в режиме сервера, если же нет, то соединение не может быть установлено.

Вызываемые команды

Оператор взаимодействует с GoRed посредством команд.

Команда

Описание

uploadКоманда для эксфильтрации файлов. В качестве аргумента принимает путь до файла.
downloadКоманда для инфильтрации файлов. В качестве аргумента принимает путь до файла.
bg-listКоманда для вывода списка внутренних фоновых команд.
bg-stopКоманда для отмены внутренней фоновой команды. В качестве одного из аргументов принимает ID команды. Также может остановить все фоновые команды.
stealth

Команда для автоматического установления частоты heartbeat в большем диапазоне для большей скрытности.

  • on — подкоманда для включения этого режима с временным диапазонном 0×9D29229E000-0×105EF39B2000.
  • off — подкоманда для отключения этого режима с временным диапазонном 0×12A05F200-0×45D964B800.
emit-periodКоманда для получения или задавания частоты heartbeat в ручном режиме.
conn-providersКоманда для получения доступных в данной версии GoRed протоколов коммуникации.
info

Команда для получения информации о жертве.

  • UserID
  • Logging
  • Tags
  • Version
  • Time
collectКоманда для сбора и эксфильтрации информации о системе (подробнее в разделе ниже).
bb-updateКоманда для обновления GoRed. Делает запрос GET по URL (переданного аргументом) и перезапускает GoRed с аргументом restart.
bb-psКоманда для получения статуса процесса, который был передан аргументом.
bb-catКоманда для чтения файла, который был передан аргументом.
bb-findКоманда для поиска файлов, которые были переданы аргументом.
bb-lsКоманда для отображения содержимого каталога, который был передан аргументом.
bb-mkdirКоманда для создания каталога, который был передан аргументом.
bb-pwdКоманда для получения текущей директории.
bb-rmКоманда для удаления файла, который был передан аргументом.
bb-wc

Команда для сбора информации о файле, который был передан аргументом.

  • Количество слов.
  • Количество строк.
  • Количество символов.
  • Количество байтов.
bb-nmapКоманда для сканирования сети. В качестве аргумента принимает хост.
bb-pingКоманда для ping внешнего хоста. В качестве аргумента принимает IP.
bb-wgetКоманда для получения файлов через HTTP. В качестве аргумента принимает URL и output в качестве параметра с именем файла в качестве аргумента.
bb-curlКоманда, похожая на утилиту curl, но с ограниченной функциональностью.

collect

Так как это команда для сбора информации о системе, ее данных и последующей эксфильтрации, мы решили описать ее подробнее.

Параметр

Назначение

local-archiveВыбор алгоритма сжатия. Tar или gzip.
skip-treesПропустить сбор информации о структуре файловой системы.
skip-filesПропустить сбор файлов.
exec-timeoutВремя для сбора файлов.

Собираемая информация

Пример собираемой информации представлен на рисунке ниже.

34.png
Рисунок 34. Пример собираемой информации

Полный список собираемой информации приведен в таблице ниже.

Файл

Содержимое

processes.jsonСписок процессов.
envvars.jsonСписок переменных окружения.
host.jsonИнформация о процессоре, оперативной памяти, установленной ОС, имени пользователя, имени группы.
network_interfaces.jsonСписок сетевых интерфейсов.
netstats.jsonСписок активных сетевых соединений.
*.txtФайлы, которые будут собраны в зависимости от значения полей в структуре model_CollectionConfig.
hardware.jsonИнформация о железе.
trees.jsonСтруктура файловой системы.

Эксфильтрация

Перед отправкой данных происходит сериализация с помощью msgpack и шифрование AES256-GCM (поле Secret в embedded_Config).

35.png
Рисунок 35. Пример преобразования собираемых данных

Далее, после архивации данных, используя метод POST, отправляет их на URL, который формируется из BackendAddress (поле из embedded_Config) + “/api/collection-result”.

36.png
Рисунок 36. Эксфильтрация данных

Также присутствует возможность обновления структуры model_CollectionConfig, используя метод GET, отправляя запрос на URL, который формируется из BackendAddress (поле из embedded_Config) + “/api/config”.

37.png
Рисунок 37. Обновление конфига

Структура model_CollectionConfig является конфигом для команды collect и имеет следующие поля.

struct model_CollectionConfig
{
    _slice__slice_string Commands;
    _slice_string Files;
    _slice_string TreePaths;
};


Поля структуры имеют следующие назначения.

Поле

Назначение

CommandsКоманда bb_files_*.
FilesФайлы.
TreePathsПути.

Выводы

Группа ExCobalt продолжает демонстрировать высокий уровень активности и решительности в атаках на российские компании, постоянно пополняя свой инструментарий и усовершенствуя свои техники. Они не только разрабатывают новые методы атак, но и активно улучшают свои существующие инструменты, включая бэкдор GoRed.

Наблюдается тенденция к расширению возможностей и функциональности бэкдора GoRed, что указывает на стремление ExCobalt к более сложным и эффективным методам взлома и кибершпионажа. Это включает в себя расширение функционала сбора данных жертв и повышение скрытности как в системе, так и в коммуникациях с управляющими серверами.

Кроме того, ExCobalt демонстрирует гибкость и адаптивность, пополняя свой инструментарий патченными тулзами, что позволяет им эффективно преодолевать защитные меры и адаптироваться к изменениям методов защиты. Использование патченных тулзов свидетельствует о глубоком понимании ExCobalt слабых мест атакуемых компаний, а использование многочисленных уязвимостей позволяет им развивать сложные атаки на них.

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

Авторы:

Владислав Лунин, старший специалист отдела исследования угроз информационной безопасности экспертного центра безопасности Positive Technologies.

Александр Бадаев, специалист отдела исследования угроз информационной безопасности экспертного центра безопасности Positive Technologies.

IOCs

Файловые индикаторы

Сетевые индикаторы

Файловые сигнатуры

rule PTESC_apt_linux_UA_Excobalt__RiskTool__PsPatched{
  strings:
    $s = "For more details see ps(1)."
    $readproc_patched = {48 8B BB A8 02 00 00 48 ?? ?? 74 0DE8 ?? ?? ?? ?? 85 ?? 0F 85 81 05 00 00 48 8B BB 28 03 00 00 48 ?? ?? 74 0D E8 ?? ?? ?? ?? 85 C0 0F 85 68 05 00 00 41 8B 54 24 50 48 89 D8 85 D2 74 13 83 7B 04 02 0F 84 52 05 00 00 83 ?? ?? 0F 84 49 05 00 00}
  condition:
    ( uint32be ( 0 ) == 0x7f454c46 ) and ( all of them )
}

rule PTESC_apt_linux_UA_Excobalt__RiskTool__NetstatPatched{
  strings:
    $s = "usage: netstat [-vWeenNcCF] [<Af>] -r"
    $main_patched = {48 8D 05 ?? ?? ?? ?? 4A 8B ?? ?? 0F B6 [1-2] 8D 50 ?? 80 FA ?? 76 ?? 84 C0 74 ?? 66 0F EF C0 31 F6}
  condition:
    ( uint32be ( 0 ) == 0x7f454c46 ) and ( all of them )
}

rule PTESC_apt_linux_UA_Excobalt__RiskTool__SsPatched{
  strings:
    $s = "Usage: ss [ OPTIONS ]"
    $main_patched = {48 63 FD 48 8D 05 ?? ?? ?? ?? 4C 89 EE 83 C5 ?? 48 C1 E7 ?? BA ?? ?? ?? ?? 48 01 C7 E8 ?? ?? ?? ?? 83 FB ?? 0F 9E C0 83 FD ?? 0F 9E C2 48 83 C3 ?? 20 D0 0F 85 ?? ?? ?? ??}
  condition:
    ( uint32be ( 0 ) == 0x7f454c46 ) and ( all of them )
}

rule PTESC_apt_linux_UA_Excobalt__RiskTool__HtopPatched{
  strings:
    $s = "/.htoprc"
    $readproc_patched = {48 89 DF E8 ?? ?? ?? ?? 48 8B 7B ?? 48 85 FF 74 ?? E8 ?? ?? ?? ?? 85 C0 75 ?? 48 8B 7B ?? 48 85 FF 74 ?? E8 ?? ?? ?? ?? 85 C0 75 ??}
  condition:
    ( uint32be ( 0 ) == 0x7f454c46 ) and ( all of them )
}

rule PTESC_apt_multi_UA_Excobalt__Backdoor__GoRed__Obf{
	strings:
		$s1 = ").Parse" ascii nocase
		$s2 = ").MarshalBinary" ascii nocase
		$s3 = ").UnmarshalBinary" ascii nocase
		$s4 = ").Request" ascii nocase
		$s5 = ").Connect" ascii nocase
		$s6 = ").MarshalJSON" ascii nocase
		$s7 = ").Hash" ascii nocase
		$s8 = ").Add" ascii nocase
		$s9 = ").Close" ascii nocase
		$s10 = "JWT" ascii nocase
		$s11 = "config" ascii nocase
		$s12 = "GET" ascii nocase
		$s13 = "POST" ascii nocase
		$s14 = "a5674391" ascii nocase
		$c1 = {0F B6 54 0C ? ? 0F B6 74 0C ? ? 29 D6 40 88 74 0C ? ? 48 FF ? ? 48 83 F9 ? ? 7C ? ? }
		$c2 = {0F B6 54 04 ? ? 0F B6 74 04 ? ? 31 D6 40 88 74 04 ? ? 48 FF ? ? 66 0F 1F 44 00 ? ? 48 83 F8 ? ? 7C ? ? }
		$c3 = {0F B6 54 0C ? ? 0F B6 74 0C ? ? 01 F2 88 54 0C ? ? 48 FF ? ? 48 83 F9 ? ? 7C ? ? }
	condition:
		((uint32be(0) == 0x7f454c46) or (uint16be(0) == 0x4d5a)) and (all of them)
}

rule PTESC_apt_multi_UA_Excobalt__Backdoor__GoRed{
	strings:
		$s1 = "embedded.GetConfig" ascii nocase
		$s2 = "common.runInBackground" ascii nocase
		$s3 = "common.run" ascii nocase
		$s4 = "common.RunCommand" ascii nocase
		$s5 = "revsh" ascii nocase
		$s6 = "dns" ascii nocase
		$s7 = "ws" ascii nocase
		$s8 = "dump" ascii nocase
		$s9 = "shell" ascii nocase
		$s10 = "files" ascii nocase
	condition :
		((uint32be(0) == 0x7f454c46) or (uint16be(0) == 0x4d5a)) and (all of them)
}

MITRE TTPs

Вердикты продуктов Positive Technologies

PT Sandbox

apt_multi_UA_Excobalt__Backdoor__GoRed
apt_multi_UA_Excobalt__Backdoor__GoRed__Obf
apt_linux_UA_Excobalt__RiskTool__PsPatched
apt_linux_UA_Excobalt__RiskTool__NetstatPatched
apt_linux_UA_Excobalt__RiskTool__HtopPatched
apt_linux_UA_Ruh8__Dropper__kit
tool_multi_ZZ_WebShell__Backdoor__Generic__PHP
tool_multi_ZZ_WebShell__Backdoor__PHP__Obfuscated
tool_multi_ZZ_Gsocket__NetTool

MaxPatrol SIEM

Unix_Boot_Modify (mod probe)
Unix_System_Information_Discovery
Unix_Connect_from_Suspicious_Dir
Unix_Suspicious_Command

PT NAD

BACKDOOR [PTsecurity] GoRed Request sid: 10011377
BACKDOOR [PTsecurity] GoRed Exfiltration sid: 10011378
ATTACK AD [PTsecurity] Anonymous SMB connect to IPC share sid: 10005876, 10005877
TOOLS [PTsecurity] .gs.thc.org domain resolve. Probably activity gsocket sid: 10009306
TOOLS [PTsecurity] gsocket server activity sid: 10009305
SUSPICIOUS [PTsecurity] Suspicious communication using UA go-external-ip & External IP Check sid: 10011380
POLICY [PTsecurity] HTTP Host header RFC2616 violation sid: 10011339
POLICY [PTsecurity] Unsuccessful SMB Port Scan External Network sid: 10001310, 10003655