eBPF rootkit installation attack
Goal
Detect eBPF rootkit installation and operation by correlating use of eBPF rootkit primitives with the context in which an eBPF program was loaded.
Strategy
Modern eBPF rootkits (e.g. LinkPro, VoidLink) rely on a small set of in-kernel helpers to tamper with the system from a BPF program: bpf_override_return to short-circuit kernel functions (hide files from getdents, silence kill, bypass LSM checks), bpf_probe_write_user to rewrite syscall return buffers in userspace (hide sockets from ss, doctor Netlink responses, tamper with argv), and bpf_send_signal / bpf_send_signal_thread to kill endpoint security tooling from a kprobe. Loading such a program from a world-writable directory or via bpftrace --unsafe is a strong indicator that the loader was dropped by an attacker rather than installed by a package manager.
This correlation rule groups events into four activity buckets:
- eBPF tampering helpers:
bpf_override_return_usage, bpf_probe_write_user_usage, bpf_send_signal_usage - eBPF tampering helpers (interactive): same set, scoped to events that occurred inside an interactive shell or Kubernetes exec session
- Suspicious eBPF loading:
ebpf_load_from_world_writable_dir, bpftrace_unsafe_bpf_prog_load - Suspicious eBPF loading (interactive): same set, interactive scope
The rule triggers different severity levels based on the combination of detected activities:
| Case | Severity | Required Components |
|---|
| eBPF Rootkit Loaded (Interactive) | Critical | Tampering Helpers + Suspicious Loading (interactive session) |
| eBPF Tampering (Interactive) | Critical | Tampering Helpers (interactive session) |
| eBPF Rootkit Loaded | High | Tampering Helpers + Suspicious Loading |
| eBPF Tampering | High | Tampering Helpers |
| Suspicious eBPF Loading | Medium | Suspicious Loading |
Triage & Response
Isolate immediately: Isolate the affected host. To preserve memory evidence, DO NOT REBOOT.
Enumerate loaded eBPF programs: Run bpftool prog show and bpftool map show and compare against a baseline. Capture the program bytecode and pinned maps for unfamiliar entries (bpftool prog dump xlated id <id>).
Identify the loader: Review the process tree of the agent rule that triggered (process, container, image, command line, parent). For ebpf_load_from_world_writable_dir, fetch and hash the binary in /tmp, /var/tmp, /dev/shm, or /run/user/*.
Map the helpers to behavior: bpf_override_return → look for hidden files/processes/sockets and silenced kills; bpf_probe_write_user → look for tampered ss/netstat/auth output; bpf_send_signal → look for EDR, auditd, osquery, or Datadog Agent processes terminated without a corresponding userspace kill.
Check persistence: Inspect /etc/ld.so.preload (LinkPro fallback), systemd units that load BPF objects, cron jobs, and any pinned programs under /sys/fs/bpf/.
Look for lateral indicators: Pivot on @process.variables.correlation_key to surface other rule hits inside the same shell or pod session (credential access, masquerading, network scanning).
Perform memory forensics: Capture a memory dump before any remediation. eBPF programs are kernel-resident and disappear on reboot — preserve them first.
Rebuild from a known-good image: After forensics, restore the host from a trusted image and rotate any credentials that were exposed on the compromised host.