В 2025 году команда SOLAR 4RAYS опубликовала исследование о масштабной кампании группировки NGC5081, которую мы обнаружили сразу в трех крупных организациях. Тогда атакующие скомпрометировали подрядчика IT-услуг, а после проникли в инфраструктуру его клиентов.

Кратко эту атаку можно описать следующим образом.

  • Крайне высокий уровень атакующих:
    • чистая работа — в большинстве систем не остается следов атаки;
    • уникальная мимикрия для процессов ВПО — имя процесса выбиралось в зависимости от установленного в системе ПО и роли самой системы;
    • мимикрия C2-доменов — доменные имена создавались под компании-жертвы.
  • Основным инструментом атакующих являлось ВПО на Rust — IDFKA. Большинство семплов жили только в памяти и без закрепления. При этом полезная нагрузка в обнаруженных образцах была зашифрована с помощью ключа, который хранился в ENV процесса.
  • В некоторых системах и на ранних стадиях компрометации атакующие использовали TinyShell.
  • Для бокового перемещения использовались скомпрометированные учетные записи, а также уязвимые внутренние сервисы.
  • Заражение подрядчика не позднее сентября 2024 года. Предполагается, что доступ внутрь инфраструктуры мог быть получен через компрометацию публично доступных веб-сервисов.
  • Заражение клиентов подрядчика — в ноябре 2024-го и в апреле 2025-го соответственно.
  • Атрибуции нет. Непохоже на группировки, которые у всех регулярно в поле зрения.

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

  • Высокий уровень атакующих, в частности понимание unix-инфраструктуры.
  • Использование уже установленного штатного ПО для бокового перемещения и удаления следов атаки.
  • При развитии атаки на новые системы атакующие используют популярные публичные утилиты, по которым невозможно провести атрибуцию. Если атака осталась незамеченной, в системах размещается самописное ВПО.
  • Уникальная мимикрия под цель атаки и под конкретную систему: домены C2 могут быть похожи на домены жертвы атаки или связанных с ней организаций, а имена процессов и файлов выбираются исходя из имеющегося ПО в системе и инфраструктуре.
  • При атаке на сервера с большим uptime (временем работы без перезагрузки), атакующие не закрепляют ВПО. Вместо этого выполняется запуск вредоносного файла с последующим его удалением с диска системы — таким образом, система будет заражена до перезагрузки.
  • В качестве C2 может использоваться адрес легитимной организации, которая также была скомпрометирована атакующими.

С тех пор GoblinRAT мы так больше и не видели, и было предположение, что IDFKA мы также больше не встретим. Но в июне 2026 г. к нам обратилась организация за помощью в реверсе подозрительного файла.

В нашем новом исследовании мы расскажем:

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

Обзор инцидента

Наша группа анализа вредоносного ПО изучила файл и заключила, что он является обновленным образцом IDFKA. Адекватно оценив уровень угрозы, с которой пришлось столкнуться организации, ее руководство привлекло команду SOLAR 4RAYS к полноценному расследованию в инфраструктуре.

В первую очередь мы изучили систему, на которой был обнаружен новый образец IDFKA — это оказалась система с установленным ПО Asterisk (далее так и будем ее называть). В этой системе в мае 2026 года атакующие хитрым способом запустили свое ВПО:

  1. Легитимный файл /sbin/pppoe-client был перемещен во временный каталог.
  2. По пути легитимного файла был размещен и запущен образец IDFKA.
  3. Вредоносный файл был удален атакующими без завершения процесса.
  4. Легитимный файл /sbin/pppoe-client вернули на его исходное место.

Такой запуск вредоносного ПО может сбить с толку команду ИБ: если процесс покажется подозрительным, то, проверив легитимный исполняемый файл, аналитик может посчитать, что и процесс легитимный. К счастью, наш заказчик смог распознать данную подмену и обнаружить ВПО.

Кроме IDFKA, в день заражения системы в мае 2026-го также был размещен эксплойт для повышения привилегий, реализующий уязвимость CVE-2021-4034 — /var/lib/asterisk/festivalcache/f455a70b7b4a510a9e41803c7e7dfcff.mp3. Несмотря на расширение mp3, это ELF-файл, с помощью которого атакующие получили root в скомпрометированной системе. О том, как именно была скомпрометирована система Asterisk, мы расскажем позднее.

По опыту из предыдущего инцидента мы сразу приступили к полному сканированию инфраструктуры на наличие индикаторов NGC5081 и достаточно быстро получили неутешительные результаты:

  • В нескольких десятках систем с мая по июль 2026 года были запущены процессы IDFKA.
  • Цепочка компрометации свелась к нескольким серверам, в которых были запущены уязвимые к RCE веб-сервисы. На момент инцидента эти серверы не были доступны публично — атака на них выполнялась уже из внутренней сети заказчика.
  • В инфраструктуре нашлись еще несколько систем, в которых с апреля 2024 года запущены вредоносные процессы класса Backdoor (работа в режиме сервера).

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

  • В уязвимых веб-сервисах ранее были обнаружены веб-шеллы, размещенные в январе — марте 2024 года;
  • В системе Asterisk летом 2025 г. размещали php-веб-шелл, а также эксплойт CVE-2021-4034 /dev/shm/.audit. При этом хеш данного файла совпал с эксплойтом, который использовали атакующие в мае 2026 года.
  • Во всех предыдущих активностях атакующих цепочка компрометации также свелась к серверам с уязвимыми веб-сервисами. При этом в имеющихся журналах за 2026 год не имелось следов поиска уязвимостей — только эксплуатация.
  • В журналах веб-сервисов мы смогли обнаружить IP-адреса систем заказчика, с которых отправлялись вредоносные запросы:
    • ≈50% систем уже выведено из эксплуатации;
    • еще в ≈40% не обнаружено следов атакующих. Вредоносное ПО отсутствовало, а журналы уже ротировались;
    • и лишь в некоторых из этих систем нам удалось обнаружить образцы вредоносного ПО, размещенное осенью 2025 года, которые мы назвали IDFKA Dropper.

Все эти данные вместе дали нам понимание, что, хоть у нас недостаточно информации о 2024 и 2025 годах, вероятно, абсолютно все, что мы видим, является активностью NGC5081.

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

Таймлайн инцидентов NGC5081
Таймлайн инцидентов NGC5081

Начало атаки

