Статья Обход EDR Linux: syscall evasion, io_uring и eBPF-атаки для пентестеров

Одноплатный компьютер с открытыми контактами на тёмном антистатическом коврике, маленький OLED-экран светится янтарным текстом. Сзади монитор отбрасывает сине-зелёный свет на золотистые дорожки платы.


Когда на engagement'е попадается Linux-хост с EDR-агентом, первый рефлекс - понять, что именно этот зверь перехватывает. На Windows ответ давно каталогизирован: ntdll-хуки, ETW-провайдеры, kernel callbacks. На Linux картина другая. Агенты разных вендоров используют принципиально разные источники телеметрии - от auditd до eBPF-сенсоров и проприетарных kernel-модулей. И у каждого подхода свои слепые зоны. Вот их-то мы и будем эксплуатировать.

По данным Kaspersky Incident Response, уязвимости в публично доступных приложениях стали первопричиной 39% состоявшихся инцидентов. При этом, согласно CrowdStrike 2025 Global Threat Report (данные цитирует Vectra AI), 82% детектов приходятся на malware-free активность - credential theft, living-off-the-land и identity-based атаки, которые Linux EDR особенно плохо отслеживает. Обход EDR Linux перестал быть прерогативой APT-групп: готовые EDR bypass-инструменты продаются на подпольных форумах от 300 долларов.

Разберём три фундаментальных вектора обхода EDR на Linux для пентестеров: прямые syscall'ы минуя glibc, злоупотребление io_uring и наступательное использование eBPF. Для каждого - конкретный механизм, почему он работает и что проверять на своих хостах.

Как Linux EDR собирает телеметрию и что именно хукается​

Чтобы понять, как работают Linux EDR bypass методы, нужно сначала разобрать, что агент видит. Телеметрия на Linux собирается тремя способами, и у каждого характерные дыры.

Три уровня перехвата: auditd, eBPF и kernel modules​

Auditd (Linux Auditing System) - самый старый и совместимый механизм. Агент подписывается на события аудита ядра: запуск процессов (execve), открытие файлов, сетевые подключения. Проблема в том, что auditd работает как очередь с ограниченным числом потребителей. Если на хосте уже крутится compliance-система, подписанная на audit events, EDR конкурирует за тот же поток. По данным Kaspersky, даже компании с развёрнутым EDR на серверах вынуждены прописывать широкие исключения в правилах мониторинга ради производительности. Результат - часть syscall'ов тупо не попадает в телеметрию.

eBPF-сенсоры - современный подход, который используют Falco, Tracee и коммерческие решения вроде BI.ZONE EDR. Агент загружает eBPF-программы, которые цепляются к kprobes (точки входа в kernel-функции) и tracepoints (предопределённые точки трассировки). Например, kprobe на __x64_sys_execve перехватит запуск любого процесса, а tracepoint sched:sched_process_exec - событие планировщика. eBPF не так сильно бьёт по производительности, как auditd, но реализация различается между ядрами. По данным Cymulate, для работы BTF и CO-RE (Compile Once, Run Everywhere) целевое ядро должно быть собрано с CONFIG_DEBUG_INFO_BTF=y - а это есть далеко не на всех серверах. Лично я на engagement'ах регулярно встречаю RHEL 7 и CentOS 7, где BTF и не пахнет.

Проприетарные kernel-модули - отдельные вендоры идут дальше. Kaspersky внедрил технологию UKLM (Universal Linux Kernel Module), которая обрабатывает события через собственный драйвер уровня ядра и, по заявлению вендора, снижает влияние на производительность в 20 раз по сравнению с auditd. Такие модули дают максимальную видимость, но создают зависимость от конкретной версии ядра и дистрибутива.

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

Syscall hooking Linux evasion: прямые вызовы ядра минуя glibc​

Большинство пользовательских программ вызывают syscall'ы не напрямую, а через обёртки glibc: execve()__libc_execve() → инструкция syscall. Часть EDR-агентов ставит uprobes именно на функции glibc или использует LD_PRELOAD для перехвата вызовов в userspace. Это первый и самый простой барьер, который можно обойти.

На Linux, в отличие от Windows, номера syscall'ов стабильны между версиями ядра одной архитектуры. Таблица определена в arch/x86/entry/syscalls/syscall_64.tbl и практически не меняется. Никакой SysWhispers не нужен - достаточно знать номер.

