yandex metrika
eBPF как следующая эра в Linux Rootkit-разработке: механизмы, детектирование и активная нейтрализация

eBPF как следующая эра в Linux Rootkit-разработке: механизмы, детектирование и активная нейтрализация

Дата публикации: 19 октября 2025 г.

1. Введение

С момента появления eBPF (Extended Berkeley Packet Filter) в ядре Linux версии 3.18 технология позиционировалась как инструмент для высокопроизводительного мониторинга, сетевой фильтрации и профилирования системных вызовов.
Однако с точки зрения безопасности eBPF представляет собой интерпретатор байткода в пространстве ядра, что превращает его в уникальный механизм внедрения и перехвата на уровне kernel-space без загрузки модулей.

В последние годы (2022–2025) были зафиксированы кейсы, где eBPF использовался в целях обхода средств защиты, скрытия сетевой активности и модификации поведения системных вызовов.
Эта статья описывает реальный подход к:

  • детектированию вредоносных eBPF-программ
  • анализу их поведения
  • активной блокировке загрузки до момента исполнения

2. eBPF как руткит-платформа

2.1. Архитектура eBPF

eBPF-программа состоит из набора инструкций (байткод), исполняемого виртуальной машиной в ядре. Код загружается пользователем через системный вызов:

int bpf(int cmd, union bpf_attr *attr, unsigned int size);

Тип cmd = 5 (BPF_PROG_LOAD) отвечает за загрузку исполняемого кода в ядро.
До выполнения программа проходит проверку верификатором ядра, предотвращающим прямое обращение к критическим областям памяти, однако эта проверка не гарантирует отсутствие логических манипуляций или перехватов системных вызовов.


2.2. Потенциал для скрытности

Традиционный руткитeBPF-бэкдор
Требует загрузки модуля ядра (LKM)Работает без LKM, через syscall
Вызывает подозрение в dmesg/lsmodНезаметен для lsmod, modinfo
Требует root-доступМожет быть внедрён через процесс с CAP_BPF
Обнаруживается через Integrity CheckНе оставляет следов в файловой системе

Таким образом, eBPF становится идеальным механизмом для in-memory руткита, полностью обходящего классические методы детектирования LKM.


3. Вредоносные возможности eBPF

3.1. Перехват системных вызовов

Используя kprobes, злоумышленник может внедрить eBPF-программу, перехватывающую критические вызовы ядра, например:

SEC("kprobe/sys_execve") int kprobe__sys_execve(struct pt_regs *ctx) { char comm[16]; bpf_get_current_comm(&comm, sizeof(comm)); if (comm[0] == 's' && comm[1] == 'h') { bpf_override_return(ctx, -EPERM); } return 0; }

Эта программа предотвращает запуск sh или bash, создавая иллюзию “зависания” процесса.
Такое вмешательство невозможно детектировать стандартными антивирусными средствами — процесс просто не исполняется.


3.2. Сокрытие сетевой активности

С помощью socket filter eBPF может фильтровать пакеты на уровне ядра, исключая C2-трафик из захвата tcpdump, Wireshark и netstat:

