Руткит Singularity предназначен для скрытного внедрения в ядро Linux. Он обошёл систему защиты Elastic Defend и загрузился в тестовом окружении без единого срабатывания. Тест проводили с актуальным агентом Elastic Defend 9.5.2 на Ubuntu с ядром 6.8. До применения обходных приёмов загрузка того же модуля порождала 76 предупреждений. Большинство относилось к сигнатурной проверке файла на диске. Остальное формировал динамический мониторинг Elastic. Речь идёт о воспроизводимой методике, а не о случайном сбое одного правила. После атаки агент продолжает работать, и это особенно опасно. Примечательно, что руткит не отключал защиту целиком. Вместо этого он использовал внутренний механизм доверия. Elastic применяет его, чтобы не создавать лишние события от легитимных процессов. Именно такие исключения становятся привлекательной целью для атакующего с правами root.
Elastic Defend входит в состав агента Elastic и защищает конечные узлы. Загрузку модулей ядра он отслеживает через технологию eBPF, то есть механизм перехвата событий внутри Linux, примерно с версии 8.14. Начиная с версии 9.5 событие загрузки несёт поле, которое отмечает ядро как "изменённое". Так система реагирует на неподписанные или внешне собранные модули. Это поле использует отдельное детектирующее правило. Оно дополняет старую проверку системного журнала, действующую с 2023 года. Файл модуля проверяется сигнатурами YARA, то есть шаблонами поиска вредоносного кода. Фиксацию загрузки обеспечивает событие eBPF. Появление новых модулей на диске контролирует ещё одно поведенческое правило. Для каждой линии у Singularity нашёлся отдельный обходной манёвр. Однако самой интересной оказалась не борьба с сигнатурами, а влияние на список доверия.
Встроенная eBPF-программа Elastic проверяет идентификатор текущего процесса перед сбором данных о модуле. Она сверяет его со специальной картой доверенных процессов. При совпадении программа завершает работу досрочно. Поэтому событие для аналитического правила не формируется. Агент при этом выглядит исправным, а внедрение кода в ядро остаётся невидимым. Замысел в том, чтобы обход выглядел как штатная активность. Никаких признаков вмешательства в работу агента не остаётся. Как выяснил исследователь 0xMatheuZ, загрузчик находит эту карту динамически. Фиксированное значение он не использует, поскольку после перезапуска агента идентификатор карты меняется. Перед вызовом, передающим модуль ядру, загрузчик добавляет свой идентификатор в доверенный список. Сразу после загрузки запись удаляется. В итоге правило, нацеленное на "изменяющий" ядро модуль, остаётся без данных. Доверенный статус превращается в канал обхода без остановки агента.
Сигнатурную проверку Singularity нейтрализует до компиляции. Скрипт запутывания кода переименовывает функции и файлы проекта. Имя результирующего модуля генерируется заново при каждой сборке. Характерные строки, по которым защита узнаёт руткит, маскируются. После обработки сканеру не за что зацепиться. Правило, контролирующее появление новых файлов модулей, обходится иначе. Elastic исключил из анализа каталоги DKMS. Это стандартный механизм Linux для сборки сторонних драйверов. Исключение сделано, чтобы не мешать легитимной установке драйверов. Singularity сохраняет туда скомпилированный модуль. Поведенческое правило не срабатывает. Кроме того, руткит фильтрует сообщения журнала ядра. Имя модуля при фиксации загрузки прячется, а сам модуль исчезает из стандартных списков.
Опубликованные материалы указывают на системную проблему защитных продуктов. Списки доверенных процессов и исключённые пути создаются ради снижения ложных срабатываний. Однако при компрометации с правами root атакующий может менять их содержимое. Организациям стоит пересмотреть, кто вправе изменять внутренние структуры агента. Полезно проверить, не позволяют ли исключения записывать вредоносные файлы в "безопасные" каталоги. Одного анализа загрузки модулей недостаточно. Такие события стоит сопоставлять с сигналами о целостности ядра. Нужно также отслеживать аномальные обращения процессов к служебным объектам агента. Детектирование не должно считать исключённые пути безопасными. Особенно это важно, когда атакующий уже получил root-доступ. Внимания заслуживают процессы, обращающиеся к внутренним объектам защиты. Подобная активность нехарактерна для обычного ПО.
Несколько уровней предупреждений не гарантируют результат. Злоумышленник может управлять входными данными хотя бы на одном из них. Любые исключения требуют строгого контроля изменений. Это касается доверенных процессов и каталогов сборки драйверов. Разработчикам средств мониторинга стоит учитывать сценарий временного добавления процесса в доверенный список. Администраторам полезно рассматривать подозрительную активность в исключённых каталогах как признак компрометации. Загрузчик Singularity пока не публикуется. Сам код руткита открыт для изучения. Публикация деталей позволяет защитным командам заранее скорректировать правила. Опыт Singularity пригодится и другим разработчикам EDR-систем, чтобы пересмотреть собственные исключения.