Минимальный пример вызова execve (syscall 59 на x86_64) напрямую, минуя glibc:
C:
#include <unistd.h>
long raw_execve(const char *path, char *const argv[],
                char *const envp[]) {
    long ret;
    __asm__ volatile (
        "syscall\n"
        : "=a"(ret)
        : "0"(59), "D"(path), "S"(argv), "d"(envp)
        : "rcx", "r11", "memory"
    );
    return ret;
}
Компилируйте с gcc -nostdlib -nostartfiles -fno-stack-protector -static (при этом нужно определить собственную точку входа _start, например: void _start() { char *argv[]={"bin",NULL}; raw_execve("/bin/sh",argv,NULL); asm("mov $60,%rax; xor %rdi,%rdi; syscall"); }) - итоговый бинарь не затронет ни одну функцию glibc. Если EDR ставил uprobe на glibc-обёртки (__libc_execve, execvp с PATH-resolution) - он этот вызов не увидит.

Что по-прежнему ловит прямой syscall: kprobes на kernel-стороне (__x64_sys_execve), tracepoints (sys_enter_execve), auditd с правилом -a always,exit -F arch=b64 -S execve, seccomp-BPF фильтры. Они перехватывают на уровне входа в ядро, до dispatch'а в конкретный handler.

Прямые syscall'ы - не серебряная пуля. Они обходят userspace-хуки, но не kernel-level мониторинг. Тем не менее на практике я регулярно встречаю агенты, которые полагаются исключительно на LD_PRELOAD или uprobe-перехват - для них этой техники за глаза.

Практический подход для пентеста Linux и обхода защиты: напишите свой лоадер на C или Go с прямыми syscall'ами для критических операций - execve, openat, connect, mmap, mprotect. Стандартные операции (чтение конфигов, вывод в stdout) оставьте через glibc - они не триггерят алерты.

io_uring - слепая зона для обхода EDR на Linux​

Если прямые syscall'ы обходят userspace-хуки, но остаются видны ядерному мониторингу, то io_uring создаёт проблему другого масштаба: операции выполняются в ядре, но не через те syscall entry points, на которых стоят kprobes EDR-агента.

io_uring - интерфейс асинхронного ввода-вывода, появившийся в ядре Linux 5.1 (2019). Работает через два кольцевых буфера в shared memory: Submission Queue (SQ), куда процесс помещает запросы, и Completion Queue (CQ), откуда забирает результаты. После начальной настройки через [URL='https://man7.org/linux/man-pages/man2/io_uring_setup.2.html']io_uring_setup[/URL] дальнейшие операции не требуют отдельных syscall'ов - ядро забирает запросы из SQ автономно.

И вот тут-то начинается самое интересное для уклонения от обнаружения Linux-хостов. EDR-агенты, использующие kprobes на syscall entry points ([B]x64_sys_read, [/B]x64_sys_write, [I]_x64_sys_connect) или syscall tracepoints (sys_enter[/I]*), не видят аналогичные операции через io_uring. Агенты, хукающие VFS-уровень (vfs_read, vfs_write) или использующие LSM-hooks, могут перехватить и io_uring-операции, потому что внутри ядра io_uring вызывает те же функции. Но таких агентов - меньшинство. По данным Vectra AI, большинство Linux EDR-решений не мониторят io_uring, что создаёт серьёзный пробел в детектировании для серверных окружений.

RingReaper: разбор техник по MITRE ATT&CK​

RingReaper - пост-эксплуатационный агент, созданный как PoC для демонстрации этой слепой зоны. По данным Picus Security, он полностью заменяет стандартные утилиты разведки io_uring-аналогами:

ЗадачаОбычный подходRingReaper (io_uring)MITRE ATT&CK
Список процессовps aux через /procАсинхронное чтение /proc через SQProcess Discovery (T1057)
Сетевые подключенияnetstat / ssЗапрос kernel network tablesSystem Network Connections Discovery (T1049)
Активные сессииwho / wЧтение /dev/pts через io_uringSystem Owner/User Discovery (T1033)
Чтение /etc/passwdcat /etc/passwdАсинхронное чтение через SQData from Local System (T1005)
Поиск SUID-бинарейfind / -perm -4000Проверка через io_uringExploitation for Privilege Escalation (T1068)
Самоудалениеrm -fАсинхронный unlinkIndicator Removal: File Deletion (T1070.004)