SEC("socket") int socket_filter(struct __sk_buff *skb) { if (skb->remote_port == 4444) return 0; // скрытый C2 return 1; }


3.3. Подмена системных данных

eBPF может перехватывать чтение из /proc или /sys и подменять значения (например, маскировать процессы):

SEC("kprobe/vfs_read") int hide_proc(struct pt_regs *ctx) { char buf[128]; bpf_probe_read(&buf, sizeof(buf), (void *)PT_REGS_PARM2(ctx)); if (bpf_strncmp(buf, "malware", 7) == 0) bpf_probe_write_user((void *)PT_REGS_PARM2(ctx), "init", 4); return 0; }


4. Обнаружение eBPF-загрузок (userspace detection)

Наиболее надёжным индикатором является сам факт вызова bpf() с параметром cmd=5.

4.1. AuditD

auditctl -a always,exit -F arch=b64 -S bpf -F a0=5 -F key=bpf_prog_load

4.2. Falco Rule

  • rule: eBPF Program Load desc: Detects eBPF program loading into kernel condition: evt.type=bpf and evt.arg.cmd=5 output: "eBPF load detected: proc=%proc.name pid=%proc.pid user=%user.name" priority: WARNING

Эти методы фиксируют факт загрузки, но не дают информации о содержимом программы.


5. Обнаружение в пространстве ядра (kernel-level detection)

Для глубинного анализа требуется перехват самого вызова bpf() на уровне ядра.

5.1. eBPF-программа для аудита eBPF

SEC("kprobe/sys_bpf") int kprobe__sys_bpf(struct pt_regs *ctx, int cmd, union bpf_attr *attr) { if (cmd == BPF_PROG_LOAD) { struct event_t evt = {}; evt.pid = bpf_get_current_pid_tgid(); bpf_get_current_comm(&evt.comm, sizeof(evt.comm)); evt.type = attr->prog_type; evt.cnt = attr->insn_cnt; bpf_probe_read_str(&evt.name, sizeof(evt.name), attr->prog_name); events.perf_submit(ctx, &evt, sizeof(evt)); } return 0;S }

Этот код извлекает имя программы, её тип (kprobe, tracepoint, xdp, и т.д.), количество инструкций и отправляет эти данные в пространство пользователя через perf-канал.


6. Поведенческий анализ eBPF-инструкций

Каждая инструкция представлена структурой:

struct bpf_insn { __u8 code; __u8 dst_reg:4; __u8 src_reg:4; __s16 off; __s32 imm; };

Ключевой индикатор вредоносности — наличие вызовов helper-функций с повышенными привилегиями:

HelperОписаниеУровень риска
bpf_override_returnSИзменение результата системного вызова🔴
bpf_probe_write_userЗапись в память пользователя🔴
bpf_copy_from_userНезаметное чтение из памяти пользователя🟠
bpf_map_update_elemПостоянное хранение данных в ядре🟡

7. Активная нейтрализация (kernel prevention)

Можно не только детектировать, но и разрушать загрузку вредоносного eBPF.

7.1. Метод подмены первой инструкции

SEC("kprobe/sys_bpf") int prevent_bpf(struct pt_regs *ctx, int cmd, union bpf_attr *attr) { if (cmd == BPF_PROG_LOAD) { struct bpf_insn bad = { .code = 0x95 }; // BPF_EXIT_INSN bpf_probe_write_user((void *)attr->insns, &bad, sizeof(bad)); } return 0; }

Такой подход заменяет первую инструкцию на BPF_EXIT, и верификатор считает программу пустой.
Результат — отказ загрузки без падения ядра.


8. Tail Calls и обход ограничений верификатора

eBPF накладывает лимит в 1 млн инструкций и отсутствие циклов. Злоумышленники обходят это, связывая несколько программ через tail calls:

bpf_tail_call(ctx, &prog_array, 1);

Это позволяет строить цепочку исполняемых программ, что требует детектирования не только по факту загрузки, но и по цепочке вызовов bpf_tail_call.


9. Пример реального кейса: ExecHijack

Вредоносная программа, обнаруженная в ходе эксперимента Solar 4RAYS, использовала tracepoint sys_enter_execve и helper bpf_probe_write_user() для перехвата имени запускаемого бинаря.
Таким образом, вызов /bin/whoami приводил к исполнению /usr/bin/backdoor.

eBPF-Prevention перехватывал момент загрузки, анализировал prog_name и helper_id, после чего вставлял BPF_EXIT_INSN() в тело программы, предотвращая активацию.


10. Итог: архитектура защиты уровня eBPF

УровеньМетодЭффект
UserspaceAuditD / FalcoФиксирует факт загрузки
Kernel-leveleBPF-детекторАнализирует структуру и helpers
Kernel preventionBPF_EXIT подменаПрерывает выполнение вредоносной программы
Post-analysisperf → SIEM (Splunk / Graylog)Передаёт телеметрию в SOC
Policy layerwhitelisting по prog_name / helper_idУстанавливает доверенные профили eBPF

11. Заключение

eBPF стал фундаментом новой парадигмы в области руткитов.
Он безопасен, пока остаётся под контролем — но в руках злоумышленников способен полностью обойти антивирусные решения, SELinux и AppArmor.

Единственная эффективная стратегия — создание уровня “Security for eBPF”, включающего:

  • аудит всех eBPF-загрузок;
  • перехват и анализ структуры bpf_attr;
  • сигнатурный и поведенческий анализ helpers;
  • активную блокировку на kernel-уровне;
  • и экспорт телеметрии в SIEM для корреляции с другими событиями.
eBPF как следующая эра в Linux Rootkit-разработке