К счастью, у одного из уязвимых веб-сервисов было настроено подробное логирование, и в журналах сохранились миллионы запросов к сервису с 2023 по 2026 год.

В этих журналах мы хотели увидеть подтверждение, что php-веб-шеллы 2024 года связаны с NGC5081, и наши труды окупились. Для начала мы отфильтровали журналы, оставив только публичные IP-адреса. Как оказалось, сервис был доступен из внешней сети с 2023 по март 2024 года. Далее мы проанализировали, какие запросы отправлялись с каждого публичного IP-адреса.

Интересный факт: мы не обнаружили ни одного легитимного запроса с публичного IP-адреса. Причина, по которой сервис долгое время был доступен публично, нам неизвестна.

Среди тысяч IP-адресов мы смогли обнаружить группу адресов, которые целенаправленно с января по март 2024 года искали уязвимости в веб-сервисе. Адреса принадлежали нескольким подсетям и ASN:

  • 195.181.160.0/20 — ASN 60068;
  • 165.231.193.0/24 — ASN 58065;
  • 185.145.245.0/24 — ASN 42532.

Сначала это были шумные сканеры и брутфорс страницы авторизации сервиса, после чего запросы стали более точечными — похожими на поиск уязвимостей вручную. В начале марта у атакующих получилось разместить в сервисе php-веб-шелл — обращения к нему также выполнялись с ASN упомянутых ранее.

Далее мы решили поискать подобные адреса в иных системах заказчика, и, как оказалось, другие уязвимые веб-сервисы также были доступны из внешней сети в 2024 году. Невероятно, но в актуальных логах с систем, в журналах с ошибках веб-приложений мы смогли обнаружить записи об ошибках из января 2024 года — и там также были те же самые адреса.

Более того, мы вернулись к нашему предыдущему отчету об атаке NGC5081 на три организации (компании A, B и их подрядчик), и обнаружили, что атака на периметр организации-подрядчика в июле 2024 года также выполнялась с участием все тех же адресов с ASN 58065, а еще что именно эти адреса пытались удалять атакующие из access-журналов веб-сервера на периметре.

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

Затишье

Между компрометацией веб-сервисов и явным появлением IDFKA в инфраструктуре имеется большой пробел длительностью более года — в имеющихся у нас данных нам не удалось найти явных активностей NGC5081 в период с марта 2024 по май 2025 года. Но именно в этот период были запущены вредоносные процессы простых образцов Backdoor в режиме сервера: заражены две системы, одна в апреле 2024-го, вторая — в январе 2025-го. Вторая заражена с первой, а откуда была заражена первая, определить не удалось.

Оба процесса были запущены из файла /tmp/cron (deleted) под root, а отображаемое имя процесса подменено на cron. Образцы слушают порт 4443 и обладают следующей функциональностью:

  • открытие PTY;
  • проксирование трафика;
  • чтение и запись файлов;
  • получение информации о системе.

Также имеет функционал для работы в режиме клиента.

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

Атака на Asterisk

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

Цепочка атаки через уязвимые веб-сервисы
Цепочка атаки через уязвимые веб-сервисы

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

В июне 2025 года аналогичным способом была совершена атака на сервер Asterisk. Тогда в системе были размещены:

  • /var/lib/asterisk/sounds/vvedite-parol-eshhe-raz.php — веб-шелл;
  • /dev/shm/.audit — эксплойт CVE-2021-4034.

Также в системе была создана cron-задача, которая выполняла команду ReverseSSH к C2-серверу атакующих.

В журналах системы мы не увидели точного способа компрометации сервиса, но из-за каталога, в котором размещен веб-шелл, у нас появилось предположение о наличии незащищенного API для загрузки аудиозаписей в веб-интерфейсе Asterisk. Аналогичный каталог был указан в качестве рабочего каталога (PWD) в переменных окружения IDFKA в 2026 году, что также подкрепляло данную теорию.

Мы решили изучить веб-интерфейс Asterisk и действительно нашли уязвимые функции загрузки файлов. Важно отметить, что ПО Asterisk не имеет собственного веб-интерфейса из коробки — разработчики рекомендуют использовать отдельный open-source-проект FreePBX для управления сервером Asterisk. На момент написания данного исследования уязвимость уже была исправлена и зарегистрирована как CVE-2026-54675.

В чем заключается уязвимость? До лета 2026 года в модуле FreePBX soundlang отсутствовала валидация загружаемых файлов — это позволяет атакующим выполнить загрузку собственного PHP-файла, получив доступ к RCE. К счастью, для эксплуатации данных уязвимостей атакующим требуется учетная запись от сервиса FreePBX. Также отметим, что в свежих git-коммитах появились фильтры для снижения рисков Path Traversal — вероятно, выполнить загрузку файла можно было не только по пути /var/lib/asterisk/sounds/, но и в произвольный каталог.

Атакующие эксплуатировали данную уязвимость за год до ее исправления и официального репорта — неужели они нашли ZeroDay? Юридически — возможно, но о проблемах с валидацией загружаемых файлов уже писали обычные пользователи на форуме FreePBX еще в 2017 году. Тогда пользователи отмечали, что загрузка нетипичного файла может привести к «нарушению работы всей IP-АТС».

Развитие атаки и надежное закрепление

В сентябре 2025 года атакующие начали активное боковое перемещение. Мы обнаружили множество следов атакующих вплоть до марта 2026 года — в марте заказчик заметил вредоносную активность и начал масштабное реагирование на инцидент. Но после реагирования инцидент не завершился.

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

  • /usr/bin/newsyslog;
  • /usr/sbin/logrotate;
  • /usr/sbin/updatedb.

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

Возвращение атакующих

В мае 2026-го атакующие вернулись в инфраструктуру с помощью одного из семплов IDFKA Dropper — далее они снова воспользовались уязвимыми веб-сервисами для запутывания цепочки компрометации, после чего проникли на десятки систем заказчика. На схеме ниже представлена абстракция цепочки компрометации, которую мы получили в результате анализа свежей активности.

Упрощенная схема цепочки компрометации в мае-2026
Упрощенная схема цепочки компрометации в мае-2026

Вывод по инциденту