Каждая из этих операций тривиальна по отдельности. Но когда они идут через io_uring, eBPF-based EDR с kprobes на sys_read, sys_openat, sys_unlink их просто не видит. При этом для базовых io_uring-операций чтения root не нужен - io_uring_setup доступен непривилегированным пользователям на хостах без явного запрета (sysctl kernel.io_uring_disabled появился только в ядре 6.6). Docker default seccomp profile блокирует io_uring_setup начиная с Docker 20.10.10 (октябрь 2021), так что в контейнерах этот фокус обычно не пройдёт.

На современных дистрибутивах (Ubuntu 23.04+, RHEL 9, Debian 12) проверьте cat /proc/sys/kernel/io_uring_disabled - значение 0 разрешает всем, 1 разрешает только процессам с CAP_SYS_ADMIN при создании ring, 2 полностью запрещает. Если 0 - вам повезло, io_uring один из самых практичных методов обхода EDR на Linux при пентесте.

eBPF evasion техники: мониторинг как оружие атакующего​

eBPF позволяет динамически загружать программы в ядро Linux. По данным Cymulate, через kprobe/tracepoint/LSM hooks он даёт возможность наблюдать syscall'ы, точки входа и выхода функций ядра и userspace, сетевые события. Модификация поведения ограничена: bpf_override_return работает только для функций с ALLOW_ERROR_INJECTION. Но та же мощь делает eBPF идеальным инструментом для атакующего - и тут ирония: EDR-вендоры сами приучили ядро доверять eBPF-программам.

С 2023 года появилось несколько руткитов на eBPF: ebpfkit, TripleCross, Pamspy. Они перехватывают учётные данные, обходят файрволы и слепят EDR-агенты. Разберём три наступательных сценария syscall hijacking Linux через eBPF.

Ослепление телеметрии - Indicator Blocking (T1562.006, Defense Evasion). Атакующий загружает eBPF-программу, которая цепляется к тем же kprobes, что и EDR, но с более высоким приоритетом. Она модифицирует данные, которые видит агент: скрывает процессы, фильтрует сетевые события, подменяет содержимое файлов. По анализу Red Canary (цитирует windshock.github.io), однажды загруженное eBPF-вредоносное ПО может скрытно изменять результаты bpftool и debugfs, делая пост-компрометационное обнаружение крайне затруднительным. По сути, EDR смотрит на мир через линзу, которую контролирует атакующий.

Перехват учётных данных. Pamspy - eBPF-программа, ставящая uprobe на функции PAM (Pluggable Authentication Modules). Перехватывает пароли в момент аутентификации. Для EDR это выглядит как легитимная eBPF-программа, потому что eBPF и есть легитимная технология ядра. Красота.

Обход файрвола и скрытый C2. BPFDoor, использовавшийся в реальных APT-атаках, вставлял пакетный фильтр в ядро. Хост при этом выглядел так, словно не имеет открытых сетевых портов - nmap показывал чистый результат. По данным windshock.github.io (со ссылкой на Trend Micro), BPFDoor внедрял packet filters в ядро для обхода правил файрвола, маскируясь под отсутствие сетевых сервисов.

Почему обход антивируса Linux через eBPF так эффективен? eBPF-программы не лежат на диске в традиционном смысле. Они работают в kernel space, недоступном для userspace-сканеров. eBPF-руткит может манипулировать данными, которые видит сам антивирус. Традиционный файловый сканер попросту не имеет точки наблюдения для обнаружения eBPF rootkit - он слепой по определению.

По данным Cymulate, добавление CAP_PERFMON или CAP_NET_ADMIN расширяет возможности программы. Исключение - BPF_PROG_TYPE_SOCKET_FILTER, которую может загрузить непривилегированный пользователь, если /proc/sys/kernel/unprivileged_bpf_disabled не установлен в 1 или 2.

Для полноценного eBPF evasion атакующему нужен root. И тут в игру вступают уязвимости повышения привилегий. В sudo до версии 1.9.17p1 опция --chroot позволяет использовать /etc/nsswitch.conf из пользовательского каталога. Атакующий подготавливает вредоносный nsswitch.conf, указывающий на подконтрольную NSS-библиотеку, и выполняет произвольный код от root. Эксплуатация требует, чтобы в sudoers пользователю было разрешено выполнение команды с --chroot (или CHROOT=* в Defaults) - на дефолтных конфигурациях многих дистрибутивов это не так. CISA добавила CVE-2025-32463 в каталог KEV (актуально эксплуатируется в дикой среде, добавлена 2025-09-29). EPSS-оценка - 0.4446 (Top 5% по вероятности эксплуатации в ближайшие 30 дней). Публичные PoC доступны - pr0v3rbs/CVE-2025-32463_chwoot на GitHub (521 звезда).

