Технологія eBPF та Кібербезпека

Вступ

Технологія eBPF дозволяє виконувати програми в просторі ядра Linux (ring 0). На відміну від драйверів/LKM, eBPF програми не зламають всю систему у разі помилки та менш залежні від оновлень ядра. Ця технологія активно використовується як рішеннями захисту типу EDR, так і всілякою малваррю. В цілому, eBPF це актуальна тема в кібербезпеці, тому й ділюсь цим дописом.

ebpf-icon

Про eBPF Програми

EBPF програма це код на C або Rust, який верифікує операційна система та дозволяє запускати його в просторі ядра. Задачею усіх eBPF програм є під’єднатися до обраних хуків, місць в ядрі ОС де можна виконати свій код у відповідь на певну подію. Найпростіший приклад нижче:

hooks-flow

Хуків є тисячі, і кожен хук має своє призначення. Одні корисні для моніторингу, інші для підміни аргументів ще до виконання задачі. В eBPF програмах, підключення до хуків виглядає наступним чином:

// LSM хук, яким можна дозволити/заборонити доступ до файлу
SEC("lsm/file_open")
int BPF_PROG(file_open, struct file *file, int ret) { do_something() }

// Tracepoint хук, яким можна моніторити запуск процесів
SEC("tp/syscalls/sys_enter_execve")
int trace_execve(struct trace_event_raw_sys_enter *ctx) { do_something() }

// Tracepoint хук, яким можна підмінити вивід "ls"
SEC("tp/syscalls/sys_exit_getdents64")
int trace_getdents(struct sys_getdents64_exit_ctx *ctx){ do_something() }

// XDP хук, яким можна модифікувати вхідний трафік
SEC("xdp")
int xdp_modify(struct xdp_md *ctx) { do_something() }

Захист з eBPF

Код eBPF працює на тому ж рівні що й Auditd та дозволяє моніторити і блокувати отримані події. Більшість сучасних антивірусів та інструментів безпеки (Falco, Tetragon, SysmonForLinux) використовують саме eBPF а не Audit API, та обирають хуки під свої задачі. Наприклад:

// process_monitoring_tool.bpf.c
// Нижче частковий вміст файлу
SEC("tp/syscalls/sys_exit_execve")
int trace_process_creation(struct trace_event_raw_sys_exit *ctx)
{
    __u32 pid = bpf_get_current_pid_tgid() >> 32;
    __u32 uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;

    char comm[16];
    bpf_get_current_comm(&comm, sizeof(comm));

    bpf_printk("New Process: pid=%d uid=%d process=%s", pid, uid, comm);
    return 0;
}

Руткіти та eBPF

Раніше руткіти опирались на модулі ядра Linux (LKM), проте зараз зловмисники також починають використовувати eBPF. Оскільки хуки дозволяють моніторити, блокувати, та навіть модифікувати системні події під час їх обробки ядром, можливостей для руткітів безліч. Наприклад:

Якщо постаратись, eBPF руткіт можна приховати від ls та find, ps та top, gdb та ptrace, та й усіх інших команд. Не знаю чи є практичні способи відшукати такий “ідеальний” руткіт коли він уже засів, проте нижче опишу як моніторити сам факт його запуску.

Запуск eBPF Коду

Все починається з юзерспейсу. Щоб eBPF програма працювала, щось має помістити її в ядро. Зазвичай, це робить юзерспейс програма, нехай /tmp/malware, що містить в собі eBPF байт-код та завантажує його в ядро. Саме тому, поки eBPF руткіт ще не почав свою роботу, в логах ви точно побачите:

  1. Системний виклик execve під час запуску /tmp/malware
  2. Системний виклик bpf що завантажує програму в ядро
  3. Закріплення /tmp/malware в cron/init.d/systemd/etc

Далі, об’єм логів що ви отримаєте від eBPF руткіта залежить від того, в якому порядку підвантажується ваш моніторинг інструмент та малварь, які хуки обробляють, та від інших факторів. Це цікава й широка тема, раджу почитати :)

У Підсумку

Якщо ви цікавитесь Linux ядром або хочете розібратись як працюють EDR та моніторингові тули, спробуйте написати свою eBPF програму, як от міні-Auditd чи руткіт що приховує свій процес через eBPF. Простенькі програми легко написати з GenAI, а складніші приклади можна знайти вже на GitHub.