Как и в предыдущих инцидентах, NGC5081 продолжают держать высокую планку в сложности своих атак, делая основной упор на скрытность. Примечательно, что в прошлых инцидентах мы видели минимум закрепления от данной группировки, однако в текущем кейсе атакующие стали использовать IDFKA Dropper. При этом, если посмотреть на общий таймлайн всех инцидентов NGC5081, IDFKA Dropper начали размещать в инфраструктуре компании С сразу после завершения реагирования в инфраструктурах компаний A и B, а также их подрядчика — возможно, атакующие пересмотрели свой подход к закреплению после потери доступа сразу к трем своим жертвам.

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

Как и в прошлых инцидентах, мы видим, что, кроме IDFKA, группа NGC5081 также использует более простые образцы вредоносного ПО, а также команды ReverseShell и ReverseSSH. Мы предполагаем, что IDFKA запускается в инфраструктуре жертвы только на поздних этапах атаки, когда атакующие уже провели разведку в скомпрометированном сегменте и знают об отсутствии мониторинга и средств АВПО.

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

Техническое описание

Новый образец IDFKA заметно расширяет возможности семейства: 40 команд управления (24 в прежней версии), 15 сетевых режимов (11 в прежней версии), самостоятельная сборка Ethernet/IPv4/UDP, перехват терминального ввода через ptrace, обновление через memfd и набор операций с ядровым модулем powerstate. Мы разобрали новую версию и сопоставили ее с семплом из прошлого исследования.

Главное

  • Новая версия сохраняет прежнюю архитектуру IDFKA, но расширяет командный интерфейс с 24 до 40 операций, а число сетевых режимов — с 11 до 15.
  • Сетевая подсистема получила новые варианты транспорта: формирование TCP в userspace, ручную сборку Ethernet/IPv4/UDP и активный режим поверх ICMP.
  • Появились egress scan для исследования исходящей фильтрации и резервный DNS-канал, позволяющий получать короткие команды при недоступности основного C2.
  • Для обновления backend реализована цепочка memfd → подготовка пути → fork/re-exec, благодаря которой новый ELF можно запустить без обычного исполняемого файла на диске.
  • Добавлен явно выраженный injected mode, учитывающий работу backend внутри стороннего процесса и ограничения на его завершение и перезапуск.
  • Отдельный блок команд управляет diamorphine-подобным LKM: его загрузкой, выгрузкой, закреплением и скрытием процессов.

Разбираемый семпл

File name asterisk_pppoe-client
MD5 88b2210766f7d61defd2eaa5d819df92
SHA1 4d24b0ae406a0644048ce1539e3a0ddeb1d4bc7a
SHA256 23c6e8163646ba03c0a5c6dcdf0f0df5688ec4a91c8bd9b663888440254bc12f
File type ELF64, x86-64, ET_DYN / shared object
File size 4.23 MB

Что изменилось

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

Подсистема Прошлый образец Новый образец Примечание
Команды управления 24 40 Добавлены SYN- и egress-сканирование, connectback worker, runtime-state, перезапуск и LKM-команды
Сетевые режимы 11 15 Добавлены режимы 3, 5, 6 и 10; часть прежних режимов получила новые ID
Passive custom IPv4 Protocol 204 Protocol 221 Изменение заметно на сетевом уровне
Raw Ethernet Не выделен Mode 6 Полный Ethernet-II/IPv4/UDP-кадр сериализуется на пользовательском уровне
Перехват ввода ptrace и eBPF ptrace и eBPF В исследованной конфигурации eBPF выключен; ptrace-код присутствует полностью
Обновление процесса Ограниченное управление payload memfd и re-exec Новый ELF может быть запущен из анонимного файлового объекта
Ядерное скрытие Нет в таблице команд IDs 37–41 Compile/load, unload, persistence, hide и unhide

Старые команды 0–18 в основном соответствуют новым 2–20, а прежние 19–23 переместились в диапазон 22–26. Между ними вставлена новая команда 21 — SYN scan. После статуса backend (ID 26) начинается полностью новый блок управления конфигурацией, соединениями, перезапуском и ядровым модулем.

Инициализация и конфигурация

После распаковки и передачи управления основному коду IDFKA разбирает встроенную конфигурацию, создает runtime-состояние и выбирает сетевой backend по тегу. Пример одной из конфигураций образца, ориентированного на пассивный прием команд через ICMP и маскировку процесса под FTP-сервер:

Пример декодированя конфигурации
Пример декодированя конфигурации
Параметр Значение в образце Смысл
Magic idkfa Маркер конфигурации/семейства
Config tag 1 Вариант структуры конфигурации
Backend 7 Passive ICMP packet capture
Capture variant 0 Вариант логики захвата пакетов
Interface ens192 Сетевой интерфейс для backend
Optional eBPF 00 (off) eBPF-перехват ввода в этой конфигурации не включен
Save argv 01 (on) Сохраняет исходные аргументы
Daemonize 01 (on) Переходит в фоновый режим
argv masquerade /usr/sbin/vsftpd vsftpd.conf --force-background Подмена видимой командной строки
Multiple instances 01 (on) Не ставит мьютекс / lock file
CRC32C C8 B2 26 CB Контрольное поле конфигурации

Смена сигнатуры конфига

В старых образцах сигнатура конфигурации была idfka, но в новых семплах она изменилась на idkfa. Возможно, авторы исправили свою ошибку в отсылке на чит-код IDKFA из легендарной игры Doom.

Команды

Командный диспетчер построен как таблица пар «идентификатор — обработчик». В новой версии в ней 40 рабочих записей и завершающая запись с ID 42. Функции сгруппированы вокруг каналов, терминала, передачи файлов, загружаемых SO, разведки, перехвата ввода и управления самим вредоносом.

Диапазон Группа Возможности
2–9 Каналы и shell Port forwarding, resource channels, listeners, интерактивный shell и данные shell
10–18 Файлы и payload Download/upload, запись чанков, отмена передачи, загрузка SO и изменение ее session state
19–21, 33 Разведка Асинхронный glob, обычное и SYN-сканирование портов, egress scan
22–25 Захват ввода Запуск и остановка ptrace- и eBPF-вариантов
26–36 Управление runtime Статус, connectback interval/worker, настройки, pending config, runtime payload state и restart
37–41 LKM и скрытие Compile/load, unload, persistence, hide и unhide

Сетевые режимы

Второй крупный диспетчер выбирает транспортный backend. Теги 0–14 охватывают как обычные bind/connectback-сценарии, так и пассивные каналы, которым не требуется устойчивое исходящее соединение.

ID Backend Наблюдаемое назначение
0 TCP bind/listener Ожидание входящего TCP-соединения
1 UDP listener Прием команд через UDP
2 Active TCP connectback, variant 0 Исходящее TCP-соединение, первый runtime-вариант
3 Userspace IPv4/TCP impersonation Формирование IPv4/TCP-поведения в пользовательском пространстве
4 Active TCP connectback, variant 1 Второй runtime-вариант исходящего TCP
5 Periodic reliable datagram Периодический connectback поверх надежного дейтаграммного слоя
6 Raw Ethernet reliable datagram Отправка надежных дейтаграмм через вручную собранный Ethernet/IPv4/UDP
7 Passive ICMP capture Пассивный прием ICMP; выбран в исследованной конфигурации
8 Passive IPv4 protocol 221 Пассивный канал в нестандартном IP-протоколе 221
9 Passive TCP marker Распознавание управляющего маркера в TCP-трафике
10 Active ICMP Активный канал поверх ICMP
11 Port-knock listener Ожидание заданной последовательности сетевых событий
12 Port-knock connectback Connectback после port-knock-условия
13 SpoofTCP IPv4 endpoint с нетипичной TCP-реализацией
14 Periodic HTTP(S) Периодическое целевое соединение через HTTP(S)-ветку

Raw Ethernet: кадр формируется в userspace

Наиболее интересен backend 6. Его send-функция не передает payload обычному UDP-сокету, а последовательно сериализует заголовки Ethernet II, IPv4 и UDP. Объект соединения хранит raw socket fd, индекс интерфейса, пары IPv4/UDP и оба MAC-адреса.

Ключевые инструкции ручной сборки и отправки Ethernet/IPv4/UDP:

Выжимка функции
0F2926 mov word ptr [rdx-2], 8 На линии появляются байты EtherType 08 00
0F2934 call write_payload_chunk_to_resource Добавлен 14-байтовый заголовок Ethernet II
0F2939 lea eax, [r12+8] UDP length = payload + 8
0F2946 mov [rdi+4], ax Записана длина UDP
…
0F2AE2 mov byte ptr [rsi-1], 11h IPv4 protocol = UDP (17)
0F2AF6 or al, 40h Версия IPv4 в старшем полубайте
…
0F2D8E call calc_udp_checksum Расчет UDP checksum
…
0F2E69 mov edi, [rcx+30h] Дескриптор raw-сокета
…
0F2E8B mov dword ptr [r8], 3000011h sockaddr_ll/AF_PACKET
0F2E92 mov [r8+4], ecx Индекс сетевого интерфейса
…
0F2EAA mov edx, 5EAh send length = 1514
0F2EB4 call linux_sendto_wrapper_ptr Кадр отправлен через sendto

Инструкция mov word ptr [rdx-2], 8 выглядит непривычно, если читать ее как число. На little-endian x86 она записывает байты 08 00, то есть сетевое значение EtherType IPv4. Затем функция задает длину UDP как payload + 8, протокол IPv4 как 17, вычисляет UDP checksum и строит sockaddr_ll. В итоге в sendto передается 0x5EA, или 1514 байт. Сама сериализация похожа на работу статически слинкованной библиотеки (по типу etherparse).

Буфер кадра, собранный перед sendto(AF_PACKET)

Рисунок 1. Структура буфера, собранного перед отправкой через AF_PACKET
Рисунок 1. Структура буфера, собранного перед отправкой через AF_PACKET

Egress scan: разведка исходящей фильтрации

Еще одна из новых возможностей IDFKA — отдельный egress scan, предназначенный для исследования исходящей сетевой фильтрации скомпрометированного узла.

Egress scan генерирует серии ICMP- и UDP-пакетов с подменными адресами, чтобы «понять», какой трафик может покинуть зараженную сеть.

Перед запуском backend проверяет глобальный флаг: два egress scan не могут выполняться одновременно. Затем рабочая функция разрешает адрес указанного VPS, создает RAW-сокет и последовательно выполняет несколько тестов:

  1. Отправляет подменные ICMP Echo Request, предназначенные для отражения (ping bounce).
  2. Отправляет отдельную серию подменных ICMP-пакетов.
  3. Формирует «bouncy UDP»-пакеты для заданных узлов.
  4. Генерирует подменный UDP/RTP-трафик на порт 6000, а исходный порт выбирается из диапазона 30000–30255.
  5. Генерирует подменные NTP-датаграммы с UDP-портом 123.

Для сборки пакетов используются внутренние функции, которые авторы назвали `craft_ping_packet()` и `craft_udp_packet()`. Пакеты отправляются с небольшими паузами, то есть речь идет не о высокоскоростном флуде, а о пошаговой диагностике сети.

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

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

Таким образом, egress scan не ищет цели для взлома. Он дает оператору карту исходящих ограничений и показывает, какие техники со спуфингом и отражением трафика доступны изнутри конкретной сети.

Обновление через memfd и повторный запуск

В новой версии управление обновлением стало самостоятельной подсистемой. После получения ELF backend может создать анонимный memfd, записать в него payload, сформировать путь или аргумент для повторного запуска и запустить новый экземпляр через fork/re-exec.

Рисунок 2. Восстановленная цепочка создания memfd и повторного запуска backend
Рисунок 2. Восстановленная цепочка создания memfd и повторного запуска backend

Powerstate, Diamorphine и ядровое скрытие

Последние пять команд диспетчера образуют отдельный блок: компиляция и загрузка модуля, выгрузка, закрепление, скрытие и возврат видимости. В именах и поведении модуль связан с powerstate и напоминает diamorphine-подобную реализацию. Корректная формулировка здесь — candidate/derived: семантическое сходство сильное, но происхождение исходного кода не установлено.

ID Действие Наблюдаемое поведение
37 Compile and load Собирает и загружает diamorphine-подобный LKM при необходимости
38 Unload Выгружает powerstate/Diamorphine LKM
39 Persistence Создает boot-persistence (путь /etc/cron.d/S<random> и chmod 0700)
40 Hide Скрывает backend или выбранный процесс через модуль
41 Unhide Возвращает видимость backend или процесса
Рисунок 3. Отправка сигнала 62 (0x3E) для управления скрытием PID-кандидата
Рисунок 3. Отправка сигнала 62 (0x3E) для управления скрытием PID-кандидата

При необходимости скрыть процесс или проверить работу Diamorphine вредонос загружает 0x3E во второй аргумент kill(). Отдельно команда закрепления работает с /etc/cron.d/S<random> и задает файлу режим 0700. Cron-задача запускает генерируемый скрипт каждую минуту от root:

* * * * * root /var/lib/misc/aa5398ff3d199f14cb9S > /dev/null 2>&1

Скрипт на закрепление и сокрытие выглядит вот так:

    
#!/bin/bash
 export PATH="$PATH:/usr/bin:/usr/sbin:/bin:/sbin:/usr/local/sbin:/usr/local/bin"
 if kill 62 5266228; then
     exit
 fi
 
 if ! insmod /var/lib/misc/aa5398ff3d199f14cb9<rand_L>L; then
     rm -f /var/lib/misc/aa5398ff3d199f14cb9<rand_L>L
     rm -f /etc/cron.d/aa5398ff3d199f14cb9<rand_R>R
     rm -f /var/lib/misc/aa5398ff3d199f14cb9<rand_M>M
     rm -f /var/lib/misc/aa5398ff3d199f14cb9<rand_S>S
     exit
 fi
 
 if ! kill 62 5266228; then
     rm -f /var/lib/misc/aa5398ff3d199f14cb9<rand_L>L
     rm -f /etc/cron.d/aa5398ff3d199f14cb9<rand_R>R
     rm -f /var/lib/misc/aa5398ff3d199f14cb9<rand_M>M
     rm -f /var/lib/misc/aa5398ff3d199f14cb9<rand_S>S
     rmmod powerstate
     exit
 fi
 
 SEMF9534CC7=5L38:HLV:9FQ:YFD /var/lib/misc/aa5398ff3d199f14cb9<rand_M>M
 
    

Каждое <rand_*> — отдельное десятичное значение u64, полученное во время выполнения.

Работа внутри чужого процесса

Одно из заметных отличий новой версии IDFKA — явно выраженный injected mode, предназначенный для работы backend внутри стороннего процесса. В этом режиме вредонос не просто меняет отображаемое имя: он учитывает, что завершение или перезапуск собственного кода может одновременно уничтожить легитимный процесс-носитель.

Лучше всего это видно в обработчике команды остановки. При обычном запуске backend завершает процесс через exit(0). Если установлен признак инъекции, команда вместо этого сбрасывает часть внутреннего состояния и начинает освобождать сокеты и другие ресурсы. Оператор получает предупреждение:

software is injected, so it cannot exit process.
Please try to connect 2 times more, to speed up closing of sockets and freeing of resources

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

Признак инъекции влияет и на другие функции. Диагностическая команда сообщает о выполнении внутри другого процесса и выводит его PID, ограничиваются операции перезапуска backend, а использование Diamorphine считается ненужным.

    
Status (backend side):
 - injected into PID <PID>
[-] Backend is injected, diamorphine is not needed.
    

Это логично, ведь отдельный процесс вредоноса в таком варианте отсутствует и скрывать его PID при помощи руткита нет необходимости.

Загружаемые библиотеки

Помимо встроенных команд, IDFKA поддерживает подключение дополнительных модулей в формате ELF shared object. Сама плагинная подсистема не является нововведением, так как существовала и в прошлых версиях IDFKA, но в этот раз мы решили копнуть глубже и разобраться, как это работает.

Загрузка плагина

Backend не реализует собственный загрузчик ELF. Получив путь к библиотеке, он вызывает dlopen(path, RTLD_NOW), после чего при необходимости удаляет файл с диска. Затем через dlsym выполняется поиск обязательного символа с фиксированным именем the_export.

Вместе с путем frontend передает смещение the_export внутри исходного ELF. Вычитая его из фактического адреса, возвращенного dlsym, backend определяет базовый адрес загруженной библиотеки:

    
handle = dlopen(path, RTLD_NOW); 
entry = dlsym(handle, "the_export");
load_bias = entry - supplied_export_offset;

После этого backend применяет к отображению библиотеки переданный от frontend список исправлений. Каждая запись описывает относительный адрес области, набор записываемых байтов, ее размер, список исключений и необходимость восстановить исполнимые права. Перед изменением области вызывается mprotect(..., PROT_READ | PROT_WRITE), а для исполнимых участков после записи возвращаются права PROT_READ | PROT_EXEC.

Некоторые позиции при копировании пропускаются восьмибайтовыми блоками. Судя по размеру и месту применения, это адреса динамических релокаций, уже исправленные системным загрузчиком (иначе библиотека повредится). Скорее всего, оператор заранее анализирует .so и через вредонос выполняет подготовленный им план постобработки уже загруженного отображения.

Интерфейс подключаемого модуля

Найденная функция the_export используется как универсальная строковая точка входа. Backend передает заданную строку, выделяет обнуленный буфер размером 64 КБ и вызывает экспорт примерно так:

    
void the_export(const char *request, char *response);
    

После возврата backend считывает ответ до первого нулевого байта и отправляет полученную строку оператору. Благодаря такому интерфейсу атакующие могут добавлять новые возможности отдельной библиотекой, не обновляя основной ELF и не расширяя его штатный набор команд.

Восемнадцатая команда закрывает хендл через dlclose и сбрасывает состояние загруженного модуля. Несмотря на возвращаемую строку .so was zeroed out in memory, явного перезаписывания страниц памяти этот обработчик не выполняет. Довольно интересно то, что в разобранных семплах эта часть не совсем рабочая. Один из ключевых вызовов в цепочке указывает на fallback-функцию, возвращающую ошибку Dynamic loading not supported.

Аварийный канал управления

В IDFKA есть аварийный канал (Emergency Communication) через временные файлы. Основной рабочий цикл вредоноса постоянно проверяет их наличие, обрабатывает и чистит при необходимости. В новых семплах мы заметили небольшие нововведения, а именно сигнальные файлы /tmp/.l5811028 и /tmp/.l5811029 для локального изменения конфигурации.

Запрос Ответ Назначение
/tmp/.l5811020 /tmp/.l5811021 Запрос PID. Содержимое .0 не используется; в .1 записывается десятичный PID процесса
/tmp/.l5811022 /tmp/.l5811023 Завершение работы. В .3 записывается terminated
/tmp/.l5811024 /tmp/.l5811025 Выполнение аварийной команды с захватом результата
/tmp/.l5811026 /tmp/.l5811027 Запрос снимка внутреннего журнала вредоноса. В .7 записывается накопленный логовый буфер
/tmp/.l5811028 /tmp/.l5811029 Передача новых аргументов конфигурации backend, установка состояния pending configchange и запрос перезапуска

Выполнение локальной команды

Содержимое .l5811024 разбивается на аргументы с поддержкой кавычек. Первый токен используется как имя исполняемого файла, остальные — как argv. Это не автоматический запуск через shell: конструкции вроде |, > и && сами по себе не интерпретируются. Но можно явно указать оболочку как программу.

Результат в .l5811025 имеет примерно такой формат:

cmd: <команда>
stdout: <стандартный вывод>
stderr: <стандартный вывод ошибок>

При ошибке запуска:

Emergency command: Could not execute <команда>: <ошибка>

Для пустого файла возвращается Emergency command was empty.

Вредонос также распознает ошибку незакрытых кавычек:

malformed commandline (e.g. unmatched quotes)

Изменение конфигурации и перезапуск

.l5811028 обрабатывается тем же парсером аргументов, который используется при запуске семпла без встроенного конфига. Но здесь есть явное ограничение: принимаются только параметры для BACKEND сборки. Для другого типа конфигурации предусмотрено сообщение:

these are not BACKEND params!

После успешного разбора вредонос сохраняет новую конфигурацию и запрашивает перезапуск. В .l5811029 возвращается:

parsing ok
либо:
parsing failed: <причина>

Как устроен обмен

  • Четные файлы — запросы, нечетные — ответы.
  • .0, .2 и .6 являются пустыми сигнальными файлами: важен сам факт существования.
  • .4 и .8 содержат данные.
  • После принятия запроса вредонос удаляет его через unlink.
  • После записи вредонос делает chmod(path, 0666).
  • Примерно раз в час он удаляет оставшиеся нечетные файлы-ответы.
  • После десяти обработанных аварийных запросов предусмотрена длительная пауза примерно на час — своеобразное ограничение частоты/защита от зацикливания.

Особенно интересен механизм .6/.7. Функция получения журнала используется также при перезапуске backend, где рядом встречается строка [LOG TRUNCATED BECAUSE OF RESTART].

Резервный DNS-канал

В новом образце IDFKA присутствует отдельный DNS fallback — резервный односторонний канал получения команд. Вредонос периодически запрашивает специально сформированные имена, извлекает данные из IPv4-адресов в ответах типа A, собирает из них строку длиной до 250 байт и передает ее существующему обработчику аварийных команд.

Канал рассчитан прежде всего на ситуацию, когда обычное соединение с backend недоступно. Результат исполнения обратно через DNS не отправляется, так как в именах запросов кодируются только идентификатор узла и номер части, а не вывод команды.

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

Место в архитектуре

Основная логика находится внутри функции аварийной коммуникации. Она обслуживает как локальные сигнальные файлы в /tmp, так и DNS fallback. Отдельный хелпер формирует запрос для конкретного узла, выполняет резолв и декодирует IPv4-ответ.

Восстановленная цепочка аварийного DNS-fallback канала
Восстановленная цепочка аварийного DNS-fallback канала

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

Общий индикатор наличия команд

Сначала IDFKA разрешает имя:

static.<domain>

Полученный IPv4-адрес проходит тот же декодер, что и последующие ответы. На этом этапе важно только, равен ли результат нулю:

  • 0 — сервер сообщает, что команд сейчас нет вообще (DNS: no commands for anyone);
  • ненулевое значение — команды есть хотя бы для одного узла (DNS: there are commands for someone).

Перед переходом к запросу конкретного узла выполняется случайная пауза от 0 до 1999 мс. Аналогичная пауза вставляется между запросами частей команды, что делает последовательность менее равномерной.

Как вредонос получает адресованную ему DNS-команду

После общего запроса static.<domain> вредонос проверяет, предназначена ли команда именно этому компьютеру. Для этого он получает hostname системы и преобразует его вместе с номером запрашиваемой части в 12-буквенную DNS-метку:

hostname + номер запроса (части) → хеш → получается метка из 12 букв → <метка>.<domain>

Благодаря hostname разные зараженные системы формируют разные имена. DNS-сервер может выдать команду одному конкретному узлу, не отправляя ее остальным.

Для построения метки используется алфавит etoahsnrildumywf, который заменяет полубайты вычисленного хеша. В результате идентификатор выглядит не как обычное шестнадцатеричное число, а как последовательность строчных букв.

Как команда передается частями

Первый запрос конкретного узла не содержит саму команду. Ответ на него сообщает ее длину:

  • 0 — команды для этого хоста нет;
  • от 1 до 250 — допустимая длина команды;
  • больше 250 — ответ считается ошибочным, после чего вредонос прекращает DNS-опрос до перезапуска процесса.

Если команда есть, вредонос последовательно запрашивает части с номерами 1, 2, 3 и так далее. Каждый DNS A-ответ переносит до трех байтов. Полученные части объединяются, обрезаются до ранее объявленной длины и передаются общему обработчику команд.

запрос 0 → длина команды
запрос 1 → первые 3 байта
запрос 2 → следующие 3 байта
запрос 3 ... → оставшиеся байты

Таким образом, DNS здесь используется не для поиска реального IP-адреса сервера, а как контейнер для небольших фрагментов данных.

Что скрыто внутри IPv4-ответа

Возвращаемый IPv4-адрес нельзя читать как обычное число. Вредонос берет из каждого из четырех октетов только по шесть средних бит. В сумме получается 24 бита — ровно три байта команды.

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

Повторы и обработка ошибок

IDFKA делает до пяти попыток получить адрес каждого доменного имени. Ошибки сопровождаются сообщениями DNS request …, а после исчерпания попыток — DNS request failed, giving up.

Отдельно разработчики оставили показательное предупреждение:

[-] DNS RESOLUTION FAILURES ARE AN OPSEC PROBLEM : <error>

Это подтверждает назначение механизма как скрытого резервного канала: повторяющиеся NXDOMAIN, SERVFAIL или иные ошибки резолва считаются демаскирующим фактором, а не просто сетевой неисправностью.

Что происходит с полученной командой

После сборки заявленного количества байтов строка попадает в тот же участок, который используется локальным аварийным каналом. Код выполняет shell-like-разбор аргументов, проверяет в том числе корректность кавычек и затем запускает полученную команду через общий хелпер исполнения. Поэтому DNS fallback не поднимает интерактивную сессию и не передает пакет основного C2-протокола, а лишь доставляет короткую аварийную командную строку.