Цепочка для пентестера: непривилегированный доступ (при наличии sudo с --chroot в sudoers) → эксплуатация CVE-2025-32463 → root → загрузка eBPF-программы для ослепления EDR → выполнение основной задачи.

Из актуального стоит отметить CVE-2025-31161 в CrushFTP (CVSS 9.8 CRITICAL, CWE-305) - authentication bypass через race condition в AWS4-HMAC авторизации, EPSS 0.8621 (Top 1%). CISA включила эту уязвимость в каталог KEV (актуально эксплуатируется в дикой среде, добавлена 2025-04-07) с пометкой об использовании в ransomware-кампаниях.

Практический сценарий: пентест Linux с обходом защиты хоста​

Требования к окружению​

  • Целевой хост: Linux kernel 5.1+ (для io_uring), дистрибутив с eBPF-поддержкой (ядро 4.4+ с BTF для CO-RE)
  • EDR-агент: Falco, Tracee, CrowdStrike Falcon for Linux, BI.ZONE EDR или аналог
  • Привилегии: для разведки - любой пользователь; для eBPF evasion - root или CAP_BPF
  • Инструменты атакующего: gcc/clang с -static, библиотека liburing, bpftool

Пошаговый алгоритм​

📚 Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
📚 Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

Обнаружение eBPF rootkit и защита Linux-хостов​

Этот раздел - для blue team и для пентестеров, которые пишут рекомендации по итогам engagement'а.

Первое, что нужно проверять - загруженные eBPF-программы и их привязки к сетевым интерфейсам:
Bash:
bpftool prog show && bpftool map show
for iface in $(ls /sys/class/net/); do
  ip link show dev "$iface" | grep -q "xdp" \
    && echo "[!] XDP attached: $iface"
done
for iface in $(ls /sys/class/net/); do
  tc filter show dev "$iface" 2>/dev/null
done
Если bpftool prog show показывает программы с типами kprobe, tracepoint или xdp, не принадлежащие известному EDR или мониторингу - повод копать дальше. Но помните: если eBPF-руткит уже активен, он может подменять вывод самого bpftool. Курица и яйцо.

Именно поэтому одного мониторинга недостаточно. По данным windshock.github.io, наиболее эффективна комбинация двух инструментов. Tracee (Aqua Security) работает как камера наблюдения - записывает аномальные eBPF-события в реальном времени, сам основан на eBPF и отслеживает загрузку новых программ. LKRG (Linux Kernel Runtime Guard) работает как решётка на окне - активно мониторит целостность структур ядра и предотвращает модификации syscall table, подмену callback'ов и DKOM (Direct Kernel Object Manipulation, техника Rootkit - T1014, Defense Evasion по MITRE ATT&CK).

Tracee без LKRG - записывает, но не блокирует. LKRG без Tracee - блокирует, но не оставляет forensic-следа. Оба вместе дают и детектирование, и предотвращение.

Дополнительные меры для Linux endpoint detection evasion prevention:
  • Установите sysctl kernel.unprivileged_bpf_disabled=1 - запретите непривилегированным пользователям загрузку любых eBPF-программ
  • Обновите sudo до 1.9.17p1+ для закрытия CVE-2025-32463
  • Мониторьте процессы с удалёнными исполняемыми файлами, использующие BPF-сокеты: если readlink /proc/PID/exe возвращает строку с (deleted), а /proc/PID/net/packet непуст - это паттерн BPFDoor
  • На серверах с ядром 5.1+ проверяйте io_uring: cat /proc/PID/fdinfo/* | grep -i uring для подозрительных процессов
  • Seccomp-BPF профили для критических сервисов - они перехватывают syscall'ы на уровне входа в ядро, включая прямые вызовы

Вопрос к читателям​

На engagement'ах с Falco или Tracee на целевых Linux-хостах - какие kprobes и tracepoints вы видите через bpftool prog show в секциях type: kprobe и type: tracepoint? Конкретно интересует: перехватывает ли ваш агент io_uring_submit или __io_submit_sqe, или io_uring остаётся полностью слепой зоной? Если писали кастомные правила Falco для детекта io_uring-операций - поделитесь фрагментом YAML-конфига с полями rule и condition, укажите версию Falco.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab