{
	"id": "1cde4c83-04fd-40c2-ae22-d7f6d9ae4c67",
	"created_at": "2026-04-10T03:20:01.962757Z",
	"updated_at": "2026-04-10T03:22:17.926219Z",
	"deleted_at": null,
	"sha1_hash": "e8bbcab2a44fbc4e9f63eab01e0c27ea3ed8215c",
	"title": "(Ex)Cobalt. Обзор инструментов группы в атаках за 2024–2025 годы",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 2233842,
	"plain_text": "(Ex)Cobalt. Обзор инструментов группы в атаках за 2024–2025 годы\r\nBy Positive Technologies\r\nPublished: 2025-12-02 · Archived: 2026-04-10 02:37:45 UTC\r\n2.4. Руткит PUMAKIT\r\nНаиболее интересным инструментом группировки (Ex)Cobalt, обнаруженным в ходе расследований, является руткит\r\nPUMAKIT, который маскируется под легитимные компоненты ОС и скрывает свое присутствие.\r\nРанее инструмент был описан в публикации Elastic Security Labs: коллеги описали дроппер и часть\r\nфункциональности LKM-руткита. Позднее исследователи из Solar 4RAYS опубликовали собственный анализ,\r\nв котором описали особенности работы бэкдора и механизм кражи данных.\r\nВ настоящей статье мы более подробно рассмотрим некоторые функции данного инструмента, а также проследим\r\nэтапы его развития.\r\nРисунок 8. Диаграмма взаимодействия компонентов PUMAKIT\r\nОбнаружить его удалось при помощи изучения аномалий во временных метках файлов. Для этого в инструментах,\r\nразработанных командой департамента комплексного реагирования на киберугрозы PT ESC, применяется алгоритм\r\nсопоставления временных меток файлов, который выявляет отклонения от времени создания или модификации\r\nфайлов в каталоге.\r\nРисунок 9. Результаты работы инструмента для поиска аномалий\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 1 of 34\n\nРисунок 10. Обнаруженные аномалии во временных метках\r\nТаким образом был определен круг подозрительных файлов в служебных каталогах ОС. Затем путем ручного\r\nанализа удалось выявить файл, маскировавшийся под компонент операционной системы (cron), размер которого\r\nотличался от размера легитимного файла более чем в 20 раз.\r\n2.4.1. Первая стадия: cron\r\nНайденный файл, подменявший системный файл cron, представлял собой дроппер и состоял из двух основных\r\nкомпонентов — оригинального cron (tgt, target) и установщика вредоносного модуля (wpn, weapon). Вместо записи\r\nисполняемых файлов на диск дроппер загружал их в анонимные файловые дескрипторы (/memfd:wpn и /memfd:tgt),\r\nа затем запускал комбинацией функций fork и execveat, обеспечивая их выполнение без сохранения в файловой\r\nсистеме. Таким образом, оригинальная функциональность cron сохранялась, что не вызывало подозрений,\r\nа вредоносная нагрузка незаметно выполнялась при каждом запуске системы.\r\nПолучив первоначальный доступ к системе с повышенными привилегиями, злоумышленники определяли версию\r\nядра и выбирали установщик модуля, совместимый с конфигурацией системы жертвы. Это обеспечивало\r\nкорректную интеграцию модуля в целевую среду и гарантировало его успешное выполнение.\r\n2.4.2. Вторая стадия: weapon\r\nОсновная задача wpn — установка LKM-руткита, совместимого с системой жертвы. Для сокрытия активности\r\nзагрузчик маскируется под системный процесс /usr/sbin/sshd.\r\nЛогика установки начинается с идентификации узла: вычисляется его уникальный идентификатор, формирующийся\r\nследующим образом:\r\n1. с помощью Netlink-сокета в ядро отправляется запрос для получения информации о сетевых интерфейсах;\r\n2. MAC-адреса обнаруженных интерфейсов последовательно сохраняются, за исключением начинающихся\r\nс «docker», «veth» или «br»;\r\n3. полученные адреса объединяются в общий буфер, разделенный символами новой строки «\\n»;\r\n4. к получившемуся результату дописывается локальное имя системы и подается на вход алгоритма\r\nхеширования MD5;\r\n5. вычисленное значение сохраняется в качестве agent_id системы.\r\nКак только был получен и сохранен идентификатор системы — формируется команда sh -c «dmesg | grep ’ecure boot\r\nenabled’», которая выполняется с помощью интерпретатора командной строки. Результат ее выполнения позволяет\r\nопределить состояние Secure Boot — механизма защиты, предотвращающего загрузку неподписанных или\r\nизмененных загрузочных образов:\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 2 of 34\n\nесли Secure Boot активен — выполнение немедленно прерывается; \r\nиначе — установка модуля ядра считается возможной: инициируется обращение к системному файлу\r\n/lib/modules/\u003cверсия_ядра\u003e/build/Module.symvers для проверки соответствия экспортируемых символов ядра\r\n(функций, переменных) символам модуля.\r\nПри успешном доступе к файлу выполняется последовательное чтение его записей, при этом для каждой\r\nсохраняются контрольная сумма (cyclic redundancy check, CRC) и само имя символа.\r\n2.4.2.1. Формирование собственного списка Module.symvers\r\nЕсли не удалось получить информацию напрямую — создается собственная версия экспортируемых символов ядра:\r\n1. Процесс обращается к двум файлам — /proc/version и /proc/cmdline, извлекая из них информацию о версии\r\nядра.\r\n2. В каталоге /boot выполняется поиск файла, начинающегося с «vmlinuz-».\r\n3. Оставшаяся часть имени, определяющая версию ядра, сравнивается с теми версиями, которые были\r\nполучены на шаге 1.\r\n4. Если все три версии ядра совпали — создается файл с именем /tmp/script.sh, в который записывается скрипт\r\nдля распаковки сжатого файла ядра.\r\n \r\n#!/bin/sh\r\n \r\nc() {\r\n if file \"$1\" | grep -q \"ELF\"; then\r\n exit 0\r\n else\r\n return 1\r\n fi\r\n}\r\n \r\nd() {\r\n for p in tr \"$1\\n$2\" \"\\n$2=\" \u003c \"$i\" | grep -abo \"^$2\"\r\n do\r\n p=${p%%:*}\r\n tail -c+$p \"$i\" | $3 \u003e $r 2\u003e/dev/null\r\n c $r\r\n done\r\n}\r\n \r\ni=$1\r\nr=\"/tmp/vmlinux\"\r\n[[ -z $vmlinuz_path ]] || exit 0\r\nd '\\037\\213\\010' xy gunzip\r\nd '\\3757zXZ\\000' abcde unxz\r\nd 'BZh' xy bunzip2\r\nd '\\135\\0\\0\\0' xxx unlzma\r\nd '\\211\\114\\132' xy 'lzop -d'\r\nd '\\002!L\\030' xxx 'lz4 -d'\r\nd '(\\265/\\375' xxx unzstd\r\nc $i\r\nexit 1\r\nЛистинг 1. Базовый скрипт для распаковки сжатого файла ядра\r\n5. Данный скрипт запускается с помощью команды bash /tmp/script.sh “/boot/vmlinuz-\u003cKERNEL_RELEASE\u003e”.\r\n6. В результате выполнения скрипта в каталоге /tmp появляется разархивированный файл ядра.\r\n7. Сам скрипт удаляется.\r\nПосле получения файла ядра выполняется перебор его таблицы заголовков для получения и сохранения размеров\r\nи смещений конкретных секций, таких как:\r\n__kcrctab_gpl: таблица контрольных сумм для символов, экспортируемых под лицензией GPL. Каждая запись\r\nсодержит CRC, соответствующую символу из __ksymtab_gpl;\r\n__ksymtab_gpl: таблица символов, используется для разрешения ссылок на символы в модулях, совместимых\r\nс лицензией GPL. Каждая запись имеет поля: \r\nvalue — адрес экспортируемого символа; \r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 3 of 34\n\nname — указатель на строку в секции __ksymtab_strings, содержащую имя символа; \r\n__ksymtab: список всех экспортированных символов, кроме тех, которые помечены GPL. Структурно\r\nидентична __ksymtab_gpl.\r\nПеред переходом к извлечению информации о символах ядра с использованием перечисленных заголовков\r\nиз секции .rodata считывается версия разархивированного ядра (рис. 11). С ее помощью определяется размер\r\nединичной записи в перечисленных выше секциях и идентификатор одного из четырех имеющихся обработчиков,\r\nкоторый должен быть установлен для корректного парсинга и сохранения записей.\r\nРисунок 11. Запись о версии ядра в секции .rodata\r\n2.4.2.2. Проверка совместимости\r\nПосле того как была получена информация об используемых символах ядра, независимо от способа ее получения,\r\nвыполняется проверка совместимости этих символов с символами модуля, хранящимися в секции versions:\r\nпоочередно каждая запись из списка системы сравнивается с соответствующей в секции модуля до тех пор, пока\r\nне будет найдено соответствие. Если хоть одна из них не была найдена в системе, было превышено количество\r\nитераций или CRC отличается — выполнение будет завершено.\r\nВ ином случае будет выполнена загрузка LKM-руткита в систему жертвы с помощью системного вызова\r\ninit_module.\r\n2.4.2.3. Отладочный режим\r\nПри детальном рассмотрении в загрузчике руткита был обнаружен отладочный режим, позволяющий получить\r\nрасширенные сведения о процессе установки. Учитывая его сложность и множество зависимостей,\r\nмы предполагаем, что данный режим используется атакующей стороной в том случае, если при стандартном запуске\r\nдроппера (без дополнительных аргументов) не удается установить вредоносный модуль.\r\nДля его активации злоумышленники выполняют два последовательных шага: \r\n1. Запускают дроппер с предварительно установленной переменной окружения HUINDER и одним\r\nиз следующих аргументов:\r\n—extract-target или -et для извлечения tgt.bin;\r\n—extract-weapon или -ew для извлечения wpn.bin.\r\n2. После получения файла wpn.bin запускают его с аргументами -f, -v и -t (порядок аргументов не имеет\r\nзначения) с правами суперпользователя.\r\nПосле выполнения описанных выше действий загрузчик запускается в режиме отладки, в котором вместо\r\nнепосредственной установки модуля осуществляется проверка его совместимости с системой. В этом режиме\r\nатакующему возвращается либо подтверждение возможности установки, либо подробное описание причин,\r\nпо которым установка невозможна. Примеры возможных ошибок и их выводов показаны на рис. 12–14.\r\nРисунок 12. Ошибка: используется Secure Boot\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 4 of 34\n\nРисунок 13. Ошибка: несовпадение экспортируемых символов\r\nРисунок 14. Предполагаемая последовательность действий злоумышленника\r\nКроме того, при запуске загрузчика в режиме отладки будет использоваться специальная версия скрипта\r\nдля распаковки ядра, которая отличается от обычной добавленным журналированием, проверкой наличия утилит\r\nдля распаковки (чтобы определить отсутствующие разархиваторы) и дополнительной проверкой на наличие данных\r\nв файле, что делает процесс более информативным (рис. 14).\r\n \r\n#!/bin/sh\r\n \r\nc() {\r\n if [[ ! -s \"$1\" ]]; then\r\n return 1\r\n fi\r\n if file \"$1\" | grep -q \"ELF\"; then\r\n echo \"OK\"\r\n exit 0\r\n else\r\n echo \"NOT ELF: $1\"\r\n return 1\r\n fi\r\n}\r\n \r\nd() {\r\n echo \"Try: $1, $2, $3\"\r\n IFS=' ' read -r dcmd dargs \u003c\u003c\u003c \"$3\"\r\n \r\n for p in tr \"$1\\n$2\" \"\\n$2=\" \u003c \"$i\" | grep -abo \"^$2\"\r\n do\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 5 of 34\n\np=${p%%:*}\r\n echo \"Check: $i.$p\"\r\n if ! command -v \"$dcmd\" \u0026\u003e /dev/null; then\r\n echo \"Warning: Decompressor '$dcmd' not available. Skipping...\"\r\n return 0\r\n fi\r\n tail -c+$p \"$i\" | $3 \u003e $r 2\u003e/dev/null\r\n c $r\r\n done\r\n}\r\n \r\ni=$1\r\nr=\"/tmp/vmlinux\"\r\n[[ -z $vmlinuz_path ]] || exit 0\r\nd '\\037\\213\\010' xy gunzip\r\nd '\\3757zXZ\\000' abcde unxz\r\nd 'BZh' xy bunzip2\r\nd '\\135\\0\\0\\0' xxx unlzma\r\nd '\\211\\114\\132' xy 'lzop -d'\r\nd '\\002!L\\030' xxx 'lz4 -d'\r\nd '(\\265/\\375' xxx unzstd\r\nc $i\r\nexit 1\r\nЛистинг 2. Расширенный скрипт для распаковки сжатого файла ядра\r\nРисунок 15. Работа расширенной версии скрипта\r\n2.4.3. Третья стадия: LKM audit\r\nПри инициализации модуля выполняется несколько подготовительных шагов, необходимых для последующей\r\nработы руткита. Сначала выполняется регистрация и снятие kprobe, чтобы извлечь адрес kallsyms_lookup_name,\r\nобычно не экспортируемый напрямую. Полученный адрес сохраняется и используется для определения\r\nрасположения таблицы системных вызовов.\r\nПосле завершения подготовительных операций отключается защита записи в памяти ядра — для этого\r\nмодифицируется содержимое регистра управления CR0: временно сбрасывается бит Write Protect, что позволяет\r\nзаписывать в ранее защищенные области памяти ядра (рис. 16).\r\nРисунок 16. Отключение защиты записи\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 6 of 34\n\nПосле этого модуль способен напрямую изменять содержимое таблицы системных вызовов, обходя стандартные\r\nмеханизмы защиты, которые в обычных условиях запрещают подобные модификации.\r\nСразу после отключения защиты модуль заменяет стандартные системные вызовы своими обработчиками.\r\nДля этого сначала сохраняется оригинальный адрес каждого перехватываемого вызова, чтобы в дальнейшем можно\r\nбыло восстановить исходное поведение системы, после чего указатель в таблице системных вызовов обновляется\r\n(рис. 17).\r\nРисунок 17. Переопределение системных вызовов\r\nПолный перечень подменяемых системных вызовов: execveat, execve, newfstatat, mmap, openat, newlstat, getdents64,\r\nnewfstat, getsid, newstat, getpgid, close, rmdir, open, getdents, write, kill, read.\r\nПочти все системные вызовы, перехватываемые вредоносным модулем, следуют единой логике: обработчик сначала\r\nанализирует переданные аргументы, а затем принимает решение о модификации возвращаемого пользователю\r\nответа.\r\nНапример, вызовы newlstat и newstat, предназначенные для получения информации о файлах, в обычных условиях\r\nвозвращают полный набор данных, тогда как их обновленные версии определяют, к какому объекту файловой\r\nсистемы запрашивается доступ, и, если это требуется, скрывают файлы вредоноса, подменяя возвращаемые\r\nзначения. Ниже приведен список скрываемых от пользователя объектов:\r\n/proc\r\n/sys/module/audit/initstate  \r\n/sys/module/audit/holders  \r\n/sys/module/audit/refcnt \r\n/sys/module/audit  \r\n/usr/share\r\nОтдельного внимания заслуживают переопределенные вызовы rmdir, read и write, содержащие ключевую\r\nдля дальнейшего использования модуля логику.\r\n2.4.3.1. Механизм стилера: перехват системных вызовов write и read\r\nДля реализации механизма стилера осуществляется подмена системных вызовов write и read. Переопределенный\r\nwrite анализирует передаваемые на запись данные с целью обнаружения конфиденциальной информации,\r\nв частности строк, содержащих ключевые слова «password» или «passphrase». Переопределенный вызов read, в свою\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 7 of 34\n\nочередь, предназначен для перехвата и сохранения закрытых криптографических ключей PEM-формата,\r\nсчитываемых процессом при установке защищенного соединения.\r\nВ обоих случаях модуль сохраняет в памяти сами перехваченные данные и их тип, а также данные о процессе,\r\nинициировавшем системный вызов.\r\nПомимо таких общих фраз, как «password», «login», «passphrase» и «...PRIVATE KEY...», мы обнаружили, что стилер\r\nдополнительно перехватывает данные, содержащие разные варианты написания слов «пользователя:» и «пароль:».\r\nФакт поиска строк на русском языке сигнализирует о фокусе атакующих на русскоязычном сегменте интернета.\r\nОсобого внимания заслуживает механизм внедрения SSH-ключа, обеспечивающий атакующему устойчивый\r\nудаленный доступ. Для этого руткит перехватывает вызовы open и openat, отслеживая обращения к файлу\r\nauthorized_keys — стандартному компоненту OpenSSH, расположенному в каталоге ~/.ssh/ конкретного пользователя\r\nи определяющему, какие публичные ключи разрешают доступ к соответствующей учетной записи без ввода пароля.\r\nПри попытке чтения файла (например, в процессе подключения или его проверки) руткит на лету модифицирует\r\nсодержимое: к оригинальным данным дописывается заранее подготовленный публичный ключ. При этом сам файл\r\nна диске остается неизменным: подмена производится исключительно в памяти, в момент вызова read.\r\nВ типичном сценарии атаки злоумышленник, имея на руках приватный ключ, инициирует SSH-подключение.\r\nПри проверке ~/.ssh/authorized_keys сервер читает подмененный в памяти список ключей, распознает внедренный\r\nключ как доверенный и открывает сессию без запроса пароля. После этого атакующий получает интерактивный\r\nдоступ от имени целевой учетной записи и может скрытно выполнять команды и разворачивать дополнительные\r\nинструменты. Скрытность сохраняется до тех пор, пока загружен модуль руткита.\r\nДаже смена пароля и отключение парольной аутентификации не устраняют угрозу: доступ по внедряемому ключу\r\nсохраняется и остается активным.\r\nНаибольшую угрозу представляет ситуация, при которой публичный ключ добавляется в authorized_keys\r\nпользователя root: это дает атакующим максимальные привилегии и полный контроль над целевым узлом.\r\nИсходя из представленной функциональности, можно предположить, что основным назначением руткита является\r\nзакрепление в системе, а также перехват учетных данных, обрабатываемых в процессе аутентификации\r\nпри установлении SSH-соединений. Эти данные впоследствии будут использованы не только для закрепления\r\nна начальном скомпрометированном узле, но и для дальнейшего перемещения внутри инфраструктуры жертвы.\r\n2.4.3.2. Механизм взаимодействия с руткитом через переопределение системного вызова rmdir\r\nКлючевую роль в работе руткита играет переопределенный системный вызов rmdir, который выступает внутренним\r\nканалом взаимодействия между пользовательской частью бэкдора и установленным модулем ядра. Инициатором\r\nэтих вызовов является сам бэкдор, передавая в качестве аргумента не путь к каталогу, а специально\r\nсформированную строку-аргумент.\r\nВ основе данного механизма лежит перехват системного вызова: при обращении к rmdir перед стандартной\r\nоперацией удаления руткит проверяет переданный путь и, если он начинается с «zarya», интерпретирует\r\nпоследующую часть строки как управляющую команду. Данная команда должна строго соответствовать\r\nопределенной структуре, которая представлена на рис. 18.\r\n \r\nrmdir zarya_[command]_[ argument]\r\nПолный список команд, доступных атакующему, представлен в табл. 1.\r\nТаблица 1. Перечень команд для управления\r\nКоманда Аргумент Описание\r\nv Атакующие\r\nиспользуют символ-заглушку «0»,\r\nно может быть\r\nи любой другой\r\nОтображает версию установленного модуля\r\nd\r\nИзвлекает данные, собранные стилером, и выводит\r\nих в пространство пользователя. После чего удаляет эти\r\nданные из памяти\r\nс\r\nКопирует встроенную конфигурацию для установки\r\nсоединения (данные из секции .puma-config)\r\nв пространство пользователя\r\nt Выполняет тестовый вызов без возврата ошибки\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 8 of 34\n\nu\r\nВосстанавливает отображение скрытого модуля,\r\nвозвращая его в список загруженных модулей\r\n9\r\nОтображает в пространство пользователя таблицу\r\nсоответствий PID ↔ IP-адрес, связывающую их между\r\nсобой (рис. 19)\r\n0 Повышает привилегии у вызвавшего процесса\r\n1\r\nИдентификатор\r\nпроцесса (PID)\r\nПринимает строку с идентификатором процесса,\r\nпроверяет наличие соответствующей записи\r\nво внутренней таблице и при ее отсутствии добавляет\r\nпереданный PID в список скрываемых. Данная команда\r\nтакже проверяет актуальность текущих записей, удаляя\r\nсведения о завершившихся процессах и их IP-адресах.\r\nИспользуется совместно с командой «5» при установке\r\nсоединения\r\n5 IP-адрес\r\nКоманда принимает IP-адрес в виде строки,\r\nпреобразовывает его в 32-битное число и, при отсутствии\r\nсоответствующей записи, добавляет его во внутреннюю\r\nтаблицу скрываемых подключений (см. команду «9»).\r\nИспользуется совместно с командой «1» при установке\r\nсоединения\r\nk\r\nИдентификатор\r\nпроцесса (PID)\r\nУстанавливает PID руткита\r\nБыло установлено, что, помимо перечисленных выше команд, при вызове rmdir с аргументом zarya или zarya_\r\n(без указания конкретной команды) у инициировавшего вызов процесса повышаются привилегии.\r\nРисунок 19. Пример отображения команды «9»\r\nАнализ множества семплов показал, что в более поздних версиях функциональность модуля была расширена.\r\nВ частности, была добавлена логика сокрытия используемых портов — аналогичная той, которая была описана\r\nдля IP-адресов. Также были добавлены команды для управления портами (табл. 2).\r\nТаблица 2. Добавленные в новых версиях команды\r\nКоманда Аргумент Описание Замечание\r\n7\r\nЛокальный\r\nпорт\r\nДобавляет локальный\r\nпорт в список\r\nскрываемых (см. рис. 20),\r\nесли значение уникально\r\nВызов rmdir с данными командами\r\nвыполняется при получении\r\nсоответствующей команды с C2-сервера\r\n(см. описание полезной нагрузки\r\nбэкдора) 8\r\nЛокальный\r\nпорт\r\nУдаляет локальный порт\r\nиз списка скрываемых\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 9 of 34\n\nРисунок 20. Пример отображения обновленной команды «9»\r\n2.4.3.3. Использование ftrace-хуков\r\nПомимо перехвата системных вызовов, данный руткит использует механизм ftrace для установки хуков на заранее\r\nопределенный набор функций ядра Linux. Адреса этих функций вычисляются динамически с помощью ранее\r\nполученного указателя на kallsyms_lookup_name. Вычисленные адреса используются для корректной установки\r\nхуков.\r\nРуткит дополнительно анализирует каждую целевую функцию, определяя в ее коде участок, наиболее подходящий\r\nдля внедрения хука. После чего с помощью функций ftrace_set_filter_ip и register_ftrace_function регистрирует\r\nобработчик, перенаправляющий выполнение функций на их подмененную реализацию.\r\nПолный перечень функций ядра, подвергающихся перехвату, можно разделить на две группы: полностью\r\nотключаемые руткитом и те, поведение которых переопределяется. В первую группу входят функции, связанные\r\nс механизмами контроля доступа: selinux_file_open, selinux_file_permission, avc_has_perm, file_map_prot_check,\r\nselinux_inode_setattr, selinux_inode_permission, selinux_socket_bind и selinux_socket_connect. Обработчики для них\r\nполностью заменяют оригинальную логику на простую заглушку, которая всегда завершает выполнение без ошибок\r\n(return 0). В итоге любые проверки или действия, которые должны были выполняться, фактически отключаются,\r\nпоскольку система считает, что они завершились успешно.\r\nКо второй группе относятся функции sk_diag_fill, tpacket_rcv, tcp4_seq_show, kernel_clone или _do_fork\r\n(в зависимости от версии модуля PUMAKIT), а также nf_hook_slow. В более поздних версиях к этому списку\r\nдобавляется функция inet_sk_diag_fill. Обработчики для каждой из них не блокируют выполнение оригинального\r\nкода полностью, а осуществляют предварительный анализ входных аргументов. Во всех перечисленных случаях\r\nобработчики извлекают IP-адрес из передаваемых в функцию аргументов и сравнивают их с внутренним списком\r\nIP-адресов, хранящимся в структуре руткита (см. рис. 19 и 20). Если среди переданных аргументов обнаруживается\r\nсовпадение с одним из имеющихся в структуре адресов — оригинальная функция ядра не вызывается, а обработчик\r\nвместо этого сразу возвращает нулевой результат, тем самым подавляя выполнение исходной функции. Такой\r\nмеханизм переопределения позволяет эффективно скрывать нелегитимные подключения, процессы и сетевую\r\nактивность, обеспечивая их невидимость для средств мониторинга и анализа.\r\nОсобого внимания заслуживает функция nf_hook_slow, поскольку перехват ее вызова позволяет злоумышленнику\r\nобойти работу файрвола на уровне ядра, исключая проверку пакетов механизмами фильтрации.\r\nВ стандартной реализации Netfilter функция nf_hook_slow выполняет последовательный вызов всех\r\nзарегистрированных в ядре хуков, включая обработчики iptables и nftables. Именно эти хуки принимают\r\nокончательное решение о том, пропустить, заблокировать или перенаправить пакет.\r\nОднако в данном случае внедренный обработчик анализирует пакет до его передачи в оригинальную функцию,\r\nизвлекая исходный и целевой IP-адреса и сравнивая их с внутренним списком, хранящимся в рутките. Если хотя бы\r\nодин из них совпадает — обработчик возвращает NF_ACCEPT (1), сразу принудительно разрешая прохождение\r\nпакета без его передачи в nf_hook_slow.\r\nТакой подход позволяет атакующему полностью скрывать трафик от механизмов мониторинга и обходить политики\r\nфайрвола, поскольку пакеты, принудительно принимаемые обработчиком, минуют стандартные процессы\r\nфильтрации и анализа. Таким образом, файрвол не имеет возможности зафиксировать, проанализировать\r\nили заблокировать такой трафик. В результате злоумышленник может беспрепятственно устанавливать скрытые\r\nсоединения, не оставляющие следов в системах мониторинга и журналирования, полностью избегая наложенных\r\nограничений безопасности.\r\n2.4.3.4. Удаление из списка модулей и инъекция бэкдора libs.so\r\nПосле завершения установки хуков и подмены адресов системных вызовов на собственные обработчики руткит\r\nпринимает меры по сокрытию своего присутствия в системе. Для этого он удаляет себя из списка загруженных\r\nмодулей ядра, модифицируя указатели в двусвязном списке, хранящим все активные модули. В результате\r\nстандартные инструменты мониторинга, такие как команда lsmod или просмотр файла /proc/modules, перестают\r\nотображать модуль руткита.\r\nДалее руткит запускает отдельный поток, выполняющий функцию, отвечающую за инъекцию и дальнейшее\r\nподдержание бэкдора. В ней в бесконечном цикле производится проверка существования процесса бэкдора\r\nс помощью функций find_get_pid и pid_task. Если целевой процесс отсутствует или с момента последней проверки\r\nпрошло более пяти секунд — проверка выполняется повторно и при необходимости руткит инициирует запуск\r\nбэкдора, обеспечивая его постоянное присутствие в системе. Для этого с помощью механизма запуска\r\nпользовательских процессов из ядра call_usermodehelper через оболочку /bin/sh выполняются две команды:\r\n1. truncate -s 0 /usr/share/zov_f/zov_latest;\r\n2. cat /dev/null 1\u003e/dev/null.\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 10 of 34\n\nПри этом, помимо самих команд, call_usermodehelper также получает указатель на массив переменных окружения:\r\nSHELL=sh \r\nHOME=/\r\nLD_PRELOAD=/lib64/libs.so\r\nPATH=/sbin:/bin:/usr/sbin:/usr/bin\r\nСреди этих переменных особое значение имеет LD_PRELOAD=/lib64/libs.so, обеспечивающая инъекцию бэкдора.\r\nВ результате первая команда обнуляет определенный файл, используемый бэкдором (его работа будет рассмотрена\r\nдалее), фактически скрывая его содержимое, пока он находится в неактивном состоянии. Вторая команда, по сути,\r\nне выполняет никаких значимых действий: она просто считывает пустой файл и перенаправляет вывод в /dev/null.\r\nОднако ее запуск необходим для выполнения с заданным окружением, которое позволяет активировать инъекцию\r\nбэкдора без заметных следов в системе.\r\nИтак, описанный выше механизм обеспечивает автоматическое восстановление и постоянное присутствие\r\nв системе.\r\n2.4.4. Четвертая стадия: libs.so\r\nПосле того как LKM-руткит вызывает call_usermodehelper с модифицированным окружением (переменная\r\nLD_PRELOAD указывает на встроенный в рутките файл libs.so), бэкдор немедленно загружается в адресное\r\nпространство созданного процесса и начинает выполнение. Поскольку вызванная команда завершается практически\r\nсразу, бэкдор дополнительно предпринимает шаги, направленные на закрепление в системе и обеспечение\r\nвозможности длительной автономной работы.\r\nДля этого выполняется превращение процесса в демон, позволяя ему работать в фоновом режиме без привязки\r\nк терминалу и управляющей сессии. Достигается это двумя последовательными вызовами функции fork: первый\r\nсоздает дочерний процесс и сразу завершает родительский, разрывая исходную связь с запускающим процессом.\r\nЗатем дочерний процесс вновь вызывает fork, а следом за ним и setsid, чтобы создать новую сессию, полностью\r\nотсоединенную от управляющего терминала. Эти действия окончательно разрывают связь бэкдора с его\r\nпервоначальным контекстом запуска, гарантируя, что процесс перестает зависеть от сигналов или жизненного цикла\r\nродительского процесса.\r\nВслед за этим бэкдор изменяет текущий рабочий каталог на корневой (chdir(\"/\")), чтобы исключить зависимость\r\nот исходного пути, устанавливает маску прав доступа в значение umask(0), предотвращая возможные ограничения\r\nпри создании файлов, и закрывает все открытые файловые дескрипторы (включая стандартные потоки ввода,\r\nвывода и ошибок), предотвращая случайный вывод данных в консоль.\r\nДля получившегося в результате процесса с помощью вызова функции getpid определяется PID, а после, с помощью\r\nполученного значения, выполняется вызов rmdir, переопределенного руткитом, с параметром zarya_k_\u003cPID\u003e —\r\nдля связывания между собой бэкдора и руткита.\r\nПосле этого бэкдор переходит к следующему этапу своей работы: он проверяет наличие конфигурационных данных,\r\nнеобходимых для установки соединения с C2-сервером, в секции .konfig, расположенной в памяти процесса.\r\nВ случае отсутствия этих данных бэкдор извлекает необходимую конфигурацию из секции .puma-config,\r\nрасположенной в памяти ранее загруженного LKM-модуля. Для извлечения и последующего заполнения\r\nсобственной конфигурационной секции используется механизм, основанный на вызове функции rmdir, но уже\r\nс аргументом zarya_c_0. Особого внимания заслуживает структура извлекаемой конфигурации, представленная\r\nна рис. 21.\r\nРисунок 21. Структура конфигурационной секции модуля .puma-config\r\nВ конфигурации могут быть указаны следующие данные.\r\nТаблица 3. Перечень команд для управления\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 11 of 34\n\nНазвание\r\nзаписи\r\nТип\r\nструктуры\r\nзаписи\r\nОписание\r\nЗначение\r\nпо умолчанию\r\nping_interval_s Int32\r\nИнтервал между попытками опроса\r\nC2-сервера при отсутствии\r\nустановленного соединения\r\n5 секунд\r\nhibernate_s Int32\r\nВремя сна при неудачном подключении\r\nк C2-серверу (после использования\r\nдолжно быть повторно получено)\r\n0 секунд\r\nsession_timeout_s Int32 Время жизни сессии 3 секунды\r\nc2_timeout_s Int32\r\nМаксимальное время, в течение\r\nкоторого бэкдор подключен к одному\r\nсерверу\r\n43 200 секунд\r\n(12 часов)\r\njitter_s Int32\r\nВеличина случайного отклонения от\r\nping\r\n50\r\ntag String Тег жертвы «x»\r\ncert Binary\r\nОтпечаток сертификата (SHA-256),\r\nиспользуемого для подключения к\r\nсерверу\r\nОтсутствует\r\nsni String\r\nДоменное имя в сертификате,\r\nиспользуемом для подключения к C2-\r\nсерверу\r\ndns_s Int32\r\nВремя жизни кэшированных DNS-записей\r\n3600 секунд\r\ngw_p String\r\nПорт, на котором бэкдор будет\r\nработать в режиме прокси-сервера\r\nОтсутствует\r\ns_a\u003c№\u003e String Адрес C2-сервера\r\ns_p\u003c№\u003e String Порт C2-сервера\r\ns_c\u003c№\u003e String Протокол C2-сервера\r\nВажно отметить, что конфигурация бэкдора способна включать в себя до 32 записей об управляющих серверах (s_*\r\n\u003c№\u003e).\r\nПолучив необходимые данные конфигурации, бэкдор формирует уникальный идентификатор зараженного узла\r\n(agent_id). Алгоритм его вычисления аналогичен тому, который был описан в загрузчике. Сформированное значение\r\nслужит для однозначного определения устройства при последующем взаимодействии с C2-сервером.\r\n2.4.4.1. Сохранение данных\r\nСледующим шагом становятся извлечение и обработка данных, ранее полученных стилером. Для этого бэкдор\r\nвыполняет вызов rmdir с аргументом zarya_d_0. Данный вызов выполняется циклически — не более восьми раз\r\nподряд либо до момента, пока его ответы не перестанут содержать информацию. Следует отметить, что при каждом\r\nобращении с таким аргументом получаемые данные удаляются из памяти модуля, благодаря чему на каждом\r\nследующем шаге возвращается новая, еще не обработанная информация.\r\nВ ранних версиях модуля структура перехваченных данных содержит такие поля, как тип перехваченных данных,\r\nUID процесса (данные которого были перехвачены) и непосредственно сама конфиденциальная информация (см.\r\nрис. 22).\r\nРисунок 22. Структура данных, возвращаемых в пространство пользователя при вызове rmdir\r\nс аргументом zarya_d_0 (в ранних версиях)\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 12 of 34\n\nСохраненный и переданный в бэкдор UID используется в качестве ключа для получения полной информации\r\nо соответствующей учетной записи системы: сначала бэкдор пытается обратиться к системному файлу /etc/passwd,\r\nчтобы, используя полученный идентификатор, извлечь информацию о зарегистрированном в системе пользователе.\r\nЕсли доступ к файлу невозможен или нужная запись не обнаружена — UID преобразуется в строковый формат\r\nи используется для формирования запроса к демону кэширования учетных данных — Name Service Cache Daemon.\r\nНа основе полученных данных бэкдор формирует строку с информацией о пользователе, используя нулевой байт\r\n(0×00) в качестве разделителя.\r\nРисунок 23. Сохраняемая информация о пользователе (usrData)\r\nВ более поздних версиях модуля, помимо UID, злоумышленники также сохраняют EUID и SUID процесса.\r\nРисунок 24. Структура данных, возвращаемых в пространство пользователя при вызове rmdir\r\nс аргументом zarya_d_0 (в поздних версиях)\r\nДобавленные идентификаторы также используются в качестве ключей для извлечения информации о пользователе\r\nиз файла /etc/passwd. Однако в отличие от более ранних версий, бэкдор сохраняет не всю запись целиком, а только\r\nимя пользователя, соответствующее каждому из указанных идентификаторов (так как эти идентификаторы могут\r\nуказывать на разных пользователей). Полученные значения формируются в строку в специальном формате.\r\nРисунок 25. Сохраняемая информация о пользователе (usrData)\r\nСледует отметить, что более поздние версии бэкдора имеют обратную совместимость со старыми версиями модуля\r\n(сохраняющими исключительно UID). Такая возможность была добавлена вследствие того, что злоумышленники\r\nне предусмотрели обновление модуля ядра, в отличие от бэкдора (описание полезной нагрузки бэкдора\r\nпредставлено в табл. 5). Для таких случаев применяется специальный формат строки (рис. 26).\r\nРисунок 26. Поддержка предыдущих версий модуля (usrData)\r\nВ более поздних версиях модуля, перед тем как полученные от руткита данные будут записаны в журнал, бэкдор\r\nхеширует их с помощью алгоритма FNV-1: если такой хеш уже присутствует в памяти — увеличивается счетчик\r\nобращений и запись пропускается, а если его нет, то он добавляется в таблицу (или замещает наименее\r\nиспользуемый при переполнении). Таким образом злоумышленники исключают запись повторяющихся данных.\r\nЕсли же запись является уникальной — сведения о пользователе, перехваченные данные и информация о процессе-источнике кодируются в Base64 и записываются в журнал в порядке, определяемом типом перехваченных данных.\r\nКаждая запись начинается с временной метки, за которой следует сформированная CSV-строка, элементы которой\r\nразделены запятыми.\r\nРисунок 27. Данные для записи в zov_logs.txt\r\nПолучившаяся запись добавляется в конец файла /usr/share/zov_f/zov_logs.txt. Перед записью бэкдор обязательно\r\nпроверяет источник, с которым связаны перехваченные данные. Если оказывается, что обработке подвергается\r\nсобственный процесс ведения журнала — запись немедленно прекращается, что исключает саможурналирование\r\nи зацикливание записей.\r\n2.4.4.2. Взаимодействие с С2-сервером\r\nПосле того как перехваченные данные были записаны в журнал, процесс предпринимает попытку установить\r\nсоединение с C2-сервером для последующего взаимодействия. Для этого, при отсутствии активного соединения,\r\nбэкдор сравнивает текущее время с сохраненной временной меткой последнего успешного взаимодействия с C2-\r\nсервером:\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 13 of 34\n\n1. При отсутствии ранее установленных соединений бэкдор фиксирует текущее время в качестве отправной\r\nточки и инициирует цикл перебора доступных конфигураций подключения, полученных из ранее\r\nзаполненной секции .konfig. Для каждой записи перед попыткой установить соединение выполняется DNS-разрешение, по результатам которого в случае успеха сохраняется временная метка. Цикл ограничен 100\r\nитерациями и продолжается до тех пор, пока хотя бы одна из записей не будет успешно разрешена.\r\n2. Если метка последнего успешного взаимодействия с сервером установлена — вычисляет время, прошедшее\r\nс момента предыдущего сеанса связи. Если это значение не превышает c2_timeout_s, процесс пытается\r\nустановить соединение, используя последнюю использованную конфигурацию. В противном случае\r\nвыбирается следующая запись из секции .konfig, для которой проводится проверка актуальности DNS-разрешения: если с момента последнего запроса прошло менее dns_s секунд — используется ранее\r\nполученный адрес. Иначе инициируется новое разрешение, по завершении которого обновляется временная\r\nметка.\r\n3. В обоих случаях перед инициализацией соединения по очереди вызываются команды:\r\nrmdir zarya_1_\u003cPID\u003e — для скрытия текущего PID процесса; \r\nrmdir zarya_5_\u003cIP\u003e — для скрытия IP-адреса разрешенного C2-сервера.\r\nРисунок 28. Алгоритм соединения с C2-сервером\r\nДля обеспечения регулярного взаимодействия с сервером в бэкдоре предусмотрен механизм внутренних таймеров,\r\nконтролирующих периодичность обращений (см. структуру конфигурации). При инициализации соединения\r\nформируется пустое сообщение с заголовком, содержащим основную информацию о зараженной системе (команда\r\n4097).\r\nТаблица 4. Базовый заголовок для всех сообщений\r\nПоле структуры\r\nсообщения\r\nДобавляемые данные\r\nagent_id Уникальный идентификатор жертвы\r\nv Версия бэкдора \r\npv Версия модуля \r\np PID процесса\r\nlog_t Время, прошедшее с момента последней записи в zov_logs.txt\r\nuptime Общее время работы руткита\r\ncmd_id Заголовок отправленных данных\r\ncmd_type Номер выполненной команды\r\njitter_s\r\nВерхняя граница величины случайной задержки, добавляемой к\r\nосновному интервалу\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 14 of 34\n\ntag Тег жертвы\r\ndpi\r\nУникальный идентификатор сообщения, представляющий собой\r\nслучайное значение из диапазона [0, 999]\r\nВажно отметить, что каждое сообщение, отправляемое на C2-сервер, начинается с этого заголовка и дополняется\r\nрезультатами выполнения соответствующих команд (см. табл. 5).\r\nПосле формирования и отправки стартового сообщения бэкдор переходит в режим ожидания ответа от C2-сервера,\r\nвключающего тип команды (cmd_type), идентификатор передаваемых данных (cmd_id) и непосредственно полезную\r\nнагрузку. Форматы отправляемых и получаемых сообщений, а также описание функциональных возможностей\r\nбэкдора приведены в табл. 5.\r\nТаблица 5. Возможности бэкдора\r\nТип\r\nкоманды\r\n(cmd_type)\r\nОписание команды Комментарий\r\nСтруктура ответного\r\nсообщения\r\n4097\r\nОтправляет на C2-сервер\r\nблок данных с основной\r\nинформацией\r\nо зараженной системе\r\n(heartbeating)\r\nКаждое сообщение\r\nначинается с этого блока\r\nТолько заголовок (см.\r\nтабл. 4), в котором будет\r\nуказан тип выполненной\r\nкоманды\r\n4100\r\nПолучает и записывает\r\nновую версию бэкдора\r\nв /usr/share/zov_f/zov_latest\r\n—\r\n4101 Запускает реверс-шелл\r\nИспользуется\r\nпсевдотерминал (pty)\r\n4103\r\nУдаляет или добавляет (см.\r\nтабл. 2) локальный порт\r\nдля работы в режиме\r\nпрокси\r\nНомер порта и выбранная\r\nкоманда поступают с C2-\r\nсервера\r\n4098\r\nОтправляет содержимое\r\nфайла\r\n/usr/share/zov_f/zov_logs.txt\r\nПри превышении порога\r\nв 8 МБ файл очищается\r\n«logs» + \u003cсодержимое\r\nzov_logs.txt\u003e\r\n4096\r\nСобирает и отправляет\r\nсистемную информацию\r\nВызов команд с помощью\r\n/bin/sh:\r\n«cat /etc/*-release»;\r\n«uname -a»;\r\n«ip a»;\r\n«ifconfig»; \r\n«hostname»;\r\n«users»\r\nПолученные в результате\r\nданные группируются:\r\n«os» + \u003cdata\u003e\r\n«uname» + \u003cdata\u003e\r\n«ip1» + \u003cdata\u003e\r\n«ip2» + \u003cdata\u003e\r\n«hostname» +\r\n\u003cdata\u003e\r\n«tag» + \u003cdata\u003e\r\n4099\r\nВыполняет произвольную\r\nшелл-команду\r\nи отправляет ее результат\r\nКоманды выполняются\r\nс помощью /bin/sh\r\n«result» + \u003cstdout\u003e\r\n«output» + \u003cstderr\u003e\r\n4102 Обновляет внутренние\r\nтаймеры Обновляемые параметры:\r\n«c2_timeout_s»;\r\n«ping_interval_s»;\r\n«session_timeout_s»;\r\n«jitter_s»;\r\n«dns_s»;\r\n«hibernate_s»\r\nЗначения\r\nконфигурационных\r\nпараметров:\r\n«c2_timeout_s»;\r\n«ping_interval_s»;\r\n«jitter_s»;\r\n«session_timeout_s»;\r\n«dns_s»;\r\n«tag»;\r\n«cert»;\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 15 of 34\n\n«sni»;\r\n«s_a\u003c№\u003e»;\r\n«s_p\u003c№\u003e»;\r\n«s_t\u003c№\u003e»;\r\n2.4.4.3. Используемые сертификаты\r\nВ ходе анализа доменов, использовавшихся для C2-серверов, было установлено, что ряд из них применяет весьма\r\nпримечательный SSL-сертификат (рис. 29). Он интересен тем, что в качестве Issuer и Subject злоумышленники\r\nуказали организацию «FSB» и адрес «2 Bolshaya Lubyanka Street».\r\nРанее, в ходе одного из расследований, мы встречали использование данного сертификата группировкой (Ex)Cobalt.\r\nПоэтому мы связываем руткит PUMAKIT с данной группировкой и рассматриваем его как часть ее инструментария.\r\nРисунок 29. Используемый сертификат\r\nДанный сертификат был использован следующими С2-серверами:\r\ncckitsfrp1.n3x1lo.pro \r\nqdkitsorp2.n3x1lo.pro \r\ncddcvesfhfp1.wris.monster \r\ndeefveskiip2.wris.monster\r\ncumfpo90sing.agddns.net\r\nprocdia42ecte.agddns.net\r\nviedeu98.agddns.net\r\nlaipros50.agddns.net\r\nfira24sonstablee.agddns.net\r\nchronback49in.duckdns.org\r\nЗлоумышленники маскируют сертификат, используемый для шифрования взаимодействия бэкдора с C2-сервером,\r\nпри помощи Server Name Indication (SNI), расширения протокола TLS. Настоящий сертификат выдается по имени,\r\nуказанному в поле sni конфигурации бэкдора. Таким образом, они могут избежать обнаружения других C2-серверов\r\nпо сертификату и получения других сертификатов без знания имени сервера из конфигурации. Сертификат является\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 16 of 34\n\nзаглушкой, которая возвращается сервером в том случае, если не указано имя сервера в запросе. Она\r\nне используется при взаимодействии с C2-сервером.\r\nОтпечаток сертификата (SHA-256) и соответствующие им SNI, использовавшиеся злоумышленниками\r\nи обнаруженные нами в конфигурационной секции, представлены в таблице 6.\r\nТаблица 6. Информация о сертификатах\r\nSHA-256 SNI\r\n29AD1A06DCA85041E793A8BF2F966B6A7CF3F35904FB3E8648A7F97D9A211F8D run.sssddd.org\r\n0B3B6E06CB7B6C25066B0DAEA6CF2D6EEA57D33FB58EF66EBAEF2107BA2A92B0 zfs.wefwe.net\r\n2.4.5. Эволюция\r\nПомимо руткита PUMAKIT, группировка (Ex)Cobalt регулярно использует и другой инструмент — Facefish,\r\nчастично повторяющий его функциональность. В ходе расследований инцидентов наши специалисты неоднократно\r\nфиксировали случаи одновременного применения двух этих инструментов внутри одной и той же инфраструктуры.\r\nСитуация дополнительно усложняется тем, что ранее злоумышленники использовали еще один руткит — Kitsune,\r\nкоторый был подробно изучен нашими коллегами из BI.ZONE.\r\nСопоставив возможности всех трех инструментов и временные метки их появления, можно четко проследить,\r\nкак эволюционировал инструментарий группировки с течением времени.\r\nТаблица 7. Сравнение инструментов\r\n  Facefish Kitsune PUMAKIT\r\nФункции\r\nКража учетных\r\nданных SSH-пользователей через\r\nперехват функций\r\nssh/sshd\r\nСбор и отправка\r\nсведений об узле\r\nИсполнение\r\nпроизвольных\r\nкоманд\r\nРеверс-шелл\r\nВ новых версиях\r\nбыл также\r\nреализован\r\nперехват функции\r\ngetdelim: при\r\nчтении\r\nконфигурационного\r\nфайла\r\n/etc/ssh/sshd_config\r\nбудет добавляться\r\nдополнительный\r\nпорт, на котором\r\nсервис SSH\r\nпринимает\r\nвходящие\r\nсоединения\r\nКража\r\nразличных\r\nучетных\r\nданных,\r\nвключая\r\nданные SSH-пользователей \r\nСбор\r\nи отправка\r\nсведений\r\nоб узле\r\nИсполнение\r\nпроизвольных\r\nкоманд\r\nРеверс-шелл\r\nПолучение\r\nинформации\r\nпутем\r\nвыполнения\r\nзаготовленных\r\nкоманд\r\nКража различных\r\nучетных данных,\r\nвключая данные\r\nSSH-пользователей \r\nСбор и отправка\r\nсведений об узле\r\nИсполнение\r\nпроизвольных\r\nкоманд\r\nРеверс-шелл\r\nПолучение\r\nинформации путем\r\nвыполнения\r\nзаготовленных\r\nкоманд\r\nБэкдор отделен\r\nот руткита,\r\nпоявилась\r\nвозможность\r\nобновление\r\nбэкдора\r\nПроксирование\r\nтрафика\r\nДобавление\r\nсобственных\r\nключей в файл\r\nauthorized_keys\r\nПервое\r\nобнаружение\r\nФевраль 2021 г. Февраль 2022 г. Март 2024 г.\r\nТип руткита Userland Userland Kernel\r\nКомпоненты Дроппер Userland-руткит\r\nДроппер\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 17 of 34\n\nUserland-руткит\r\nс функциями\r\nбэкдора и стилера\r\nс функциями\r\nбэкдора\r\nи стилера\r\nЗагрузчик LKM-руткита\r\nLKM-руткит\r\nс функциями\r\nстилера\r\nБэкдор\r\nСпособ\r\nзакрепления\r\nв системе\r\nДроппер сохраняет\r\nuserland-руткит в файл\r\n/lib64/libs.so\r\nи прописывает его\r\nв /etc/ld.so.preload\r\nUserland-руткит\r\nсохраняется\r\nзлоумышленниками в\r\nфайл\r\n/lib64/libselinux.so\r\n(как в рутките\r\nAzazel) и\r\nпрописывается\r\nв /etc/ld.so.preload\r\nПодмена системного\r\nсервиса cron\r\nСпособ\r\nактивации\r\nПри вызове функции\r\nbind() процессом sshd\r\nПри вызове функции\r\nbind() процессом sshd\r\nАктивируется сразу при\r\nстарте подмененного\r\ncron-сервиса\r\nФормат\r\nпакетов для\r\nобщения с\r\nC2-сервером\r\nСобственный формат\r\nпакетов\r\nBSON BSON\r\nХранение\r\nконфигурации\r\nХранится в «хвосте»\r\nдроппера\r\nХранится в файле\r\n/etc/config__hhide\r\nХранится в виде ELF-секции руткита, в\r\nнекоторых версиях в\r\nфайле\r\n/usr/share/zov_f/zov_config\r\nМеханизмы\r\nскрытности\r\nОтсутствуют\r\nРеализован механизм\r\nскрытия записи\r\nв /etc/ld.so.preload.\r\nСкрывает файлы,\r\nпроцессы и сетевые\r\nсоединения,\r\nсвязанные с ними,\r\nна уровне\r\nпользователя через\r\nперехват функций\r\nlibc\r\nПолноценный руткит\r\nуровня ядра. Скрывает:\r\nпроцессы,\r\nфайлы и каталоги,\r\nсетевые\r\nсоединения\r\nПри этом мы считаем, что Facefish не был разработан группировкой (Ex)Cobalt, а был приобретен на черном рынке\r\nили получен в результате утечки. В пользу данного предположения говорит тот факт, что используемая в Fasefish\r\nконфигурация расположена в конце файла и не упаковывается UPX, а просто зашифровывается (рис. 30). Таким\r\nобразом, не имея исходного кода, злоумышленники могли беспрепятственно менять параметры конфигурации.\r\nРисунок 30. Расположение конфигурации в Facefish\r\nОднако предоставляемой Facefish функциональности, по всей видимости, оказалось недостаточно, вследствие чего\r\nгруппировка разработала собственный руткит — Kitsune, во многом наследующий возможности своего\r\nпредшественника. Примечательно, что в Facefish и в Kitsune запуск бэкдора привязан к вызову функции bind(), что\r\nуказывает на прямую преемственность архитектурных решений.\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 18 of 34\n\nВ то же время имеются все основания предполагать, что Kitsune был создан на основе исходного кода руткита\r\nAzazel: помимо схожих архитектурных решений, шифрование строк, заложенное в Azazel (XOR с байтом 0xFE),\r\nсохранилось в Kitsune и в дальнейшем перешло в его следующую версию (рис. 31 и 32).\r\nРисунок 31. Механизм шифрования строк в Azazel\r\nРисунок 32. Механизм расшифровки строк в бэкдоре PUMAKIT\r\nПереход от руткита Kitsune к PUMAKIT не был резким: между ними существовала промежуточная версия,\r\nизвестная как Megatsune. И хотя Megatsune по-прежнему оставался руткитом уровня пользователя, а его архитектура\r\nи логика напрямую наследовали решения Kitsune — именно на Megatsune злоумышленники начали тестировать\r\nбэкдор, позднее реализованный в PUMAKIT, сохранив ту же логику работы, набор команд и структуру\r\nконфигурации. \r\nНа основании проведенного анализа мы объединяем эти три инструмента в одно семейство — PUMA,\r\nпредполагаемая цепочка эволюции которого представлена на рис. 33.\r\nРисунок 33. Эволюция семейства PUMA\r\n2.4.6. Методы обнаружения и борьбы с руткитом в своей системе\r\nВ ходе анализа мы выявили в LKM-модуле важную уязвимость, проэксплуатировав которую можно обнаружить\r\nустановленный модуль PUMAKIT через его же интерфейс: руткит не проверяет, какой именно процесс вызывает\r\nпереопределенный rmdir для выполнения команд. Таким образом, вызвав rmdir с определенными аргументами,\r\nможно однозначно определить присутствие руткита в системе.\r\nВы можете легко проверить свою систему на наличие руткит-модуля, воспользовавшись нашим скриптом\r\nдля детекта.\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 19 of 34\n\nimport ctypes\r\nimport subprocess\r\nimport os\r\n \r\nSYS_rmdir = 84\r\n \r\nbuffer_size = 16\r\npath_buf = ctypes.create_string_buffer(buffer_size)\r\nctypes.memmove(path_buf, b\"zarya.u\\0\", 7)\r\n \r\nlibc = ctypes.CDLL(\"libc.so.6\", use_errno=True)\r\nret = libc.syscall(SYS_rmdir, path_buf)\r\n \r\ntry:\r\n proc = subprocess.Popen(\r\n \"lsmod | grep audit\",\r\n shell=True,\r\n stdout=subprocess.PIPE,\r\n stderr=subprocess.PIPE,\r\n universal_newlines=True\r\n)\r\n output, _ = proc.communicate()\r\n \r\n if output.strip():\r\n path_buf2 = ctypes.create_string_buffer(buffer_size)\r\n ctypes.memmove(path_buf2, b\"zarya.t.0\\0\", 9)\r\n ret2 = libc.syscall(SYS_rmdir, path_buf2)\r\n \r\n if ret2 == 0:\r\n print(f\"Puma detected on this machine, module info:\\n{output.strip()}\")\r\n else:\r\n print(\"Puma wasn't detected at this machine\")\r\n else:\r\n print(\"Puma wasn't detected at this machine\")\r\n \r\nexcept Exception as e:\r\n print(\"Error:\", e)\r\nЛистинг 3. Скрипт для обнаружения скрытого модуля руткита\r\nУязвимость трудноустранима, поскольку злоумышленники, используя возможности бэкдора, могут обновлять лишь\r\nего код, но не код загруженного в систему модуля ядра. Для этого потребуется значительно больше усилий.\r\nКроме того, чтобы убедиться в отсутствии вмешательства руткита в файл authorized_keys, выполните в своей\r\nсистеме следующий скрипт.\r\nimport difflib\r\nimport os\r\nimport shutil\r\nimport subprocess\r\nimport sys\r\nimport tempfile\r\nfrom pathlib import Path\r\n \r\ndef read_with_binary(binary_path: str, target_file: str) -\u003e bytes:\r\n try:\r\n res = subprocess.run(\r\n [binary_path, target_file],\r\n stdout=subprocess.PIPE,\r\n stderr=subprocess.DEVNULL,\r\n check=True\r\n )\r\n return res.stdout\r\n except subprocess.CalledProcessError:\r\n print(f\"Ошибка: неудалосьзапустить{binary_path}\", file=sys.stderr)\r\n sys.exit(1)\r\n \r\ndef detect_in_memory_modification(authorized_keys: Path):\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 20 of 34\n\nclean = read_with_binary(\"/bin/cat\", str(authorized_keys))\r\n \r\n with tempfile.TemporaryDirectory() as td:\r\n fake_ssh = Path(td) / \"ssh\"\r\n shutil.copy2(\"/bin/cat\", fake_ssh)\r\n fake_ssh.chmod(0o755)\r\n modified = read_with_binary(str(fake_ssh), str(authorized_keys))\r\n \r\n if clean != modified:\r\n print(\"Обнаружено вмешательство руткита!\")\r\n clean_lines = clean.decode(errors=\"ignore\").splitlines()\r\n mod_lines = modified.decode(errors=\"ignore\").splitlines()\r\n diff = difflib.ndiff(clean_lines, mod_lines)\r\n added = [\r\n line[2:]\r\n for line in diff\r\n if line.startswith('+ ') and line[2:].strip() != ''\r\n ]\r\n if added:\r\n print(\"\\nДобавленныестроки:\")\r\n for l in added:\r\n print(f\" + {l}\")\r\n else:\r\n print(\"Изменения обнаружены, но добавленных строк не найдено.\")\r\n else:\r\n print(\"Не обнаружено подмены содержимого authorized_keys при запуске ssh-процесса.\")\r\n \r\nif __name__ == \"__main__\":\r\n path = sys.argv[1] if len(sys.argv) \u003e 1 else os.path.expanduser(\"~/.ssh/authorized_keys\")\r\n ak = Path(path)\r\n if not ak.is_file():\r\n print(f\"Ошибка: файл{ak} ненайден.\", file=sys.stderr)\r\n sys.exit(1)\r\n detect_in_memory_modification(ak)\r\nЛистинг 4. Скрипт для проверки возможной подмены содержимого файла authorized_keys\r\nСкрипт последовательно читает файл authorized_keys через настоящий /bin/cat и через его копию, переименованную\r\nв ssh, сравнивает оба вывода и при обнаружении различий выводит только те строки (ключи), которые руткит\r\n«подмешивает» на лету.\r\nОтключение локального файрвола, реализованное в рутките, наглядно демонстрирует, насколько легко\r\nзлоумышленники могут обойти встроенные средства защиты, если те функционируют исключительно внутри\r\nцелевой системы. Чтобы противостоять подобным атакам, критически важно выносить фильтрацию трафика\r\nза пределы потенциально скомпрометированной среды. Одним из надежных решений может стать использование\r\nвнешнего межсетевого экрана нового поколения, например PT NGFW — продукта, который не только отслеживает\r\nи блокирует подозрительный трафик на уровне приложений, но и позволяет своевременно выявлять сложные\r\nсетевые атаки, включая попытки обхода традиционных механизмов защиты.\r\n2.5. Octopus и его щупальца\r\nЕще одним заслуживающим внимания элементом инструментария группы является реализованный на Rust\r\nинструмент Octopus, который может применяться для повышения привилегий и закрепления\r\nв скомпрометированной Linux-системе.\r\n2.5.1. Локальные крейты\r\nВ реализации инструмента применялся ряд недоступных публично крейтов1. Приведем их краткое описание.\r\n1\r\n Crate, контейнер — модуль, обособленная единица компиляции в языке Rust.\r\nТаблица 8. Локальные крейты Octopus\r\nКрейт Назначение\r\noctopus\r\nОбертка для управления функциональностью, предоставляемой крейтами,\r\nприведенными ниже\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 21 of 34\n\noctorepl Реализация read–eval–print loop (REPL) на основе крейта clap\r\noctosys Реализация взаимодействия с операционной системой\r\noctoproc Реализация взаимодействия с оболочкой во время запуска эксплойтов\r\noctolog Реализация журналирования\r\nleech\r\nРеализация библиотеки патчинга на основе крейта gimli, также присутствует\r\nвозможность сетевого взаимодействия\r\nbaron\r\nРеализация CVE-2021-3156 (уязвимость heap overflow в утилите sudo),\r\nпереписанная на Rust\r\nroute4-filter\r\nРеализация CVE-2022-2588 (уязвимость double free в функции route4_change),\r\nпереписанная на Rust\r\nlooney_tunables\r\nРеализация CVE-2023-4911 (уязвимость buffer overflow в ld.so), переписанная\r\nна Rust\r\ninfect Реализация заражения исполняемых файлов\r\ngtfo Реализация поиска GTFOBins\r\n2.5.2. Поток управления\r\nПоток управления базируется на локальном крейте octorepl, который основан на крейте clap и реализует\r\nсобственный REPL. Если Octopus будет запущен без аргументов, откроется интерактивная оболочка — REPL.\r\nПри вызове команды help из оболочки будет выведен список команд и их описание.\r\nРисунок 34. Список команд, доступных в REPL\r\nВ режиме REPL нет возможности запускать часть команд, которые доступны только в режиме запуска через\r\nинтерфейс командной строки.\r\nРисунок 35. Список команд, доступных в командной строке\r\nЕсли в режиме REPL попытаться ввести команду, которая доступна только в командной строке, оболочка подсветит\r\nтакую команду красным цветом, в ином случае зеленым. Стоит отметить, что в оболочке есть автодополнение\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 22 of 34\n\nкоманд, а также что за дополнительной информацией можно обратиться на внутреннюю вики.\r\nРисунок 36. Отображение недоступных команд в REPL\r\n2.5.3. Команды\r\nИнструмент Octopus содержит следующий набор команд.\r\nТаблица 9. Команды Octopus\r\nКоманда Описание\r\ndetect\r\nПроверяет, какие эксплойты повышения привилегий работают в системе.\r\nЭксплойты берутся из команды do\r\nsystem-id\r\nГенерирует идентификатор жертвы посредством сбора информации о системе\r\nи хеширования ее алгоритмом MD5\r\ndo\r\nЗапуск набора эксплойтов для повышения привилегий:\r\npwnkit — запуск эксплойта CVE-2021-4034;\r\nlooney-tunables — запуск эксплойта CVE-2023-4911;\r\nbaron — запуск эксплойта CVE-2021-3156;\r\nroute4-filter — запуск эксплойта CVE-2022-2588\r\nsu\r\nСканирование системы для обнаружения компонентов, содержащих уязвимости,\r\nпозволяющие повысить привилегии. Использует команду detect\r\nglue\r\nМодификация легитимных утилит, установленных в системе, для обеспечения\r\nзапуска вшитой полезной нагрузки\r\nnetpatch Патчинг бинарных файлов, для перехвата соединения и восстановления себя\r\nnetpatch-update\r\nОбновление бинарных файлов, ранее пропатченных с помощью команды netpatch\r\ncheck-bin Проверка бинарного файла на роль кандидата для заражения\r\ngtfo Поиск GTFOBins\r\nhelp Вывод информации о командах\r\n2.5.4. Полезная нагрузка\r\nOctopus несет в себе 4 полезных нагрузки:\r\nРуткит libzst.so.0 (мы дали название ему Spawner). Руткит для межпроцессорного взаимодействия.\r\nРуткит libsockopt.so.1 (мы дали название ему TransMarker). Руткит для межпроцессорного взаимодействия.\r\nРуткит libsockopt.so.2 (mosquito). Руткит для межпроцессорного взаимодействия.\r\nБэкдор mycelium. Инструмент для удаленного управления зараженной системой.\r\nПолезная нагрузка находятся в секции .rodata в сериализованном (CBOR) и сжатом (zstd) виде.\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 23 of 34\n\nРисунок 37. Вшитая полезная нагрузка\r\nimport zstandard\r\nimport cbor2\r\nimport sys\r\nimport json\r\n \r\ndctx = zstandard.ZstdDecompressor()\r\n \r\nwith open(sys.argv[1], 'rb') as ifh, open(sys.argv[1] + '.dec', 'wb') as ofh:\r\n  dctx.copy_stream(ifh, ofh)\r\n \r\nwith open(sys.argv[1] + '.dec', 'rb') as fp, open(sys.argv[1] + '.des', 'w') as fw:\r\n  obj = cbor2.load(fp)\r\n  json.dump(obj, fw, indent = 4, sort_keys = True, default = repr)\r\nЛистинг 5. Скрипт на Python для декодирования полезной нагрузки\r\nПосле десериализации будет получен JSON-объект, хранящий тело полезной нагрузки, а также индекс и название\r\n(при наличии).\r\nРисунок 38. Разархивированная и десериализованная полезная нагрузка\r\n2.5.5. Команда glue\r\nКоманда отвечает за модификацию легитимных утилит, установленных в системе, для обеспечения запуска вшитой\r\nполезной нагрузки.\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 24 of 34\n\nРисунок 39. Параметры команды glue\r\nДля использования команды должна быть указана как минимум одна из опций --bin, --bin-network или --package-managers. В противном случае будет выведено сообщение об ошибке.\r\nРисунок 40. Запуск команды glue без параметров\r\nДля каждого переданного команде glue файла будет выполнена проверка на наличие библиотеки libc.so.6 в\r\nзависимостях. Более того, Octopus должен иметь доступ к атрибуту security.selinux (если такой имеется\r\nу библиотеки).\r\nВ случае успешных проверок, в зависимости от того, передавался ли аргумент --glue, Octopus попытается либо\r\nсчитать файл (если в glue передавался путь до библиотеки, при этом важно, чтобы путь начинался с символа /), либо\r\nнайти его по имени среди встроенных библиотек.\r\nЕсли glue не удалось по какой-либо причине найти, Octopus выдаст сообщение об ошибке.\r\nДалее Octopus сериализует конфигурацию, необходимую для работы руткитов, используя последовательно алгоритм\r\nCBOR и сжатие с помощью zstd. Сериализацию проходят параметры, переданные команде в качестве аргументов.\r\nЗатем Octopus записывает необходимые руткиты в каталог, в котором находится libc.so.6. В каждом записываемом\r\nрутките дополнительно создается секция с типом SHT_NOTE, в которую помещается сериализованная ранее\r\nконфигурация.\r\nКрейт, который непосредственно отвечает за патчинг, назван leech.\r\nРисунок 41. Название крейта, в котором реализована функция патчинга\r\nСуть используемого метода патчинга состоит в следующем. В выбранный легитимный исполняемый файл\r\nвнедряется дополнительная зависимость от вредоносного модуля, который является библиотекой .so.\r\nОпишем механику внедрения зависимости. Leech пересобирает секции исходного бинарного файла, добавляя\r\nв секцию .dynamic (содержит сведения о динамически загружаемых модулях) ссылку на строку с именем\r\nвнедряемого вредоносного модуля (дописывается в секцию .dynstr). Вначале leech парсит исходный бинарный файл,\r\nсобирая информацию о его сегментах и секциях. В первую очередь это секция .interp (специальная секция, которая\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 25 of 34\n\nсодержит путь к нужному компоновщику), а также секции с типом SHT_NOTE. Секции SHT_NOTE, как правило,\r\nнесут дополнительную информацию о файле, однако существует такая секция с именем .note.ABI-tag, которая,\r\nсогласно спецификации, должна присутствовать в каждом ELF-файле.\r\nРисунок 42. Сбор информации о бинарном файле\r\nДалее leech ищет индекс секции, содержащей таблицу символов (данная секция называется .dynstr и имеет тип\r\nSHT_STRTAB), и индекс секции, в которой находится информация, необходимая для динамического связывания\r\n(секция имеет имя .dynamic и тип SHT_DYNAMIC).\r\nЗатем, в соответствии с собранной информацией, создается новый сегмент с типом PT_LOAD, куда помещаются\r\nсобранные секции. А в секции .dynamic дополнительно создается поле с типом DT_NEEDED и указателем на строку\r\nс именем руткита, которое предварительно дописывается в секцию .dynstr.\r\nТаким образом, при запуске пропатченного файла компоновщик автоматически подгрузит руткит (так как это\r\nнеобходимая зависимость). В результате модификаций создается временный файл с именем \u003coriginal_parent_dir\u003e/.~\r\n\u003coriginal_name\u003e.tmp, куда записывается пропатченный ELF с учетом перестроения.\r\nВ случае корректного запуска данный временный файл переименовывается в оригинальный (с помощью системного\r\nвызова rename), тем самым замещая его.\r\n2.5.6. Команда netpatch\r\nКоманда netpatch внедряет полезную нагрузку в один из сервисов, функционирующих в системе.\r\nРисунок 43. Параметры команды netpatch\r\nВнедрение происходит через модификацию исполняемого файла целевого сервиса. В качестве полезной нагрузки\r\nпо умолчанию применяется бэкдор mycelium. Также есть возможность использовать в качестве полезной нагрузки\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 26 of 34\n\nвстроенный бэкдор mosquito (libsockopt.so.2). Для его применения необходимо указать опцию --mosquito\r\nпри выполнении команды netpatch.\r\nПри использовании опции --mosquito значительно уменьшаются возможности Octopus: нельзя использовать опцию -\r\n-connect-to и не поддерживаются спавнеры (например, сервис cron) и пакетные менеджеры.\r\nВ опцию --connect-to передается значение в формате host:port, к этому порту будет подключаться полезная нагрузка\r\n(в данном случае mycelium).\r\nСлово «спавнер» в контексте Octopus означает программу-демон, пропатченную библиотекой libzst.so.0, которая\r\nс определенной периодичностью (согласно справке к команде glue, в интервале от --interval до --interval-spread)\r\nбудет запускать нагрузку (в данном случае это mycelium).\r\nСтоит отметить, что бэкдор mycelium работает на дистрибутивах, версия ядра которых не ниже 2.6.27.\r\nРисунок 44. Строка о минимальной версии ядра для запуска mycelium\r\nПри запуске команды netpatch будет выведена информация о доступных в системе сетевых интерфейсах, перебор\r\nкоторых осуществляется с помощью инструмента Netlink.\r\nРисунок 45. Доступные сетевые интерфейсы\r\nВыводится также информация об известных и неизвестных сервисах, а также о названиях пакетов, которые\r\nих поставляют.\r\nРисунок 46. Запущенные сервисы\r\nПод известными подразумеваются следующие сервисы: \r\ncron (crond),\r\nsshd,\r\npostgres,\r\nnginx,\r\napache,\r\nlighttpd,\r\nmariadb.\r\nКонтроль над тем, что необходимо пропатчить, осуществляется либо с помощью опции —filter, либо с помощью\r\nнепосредственного считывания строки во время запуска команды.\r\nЕсли команда netpatch запущена c полезной нагрузкой по умолчанию (mycelium), то при указании опции —connect-to для данной нагрузки кодируется конфигурация и генерируется TOKEN (см. рис. 49).\r\nНачальная конфигурация для mycelium представляет собой набор аргументов, с которыми он запускается.\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 27 of 34\n\nРисунок 47. Команды mycelium и их описание\r\nКонфигурация сериализуется алгоритмом CBOR. Затем генерируется случайная гамма размером 5 байт\r\nи сериализованная конфигурация гаммируется. К зашифрованной конфигурации дописывается сгенерированная\r\nгамма, и все это кодируется алгоритмом Base64.\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 28 of 34\n\nРисунок 48. Шифрование конфигурации для mycelium\r\nЗакодированная конфигурация mycelium будет дописана в секцию с конфигурацией руткита, который записывается\r\nкомандой glue.\r\nРисунок 49. Передаваемая конфигурация для mycelium\r\nПосле того как все переданные сервисы были пропатчены, Octopus заменяет MD5-хеш-суммы пропатченных\r\nсервисов. Например, для сервиса openssh в системе с dpkg изменится сумма в файле /var/lib/dpkg/info/openssh-server.md5sum.\r\nДля запуска mycelium Octopus вызывает fork и в процессе-потомке использует вызов execveat.\r\nСтоит заметить, что вшитая полезная нагрузка на диск не записывается. Она хранятся в скрытом файле с именем\r\nkernel, который создается с помощью системного вызова memfd_create.\r\nМатрица MITRE ATT\u0026CK\r\nID ТЕХНИКА ОПИСАНИЕ\r\nPERSISTENCE\r\nT1554\r\nCompromise Host Software\r\nBinary\r\n(Ex)Cobalt подменяет системный бинарный файл cron\r\nна вредоносный загрузчик руткита PUMAKIT,\r\nсохраняя внешний вид и функции штатного демона.\r\n(Ex)Cobalt модифицирует легитимные исполняемые\r\nфайлы на скомпрометированных системах, внедряя\r\nкод запуска руткита с помощью утилиты Octopus\r\nT1547.006\r\nBoot or Logon Autostart\r\nExecution: Kernel Modules\r\nand Extensions\r\n(Ex)Cobalt устанавливает модуль ядра (audit) после\r\nпроверки необходимых условий — отключенного\r\nSecure Boot и совместимости символов ядра,\r\nиспользуя wpn\r\nT1574.006\r\nHijack Execution Flow:\r\nDynamic Linker Hijacking\r\n(Ex)Cobalt использует загруженный модуль ядра\r\n(audit) для загрузки бэкдора, устанавливая\r\nв LD_PRELOAD значение /lib64/libs.so\r\nT1098.004\r\nAccount Manipulation: SSH\r\nAuthorized Keys\r\n(Ex)Cobalt использует загруженный модуль ядра\r\n(audit) для перехвата системных вызовов open и read,\r\nчтобы при обращении легитимных процессов\r\n(например, sshd) к файлу ~/.ssh/authorized_keys\r\nдобавлять в возвращаемый буфер собственный SSH-ключ. Сам файл на диске остается неизменным,\r\nно с точки зрения sshd у учетной записи появляется\r\nеще один авторизованный ключ, что создает скрытую\r\nи устойчивую точку входа по SSH\r\nEXECUTION\r\nT1059.004\r\nCommand and Scripting\r\nInterpreter: Unix Shell\r\n(Ex)Cobalt использует бэкдор (libs.so) для запуска\r\nи выполнения произвольных команд через /bin/sh\r\nPRIVILEGE ESCALATION\r\nT1068\r\nExploitation for Privilege\r\nEscalation\r\n(Ex)Cobalt применяет эксплойты для уязвимостей\r\nCVE-2021-3156 (heap overflow в утилите sudo), CVE-2022-2588 (double free в функции route4_change), CVE-2023-4911 (buffer overflow в ld.so) для повышения\r\nпривилегий\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 29 of 34\n\nID ТЕХНИКА ОПИСАНИЕ\r\nT1548.001\r\nAbuse Elevation Control\r\nMechanism: Setuid and\r\nSetgid\r\n(Ex)Cobalt использует загруженный модуль ядра\r\n(audit) для модификации структуры cred вызывающего\r\nrmdir zarya_0_0 процесса, устанавливая\r\nUID/GID/EUID/EGID в 0 и, тем самым, повышая его\r\nпривилегии до root\r\nDEFENSE EVASION\r\nT1014 Rootkit\r\n(Ex)Cobalt использует загруженный модуль ядра\r\n(audit) для перехвата и модификации системных\r\nвызовов и ключевых функций ядра. Это позволяет\r\nскрывать собственные файлы, процессы, сетевые\r\nсоединения и установленный модуль, маскируя\r\nприсутствие в системе\r\nT1036.005\r\nMasquerading: Match\r\nLegitimate Name or\r\nLocation\r\n(Ex)Cobalt подменяет легитимный cron (/usr/sbin/cron)\r\nна дроппер PUMAKIT, сохраняя ожидаемое поведение\r\nсервиса\r\nT1027.009\r\nObfuscated Files or\r\nInformation: Embedded\r\nPayloads\r\n(Ex)Cobalt использует утилиту Octopus, которая\r\nхранит полезную нагрузку в секции данных своего\r\nисполняемого файла\r\nT1027.011\r\nObfuscated Files or\r\nInformation: Fileless Storage\r\n(Ex)Cobalt использует анонимные файловые\r\nдескрипторы (memfd) для запуска ключевых\r\nкомпонентов ВПО PUMAKIT (например, /memfd:tgt\r\nи /memfd:wpn).\r\nПолезная нагрузка, устанавливаемая инструментом\r\nOctopus, не записывается на диск и хранится\r\nв скрытом файле с именем kernel, который создается\r\nс помощью системного вызова memfd_create\r\nT1027.015\r\nObfuscated Files or\r\nInformation: Compression\r\n(Ex)Cobalt использует утилиту Octopus, которая\r\nсжимает полезную нагрузку алгоритмом zstd\r\nT1036 Masquerading\r\n(Ex)Cobalt использует утилиту Octopus, которая\r\nсохраняет экземпляры полезной нагрузки в файлы,\r\nимена которых имитируют названия легитимных\r\nкомпонентов: libzst.so.0, libsockopt.so.1, libsockopt.so.2\r\nT1218\r\nSystem Binary Proxy\r\nExecution\r\n(Ex)Cobalt использует утилиту Octopus, которая имеет\r\nфункцию поиска установленных в системе утилит\r\nGTFOBins\r\nT1564.001\r\nHide Artifacts: Hidden Files\r\nand Directories\r\n(Ex)Cobalt использует загруженный модуль ядра\r\n(audit) для перехвата вызова getdents и последующей\r\nмодификации его результатов, скрывая артефакты\r\nPUMAKIT из результатов чтения каталогов, делая\r\nих невидимыми для ls и аналогичных утилит\r\nT1564 Hide Artifacts\r\n(Ex)Cobalt использует загруженный модуль ядра\r\n(audit) для фильтрации вывода информации\r\nо процессах, модулях и сетевых соединениях, скрывая\r\nих артефакты от стандартных средств мониторинга\r\nT1562.004\r\nImpair Defenses: Disable or\r\nModify System Firewall\r\n(Ex)Cobalt использует загруженный модуль ядра\r\n(audit) для перехвата nf_hook_slow, что позволяет\r\nPUMAKIT обходить фильтрацию netfilter/iptables\r\nи сохранять связь с C2-сервером, нивелируя работу\r\nвстроенного файрвола для собственных соединений\r\nT1562.001\r\nImpair Defenses: Disable or\r\nModify Tools\r\n(Ex)Cobalt использует загруженный модуль ядра\r\n(audit) для перехвата функций SELinux, препятствуя\r\nприменению политик к важным для руткита\r\nпроцессам и соединениям, фактически отключая\r\nмеханизмы контроля доступа и аудита\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 30 of 34\n\nID ТЕХНИКА ОПИСАНИЕ\r\nCREDENTIAL ACCESS\r\nT1056.004\r\nInput Capture: Credential\r\nAPI Hooking\r\n(Ex)Cobalt использует загруженный модуль ядра\r\n(audit) для перехвата системных вызовов read и write,\r\nанализируя передаваемые буферы на наличие искомых\r\nподстрок\r\nDISCOVERY\r\nT1082\r\nSystem Information\r\nDiscovery\r\n(Ex)Cobalt используют бэкдор (libs.so) для сбора\r\nсведений об операционной системе, ядре, архитектуре\r\nи окружении, передавая их оператору для оценки узла\r\nLATERAL MOVEMENT\r\nT1021.004 Remote Services: SSH\r\n(Ex)Cobalt использует загруженный модуль ядра\r\n(audit) для скрытного добавления SSH-ключа, что\r\nпозволяет злоумышленникам входить по SSH\r\nпод видом легитимного пользователя и использовать\r\nузел в качестве точки дальнейшего продвижения\r\nв инфраструктуре жертвы\r\nCOLLECTION\r\nT1074.001\r\nData Staged: Local Data\r\nStaging\r\n(Ex)Cobalt агрегирует украденные пароли, ключи\r\nи другие данные в локальном файле (zov_logs.txt),\r\nслужащим буфером перед эксфильтрацией\r\nCOMMAND AND CONTROL\r\nT1071.001\r\nApplication Layer Protocol:\r\nWeb Protocols\r\n(Ex)Cobalt использует бэкдор (libs.so) для установки\r\nTLS-соединения с C2-сервером и последующей\r\nпередачи бинарных сообщений в формате BSON\r\nT1571 Non-Standard Port\r\n(Ex)Cobalt использует для C2-серверов нетипичные\r\nпорты (например, 8443)\r\nT1573 Encrypted Channel\r\n(Ex)Cobalt использует TLS для взаимодействия с C2-\r\nсерверами\r\nT1105 Ingress Tool Transfer\r\n(Ex)Cobalt использует бэкдор (libs.so) для получения\r\nс C2-сервера своей обновленной версии (zov_latest)\r\nT1090.001 Proxy: Internal Proxy\r\n(Ex)Cobalt использует бэкдор (libs.so) для запуска\r\nна узле SOCKS-прокси\r\nEXFILTRATION\r\nT1041\r\nExfiltration Over C2\r\nChannel\r\n(Ex)Cobalt использует бэкдор (libs.so) для  передачи\r\nсодержимого файла zov_logs.txt (перехваченные\r\nпароли и ключи), файл отправляется на C2-сервер\r\nпо тому же зашифрованному каналу, что и команды\r\nуправления\r\nT1030 Data Transfer Size Limits\r\n(Ex)Cobalt ограничивает размер файла zov_logs.txt\r\nдо 8 МБ, при превышении этого порога файл\r\nавтоматически очищается\r\nИндикаторы компрометации\r\nPUMA\r\nИмя SHA-256 SHA-1\r\ncron 0eafb9dbaf9f72e61ee65926a61b3b07a3a5d1ab1a47b0bb6e3e8bc3c9f9f9f2 7fbe0a0dbeb8d55ad7764242fd4f8d6a\r\n0f37d45c70d5fae0c76b9eeed454b4a0ef58609ce99534d1ca9365fac5155fc9 c1633991f910735949380c416b2ffd9e\r\n5cef48c73704040bea45f85601cbc0fe40b5dea84721344b1cfd67c62650e996 528585a5c921ddf7027a4d5d3096d99\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 31 of 34\n\nPUMA\r\n8ce769d07989f59ece5f137afce84e4075fcc1010426bf1a77378d7e2a628995 aae68641de0d64f3d2ee711298191683\r\n8d3a411ea9225d8bc0503235e0832025e7a3260b9bcdb85119ccb508c4547107 b75165c8713f4f28df8f36694f988467\r\n9f5a79d9c5100de0af25bec6f640264e4db367d1a6cd1804e1e2a1772637c30a 2d9607bdc928cdd057c7c03732235e7\r\n37a5bd6361e48586749594f5fb853c4b79a0931f979f03bdb31dc91c526924ec cb6c56e43c3db089d140bf54047bd1a\r\n49d4cd2bf3b65db2ecbaaa46944b2b76786de51724fc73c44d0d8e485fa1f3b5 3ab09a089574545e2824cd5549715af\r\n9959a16f1fd19191de2d40cad5dbf4e63c62eb55487068de0a6f6b4696bccd72 c095b4e41bcab55fc0cbb29889fbd43c\r\n87290ce0d3bd3a680d092fe038a9e80b37b3358502d511236ec5df50e4bfe6ee acb36b770d5d5322abd2a2f5712e34e\r\ndf9b0d259a869c2bf71cbcdd90643750ffad067acdf8480e2981531f47ebf930 c6468621aa3021b46190777b903c634\r\n30b26707d5fb407ef39ebee37ded7edeea2890fb5ec1ebfa09a3b3edfc80db1f 810f4b422b9c0a6718e9461de3a2edd\r\n13c9c6cc9b7a180949e29baa38efde81e06e7e0a29be3b4ed98eb10af4ab60ef 30b3241f662d2c17945c7ce19bb4666\r\nwpn\r\n7b1da4c6c9b44444e9a6c4d58f7676af52ecd504fe2657fce9ce1adc2b711055 d3910322b006a398011dc22b831d709\r\nbe44c606c2114534222f1be794a37a7d113d849376328149d60d6b67c1318946 329254021a8679a603bf0f29f97195b6\r\nf71873f37fbd5a5870a51db75d8b99ff83ac2d03f0e5682e943d8cb9c84b6129 8ba2aeb62c043b2f35e56e1e1633031b\r\n650bd3fec6160759878f58962d2cffe9cf0074b59b01e569621335470da1e005 798b5cc955c7d39cb9d40d011b5f598\r\n28dbae86db73868758301c140f2b2c7bc19ce3862e0cd21966bfe9d3e7476e4a d05c83c339d8d1224e5992eae04d8eb\r\n809e41c3a700ccaaaf2602b62e162a4ae59315ef86ee37daca8c7064d01bf086 be0da40d7dc3ea8bcd2a72821d223f2\r\n65e35f68c12938f133fb120565f3c8221c53d3b841dbddb98e0d3c6054f2c7be fe25ed6b992af2d37fb3876ef48d4431\r\nfb1c815365622a37328570c4b3703654fe7e5c9e9eae335542bac6090e2f9a2e b342fc7e1655fabd5a9232f8fe85f6777\r\n1444b49c6e6183f856a4f18ea6a657ae3033b47a45c1152dcf60dc361a7826d7 b11fdff95db2e06f64151600772b7e1d\r\n8fc01417832dd08559609ef0667149a24897380113294e10b44694071afd22f2 06ab28718573365dfe47238e89372c8\r\n2d7328d0623b36c31e22b1b12ece8094aa232ccdb7ea92ec860ff6b7030fbe93 4eb2e95b7e1079d53610acc7ea910af4\r\nbcc0cda02a11acbd0923db43099f854038caaf03ddbf59e1d53452641119d42a b8f5d35e407090e800b1686a0a3d550\r\n17d9359625b47fcf65ccdb59bd0dba6c44f8e140d8eedc37a8923baa8aefd481 9ec003870a9cfe15ea7530197c95241d\r\naudit\r\nd291f0bfa4a82cad1c95d11ebabb1539b9d9d5837534a8bd5b659462dd2eeabd 6bce7d2a1df368edd56f0f6a126299f8\r\n9a0d5e90444d94778775180b6373a6d4f0ca293764f51b4371f4ab5452f202e3 ec6e3309e371977ba840b3e1240653a\r\n873c4fbf47b8036238b52fd02bf7846d86df4c87f6dbf302db9a959371d464ff 2ac766bab601a2c172db98c7c9e7805\r\n58cb73a09b0cb497119bf6450f20d8343f01ea8c8ace72cc71e9414920f77342 0d49e3c8ec9f02a262e29ce90d66b58d\r\n13299152ead4b1592843b9eddfbfd7b320a5fdec09c568a11ec7f9834cd7a610 26824e4f248fbd9e162c522962972a9a\r\nfb0d79602dfac97f5e36f5f0287d167271d04294dccfdccfa15a88ff6dba11b7 cbe1e249142b25fe3bc11ad7f57d4777\r\ne49eb564c061b132c411859e79e8026f46059f4be759d812e2043956400d32ad 88f5e5832ec4a2641792e3448653ed9\r\nee33385b20be0d457f4acccc5d222eb11adb7683c96b4dfd30d2772361f2346b 113a9d00dbe3d5db956463d4675b939\r\nef8305ea0a53676a04da70abb9d18ea36acba67404e5bfdeddc201d95ec1c22a e0c5d82debbea11bc46357ccbbd0371\r\n1cc3edb1758fcadd97cd835f6b516db3aa8c3cbc563776080ab8ebf378536c71 b1c5f389de9de271681ac277ef7fe4c8\r\nec2fc991758ae4b28c1691a00470c3723171db69f901627c8e07b7f8dce221ca d78b2ba08f5ed03bcd48019efab92052\r\nf5635d9afd0116846cf447e218c561cc111c50e6a994b5c719556ce28c4e64d6 e9ed4d162edce0bc6be286cbfadb6216\r\nfb1811eaa27caeb84077900e5cfe7000bc1130a4dc51d3a2f93340080e16776f 0179608320b26e25f94ba41b1e9366e\r\n/lib64/libs.so\r\n(zov_latest)\r\ne7aa94e76acf181b4fd6fbfdcf599ce677c5f95675b50e68fe2d43ea72202bf7 d752651d0bcfc2fc7768c60409dbd858\r\nd6763d0d0c2b12ea9124ccaea3e6497bfc2cb4a3a9dfe6239a734afeaf372013 a071b503940a34c8779cf2d7fe466f12\r\n77b9a372ee97c0c832a618def71940e65a3c3d1eca1f8cb34ff395fd39cd23a2 56f7874b9f89c0299cc3656d4f5d48f4\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 32 of 34\n\nPUMA\r\n0002afc2839af82725499ed50f75a193b01a32111253ba3d8647189d8f0f2c28 8a9b9d23fc25fe963e64d692fed09da4\r\ncad9677ad4198b1710d390747e98b6b5e29ceab18f16e8ab12be5b6b8aab4021 f971af8387ac3e11f5feb2728978e37e4\r\nd597eee02fc30b3bc1c523e8afad63f75912c4c2a47d3e981c5950a00bd76a6d f7db8a88992966917179cc22d833bf8\r\n3ee7ae78964f35ec56a1d5d6026482e40e6ca9a59f9f474fff475e32231e2ff4 c1ec4ed9c5c6244fc0a4b0856bb51907\r\ne7e52e15636cb32ac227f4833adf75a0bacca3d8b9081f3e3bdcb8243a8b68aa 07ce236a5b5edfa0b071cd9338f28edc\r\nd984d1de0c18d0290450b990b572a7e8454b095295810bdea6ced9151973a350 9fdc6f48ea3100cebe038d11c4c6669d\r\nc1949fe4bdf693ae4e62f52bc728bf3b455b47862298fdfcd61fefa7e304d7cf 624ba4531c4099eceae27c284cced7f5\r\nOctopus\r\ntmp.CGVX9dKVLX 8b43430136fcc9ef43f201febddbdd608cfebd25c611c13a146a66883b0f201e 05245a5aff54c598adef5e67d8dccd8b\r\nlibsockopt.so.1\r\n87b34520d791933733b8f9f27088a68d9b4e67b2db869be50de2a6e01a92077f fc27af1478a427fdbeae8cc2b7cc7a40d\r\n82581c8043ac12ef3e5c35572759fb494775aff210b060ebd7492d3134b25024 3f5d0bc0c7b8acacc614763179f810f2\r\nlibsockopt.so.2 46a4f04b3a88d2ecfc98c3488c5efe485eb0e48d4b1701123319dec2ceea96fc 8a8797238494808840a724ece404551\r\nlibzst.so.0 a9e316ba471b1bf606ee3c77ae8bf4b6552e139928d6071b7dca3f847c571c92 8422dad0a89a3665d502eb2203f0509\r\n- 36d9db678b6895cd08e8673438b3de44a2cd589329c68cbf214067a38cd47d0e 1bc50e17273ff2414d099403ea395905\r\nСетевые индикаторы\r\nPUMAKIT\r\nrhel.opsecurity1.art\r\nsec.opsecurity1.art\r\n89.23.113.204\r\nqxpngendvvp1.wris.monster\r\ncnhgenfhfp2.wris.monster\r\nccdertsfrp1.wris.monster\r\nqdprtsorp2.wris.monster\r\ncckitsfrp1.n3x1lo.pro\r\nqdkitsorp2.n3x1lo.pro\r\ncddcvesfhfp1.wris.monster\r\ndeefveskiip2.wris.monster\r\ncumfpo90sing.agddns.net\r\nprocdia42ecte.agddns.net\r\nviedeu98.agddns.net\r\nlaipros50.agddns.net\r\nfira24sonstablee.agddns.net\r\nchronback49in.duckdns.org\r\nВердикты продуктов Positive Technologies\r\nPT Sandbox\r\nCreate.Process.Grep.SecureBoot\r\nTrojan-Dropper.Linux.PumaKit.a\r\nCreate.Process.Memory.ReflectiveCodeLoading\r\nRead.Process.Pkexec.DetectPwnKit\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 33 of 34\n\nPT Sandbox\r\nCreate.Process.ExploitProbe.TcacheCheck\r\nRead.Process.Sudo.PrivilegeCheck\r\nWrite.File.Binary.MyceliumPatch\r\napt_linux_UA_Excobalt__Backdoor__Octopus\r\napt_linux_UA_Excobalt__Rootkit__TransMarker\r\napt_linux_UA_Excobalt__Rootkit__Mosquito\r\napt_linux_UA_Excobalt__Rootkit__Spawner\r\napt_linux_UA_Excobalt__Backdoor__Mycelium\r\nMaxPatrol SIEM\r\nSuspicious_Read_System_Distro_Reconnaissance\r\nUnix_Recon_Tools_And_Commands\r\nSuspicious_Create_Process_DPKG_CheckInstalledSoftware\r\nUnix_System_Information_Discovery\r\nUnix_Software_Discovery\r\nSuspicious_Create_File_Attribute_Hidden\r\nUnix_Suspicious_Hidden_Files\r\nUnix_Lib_Modify\r\nUnix_Bin_Modify\r\nSuspicious_Read_File_Shell_History\r\nSuspicious_Read_File_Passwd_CredentialsEnumeration\r\nMalware_Exploit_Elf_CVE_2021_4034_a\r\nSubrule_CVE_2023_4911_GLIBC_Buffer_Overflow\r\nSource: https://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nhttps://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025\r\nPage 34 of 34\n\necho \"Try: $1, $2, IFS=' ' read -r dcmd $3\" dargs \u003c\u003c\u003c \"$3\" \nfor p in tr \"$1\\n$2\" \"\\n$2=\" \u003c \"$i\" | grep -abo \"^$2\"\ndo  \n  Page 5 of 34",
	"extraction_quality": 1,
	"language": "RU",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://ptsecurity.com/research/pt-esc-threat-intelligence/ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025"
	],
	"report_names": [
		"ex-cobalt-a-review-of-the-group-s-attack-tools-for-2024-2025"
	],
	"threat_actors": [],
	"ts_created_at": 1775791201,
	"ts_updated_at": 1775791337,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/e8bbcab2a44fbc4e9f63eab01e0c27ea3ed8215c.pdf",
		"text": "https://archive.orkl.eu/e8bbcab2a44fbc4e9f63eab01e0c27ea3ed8215c.txt",
		"img": "https://archive.orkl.eu/e8bbcab2a44fbc4e9f63eab01e0c27ea3ed8215c.jpg"
	}
}