В DNS-ветке нет обратного канала для stdout/stderr. Формирование следующего DNS-имени зависит только от hostname и номера части, а вывод исполненного процесса в него не включается. Следовательно, механизм подходит для односторонних действий: запуска команды, изменения локального состояния или восстановления обычного канала связи.

Команда 26: полный статус backend

Команда 26 вызывает обработчик сбора состояния вредоноса. Обработчик не возвращает одну бинарную структуру, а последовательно формирует текстовые строки и добавляет их в общий канал ответов backend.

Название «полный статус backend» условно. Отчет по состоянию хорошо описывает жизненный цикл процесса, сессии, DNS-таймер, eBPF и перехват терминального ввода, но не содержит конфигурацию C2, состояние Diamorphine и закрепления, pending configchange, egress scan или сведения о загруженных .so.

Восстановленный вид ответа

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

    
Status (backend side):
 - DEBUG BINARY (containing both frontend and backend code)   # условно

 - PID: <pid> ('<отображаемое имя процесса>')     # обычный режим
или
 - injected into PID <pid>                              # injected mode

 - active sessions: <count>
 - pivoting sessions going through this node: <count>
 - started <duration> ago

 - process restarting disabled
или
 - process will restart <duration> after start

 - process has been restarted <count> times
 - connectback interval (if applicable): <duration_1> + <duration_2>

 - DNS fallback not active
или
 - DNS timer set to <duration_1>+<duration_2>, will next fire in <duration>

 - EBPF: Mutex error!
или
 - EBPF: Module not loaded
или
 - EBPF: Module loaded
 - EBPF: hiding process
или
 - EBPF: NOT hiding process
 - EBPF: password capture active                                  # только если активно

 - ptrace password capture active                                 # только если активно
 - download_window: <integer>
    

Разбор полей статуса:

Строка Смысл
DEBUG BINARY ... Это debug-сборка, в которую одновременно включен код frontend и backend. В обычном backend строка отсутствует
PID: ... Текущий PID и отображаемое имя процесса. Последнее, вероятнее всего, соответствует текущему argv[0], поэтому строка заодно позволяет оператору проверить маскировку имени
injected into PID ... Backend работает внутри другого процесса и сообщает PID процесса-носителя вместо обычной строки идентификации
active sessions Число активных операторских/рабочих сессий
pivoting sessions ... Число сессий, которые проходят через этот узел как через промежуточный pivot
started ... ago Uptime текущего экземпляра backend
process restarting disabled Автоматический рестарт процесса отключен
process will restart ... after start Настроенный момент/задержка автоматического рестарта относительно запуска
process has been restarted ... times Число уже выполненных перезапусков, сохраненное при переносе состояния в новый процесс
connectback interval ... Пара параметров интервала активного connectback. Скорее всего, заданная задержка + рандомизированная
DNS fallback not active DNS-fallback не настроен
DNS timer set to ... Показывает два параметра DNS-таймера и оставшееся время до следующего срабатывания. Сам домен не выводится
EBPF: Mutex error! Состояние eBPF нельзя безопасно прочитать; остальные eBPF-поля в этой ветке не печатаются
EBPF: Module not loaded Модуль eBPF не загружен
EBPF: Module loaded eBPF-компонент успешно создан и доступен
EBPF: hiding process / NOT hiding process Сообщает, используется ли eBPF для скрытия процесса. В отличие от перехвата паролей, отрицательное состояние выводится явно.
EBPF: password capture active Активен перехват вводимых данных через eBPF.
ptrace password capture active Активен отдельный механизм перехвата терминального ввода через ptrace
download_window Текущий лимит/бюджет передачи данных при скачивании с узла. Значение выводится как целое число

Что означает download_window

Поле инициализируется значением 0x8000, то есть 32768. После обработки пакета команд backend читает это значение и делит его как минимум на количество активных обслуживаемых элементов. Полученный размер участвует в выделении/ограничении буфера очередной порции данных.

Поэтому download_window лучше понимать как общий бюджет выдачи данных за цикл, распределяемый между активными передачами, а не как размер файла или TCP window. Отдельная команда 29 позволяет оператору менять этот параметр во время работы. То есть оператор управляет скоростью передачи и потреблением памяти при параллельных скачиваниях с зараженной системы.

eBPF и два независимых механизма перехвата

Статус разделяет две реализации перехвата терминального ввода:

  1. eBPF password capture — часть загружаемого eBPF-компонента.
  2. ptrace password capture — отдельная глобальная подсистема.

Из этого следует, что строки не являются двумя названиями одного режима. Оба признака теоретически могут присутствовать в одном отчете. Кроме того, eBPF-state используется не только для перехвата: статус отдельно показывает загрузку модуля и скрытие процесса.

Логика eBPF-ветки имеет вид:

lock eBPF state
 error -> "Mutex error!"
 empty -> "Module not loaded"
 loaded -> "Module loaded"
   + hiding / NOT hiding
   + password capture active, если установлен флаг после чего независимо проверить ptrace capture

Сравнение со старым семплом

В расшифрованных строках старого семпла не обнаружены ни заголовок Status (backend side):, ни отдельные строки этого отчета. Старый диспатчер команд содержит switch на 24 команды, тогда как в новой версии присутствует расширенная таблица команд и отдельный обработчик команды 26.

По имеющимся данным, полноценный агрегированный статус является новой функциональностью исследуемой версии. Старый семпл уже содержал отдельные подсистемы, в том числе eBPF, но единого операторского отчета с признаками injected mode, pivoting, DNS-таймера и обоих перехватчиков в нем нет.

Обработка аргументов запуска

Отдельно отметим, что в новых версиях изменилась обработка аргументов запуска. Ранее можно было вызвать --help, пропатчив проверку поиска конфига или просто затереть конфиг. Теперь же --help удален, а все возможные аргументы заменены на хеш. Перед тем как сверить введенный аргумент, вредонос прогоняет его через несколько преобразований, а полученный хеш сверяет с 38 дескрипторами команд (алиасы и полные названия). Нам удалось восстановить 28 аргументов:

1 frontend-tcp
2 frontend-knock
3 frontend-knock-connectback
4 frontend-udp
5 frontend-listen-udp
6 frontend-listen
7 frontend-listen-icmp
8 пока неизвестна
9 frontend-future-connectback
10 frontend-future-connect
11 frontend-magictcp-connect
12 frontend-magictcp-connectback
13 frontend-icmp-connect
14 frontend-icmp-connectback
15 frontend-spooftcp
16 frontend-http

17 backend-tcp
18 backend-udp
19 backend-connectback
20 backend-connectback-tcpstack
21 пока неизвестна; алиасы bct и sct
22 backend-connectback-udp
23 пока неизвестна
24 backend-icmp
25 backend-future
26 backend-magictcp
27 backend-icmp-connectback
28 backend-knock
29 backend-knock-connectback
30 backend-spooftcp
31 backend-http

Атрибуция и следы разработки

Новый семпл относится к той же кодовой линии IDFKA: это подтверждают конфигурационная сигнатура, структура runtime, почти прямое смещение старых команд в новую таблицу и узнаваемый набор сетевых backend. Дополнительные зацепки удалось получить из собственных диагностических сообщений разработчиков.

Mario — внутреннее имя проекта?

В новой версии дважды встречается ранее неизвестное имя Mario:

reading mario binary
BUG: portscan: thread died. Mario is unstable now. please restart.

Первая строка находится в механизме закрепления. Вредонос открывает /proc/self/exe, читает собственный исполняемый образ, а позднее тот же буфер описывается сообщением writing backend binary и сохраняется как копия backend:

1) reading mario binary
2) чтение текущего ELF
3) writing backend binary

Вторая строка выводится после аварийного завершения потока portscan. Имя используется в двух независимых подсистемах, поэтому оно, вероятнее всего, относится не к сканеру, а к backend и его состоянию.

В старом образце Mario не обнаружено. Соответствующее сообщение там выглядело короче:

(старый) bug: portscan: thread threw exception
(новый) BUG: portscan: thread died. Mario is unstable now. please restart.

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

Языковой след в диагностических строках

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

Строка Версия Наблюдение
cannot serialice / cannot deserialice старая и новая Формы на -ice напоминают образование форм испанских глаголов на -izar
ELF deserializacion failure старая и новая deserializacion близко к испанскому deserialización
sid contiens invalid caracter старая и новая caracter соответствует испанскому carácter, а contiens совпадает с французской формой
Could not listening for packets старая и новая Устойчивая грамматическая ошибка неносителя английского
unexpected pivoting message from cçient старая и новая Символ ç может быть следствием использования раскладки, где он вынесен на отдельную клавишу (португальский, испанский и др.)
Port forward: Could not conect to новая Одинарная n совместима с влиянием испанского conectar
i can sent my confirmation out новая После can ожидалось send; еще один признак ненативного английского

Большая часть ошибок указывает на испанский или близкий романский языковой фон. На это указывает не одна случайная опечатка, а целая пачка форм serialice, deserialice, deserializacion, caracter и conect, встречающихся в разных частях IDFKA. Французский след основан главным образом на contiens, а возможный португальский — на единственном cçient.

Определить по этим строкам страну, состав команды или конкретную группировку невозможно. Ошибки могли принадлежать разным разработчикам.

Заключение

Новая версия IDFKA показывает последовательное развитие универсального linux-бэкдора. Авторы расширили командный интерфейс, добавили новые способы установления связи, усилили управление жизненным циклом и вынесли скрытие в отдельный ядровый контур. При этом архитектура осталась модульной: сетевой транспорт выбирается конфигурацией, перехват ввода имеет ptrace- и eBPF-варианты, а высокорисковые действия доступны как отдельные команды.

Текущий инцидент NGC5081 начался с компрометации публично доступных веб-сервисов еще в январе 2024 года — после целевой атаки на периметр организации были размещены веб-шеллы сразу в нескольких сервисах. Далее атакующие смогли развить атаку и оставить несколько бэкдоров внутри инфраструктуры, которые помогли им сохранить доступ к системам вплоть до лета 2026 года.

Мы не можем дать конкретных инструкций по противодействию группе NGC5081, так как они будут неактуальны сразу же после выпуска данного исследования, но можем дать основные советы, которые обязательно вам помогут:

  1. Наличие SOC. Регулярный анализ событий внутри инфраструктуры на наличие следов компрометации. Оба инцидента NGC5081 были начаты только из-за обнаружения атакующих силами инженеров и аналитиков линий мониторинга. Более того, мы регулярно встречали системы, в которых отсутствовали следы NGC5081, но в SIEM оставались все события их активностей
  2. Убрать сервисы за VPN, если они используются только внутри компании. Подробный лог веб-сервиса, который нам удалось изучить, не содержал ни одного легитимного запроса из внешней сети. Легитимно им пользовались только внутри компании, а из внешней сети было совершено более 2,7 миллиона запросов с целью найти в сервисе уязвимости, что в итоге и привело к его компрометации.
  3. Устранение явных дыр в безопасности. Если вам известно о наличии уязвимого сервиса, просто убрать его за VPN недостаточно. Его наличие в инфраструктуре — это в любом случае риск, так как атакующие могут развить свою атаку через него. Если уязвимость нельзя исправить, то проблемные сервисы, системы и ПО должны быть выведены из эксплуатации.
  4. Повышение покрытия СЗИ. Подключение к СЗИ четырех критичных хостов из нескольких тысяч систем в компании недостаточно. Чем выше уровень покрытия, тем выше шанс заметить атаку.
  5. Своевременные обновления ПО.
  6. Разграничение доступа внутри сети.

IOC

4b556f5a79bcbce79e5732865ccd1911304fe317756552900f249ab08777a61d
cde9c6217d91a6a722144826f81c53190c5d88b4b49d236e72e74eaf202359e5
3a2c7e2db1d6574820859ef284506daa4b24355def452f2bd9973466ae90a2fd
1b269baaf1b36b12b1f165e1ca66c3afae79bb78e128c406b027add3198fb704
62554f67379634ef8c045bc948ac50ccc4613bf27de52aded58e4093eb0fe4aa
315873f1b291ca0a77e5458fc35c0b086f7fa55d08e2c2a17e1b741420d410a0
cb95531461799808dd0d8b6ed98a6a4f7cf9742ffa68b407e81f6a73991a25c5
851e154ddf49efc736a75a14680a531991daa258cbadc9d868721652ebc07c3b
a99d655b19839beb04929071f199be1850dc2060b0bd60b97398f628ce473bd8