Offensive eBPF: The Kernel as Your Backdoor
9 min read
June 21, 2026

Table of contents
👋 Introduction
Hey everyone!
Last week we broke SAML SSO via XML signature wrapping and parser differentials. This week we go further down the stack: past userland, past the OS, into the kernel subsystem that nation-state actors have quietly weaponized since at least 2021.
eBPF (Extended Berkeley Packet Filter) started as a way to run observability programs inside the Linux kernel without writing kernel modules. The kernel verifies each loaded program for safety before execution. But “safe” means “won’t crash the kernel,” not “won’t harvest credentials.” Load your program with root access and you hook into PAM authentication, SSL handshakes, process enumeration calls, and raw network packets before any firewall touches them. Your rootkit hooks the same syscalls that forensic tools use to detect it. Standard introspection sees nothing.
This week: eBPF program types and what attackers attach to them, cleartext credential harvesting without touching a single binary on disk, hiding processes and connections at the kernel level, XDP backdoors that bypass all host firewalls, and the APT campaigns running this in production right now.
Let’s get into it 👇
🔌 The Kernel Hook System Attackers Exploit
Linux uses fine-grained privileges called capabilities to control access to sensitive kernel subsystems. Loading offensive eBPF requires CAP_BPF + CAP_PERFMON (Linux kernel 5.8+), or legacy CAP_SYS_ADMIN. These are post-exploitation tools, not initial-access tools. You need a root foothold. What you get in return is persistent, invisible access that the OS considers safe.
Three eBPF program types power most offensive use:
- Kprobes / Uprobes: attach to any kernel function (kprobe) or any userland function in a shared library (uprobe). No binary modification, no new process, no
LD_PRELOAD. - XDP (eXpress Data Path): processes packets at the NIC driver, before iptables/nftables. Packets handled here never reach the host firewall.
- Tracepoints: attach to static kernel instrumentation points (
sched_process_exec,raw_syscalls:sys_enter). Stable ABI across kernel versions unlike kprobes.
“Attaching” means your code fires automatically every time the hooked function executes, inside the kernel, before any userland tool can observe the call.
# Verify eBPF support on target kernel
bpftool feature probe kernel | grep bpf_prog
# List loaded programs (before a rootkit hides itself)
bpftool prog list
# Build bad-bpf educational rootkit collection
git clone https://github.com/pathtofile/bad-bpf && cd bad-bpf && make
🕵️ Credential Harvesting Without Touching Disk
You want the cleartext password. Not the hash. Not the token. The credential as the user types it, before any hashing or network transmission. Standard keyloggers modify binaries or spawn visible processes. Both show up in forensics. Uprobes show up nowhere.
Because libpam.so is a shared library that every PAM-using application links against at runtime, one uprobe on pam_get_authtok covers PAM (Pluggable Authentication Modules) authentication for all of them simultaneously: sudo, sshd, su, passwd, GNOME login, VPN clients. No binary on disk is modified. The hook runs in kernel space, inside the OS itself, before the credential travels back to the requesting application in userland. pamspy implements this in under 200 lines:
# pamspy: harvest all PAM credentials via uprobe on libpam.so
# No binary modification, no LD_PRELOAD, no new process visible in ps
sudo ./pamspy
# [2026-06-21 10:03:41] user=root password=SuperS3cret
# [2026-06-21 10:03:55] user=alice password=Hunter2!
The bpf_probe_write_user() kernel helper extends this further. bad-bpf’s sudoadd.bpf.c hooks sys_read() on /etc/sudoers file descriptors and injects ALL=(ALL) NOPASSWD:ALL for an attacker-controlled username into the buffer the process receives. The file on disk reads clean. The running sudo binary sees injected content. No file modification. No audit log entry. No integrity check alert.
The same uprobe technique targets SSL_write() in libssl.so, capturing plaintext HTTPS requests, SSH session data, and database credentials before any encryption happens. The certificate is valid. The TLS handshake is legitimate. Every byte of “encrypted” traffic is readable to your hook.
👻 Hiding Processes, Connections, and Itself
/proc/ is Linux’s virtual filesystem where the kernel exposes every running process as a numbered directory. Every process-listing tool reads it by calling getdents64 to enumerate those directory entries. A kretprobe on sys_getdents64 intercepts the return value, walks the entry buffer, finds the target PID, and rewrites d_reclen in the preceding entry to make the directory reader skip it. The process runs normally. It never appears in any process listing.
Network connections disappear the same way. /proc/net/tcp is where the kernel publishes all active TCP connections. A hook on sys_read() intercepts reads of that file and filters out lines containing the backdoor’s port. ss, netstat, and lsof show a clean network state while the connection remains active.
The final evasion layer hides the eBPF programs themselves from the tools used to inspect them:
# Before self-hiding: defender sees rootkit programs in bpftool
bpftool prog list
# 42: kprobe name hide_pid tag abc123 gpl
# 43: uprobe name hook_pam tag def456 gpl
# After hooking sys_bpf to filter BPF_PROG_GET_NEXT_ID enumeration:
bpftool prog list
# (empty - programs still executing in kernel, invisible to userspace)
TripleCross, the most complete public eBPF rootkit, implements all three hiding layers: PID hiding via getdents64, connection hiding via /proc/net filtering, and self-hiding via sys_bpf interception. The accompanying technical thesis is the deepest public documentation of eBPF rootkit internals available.
🌐 XDP: The Firewall You Can’t Reach
On Linux, packets travel through layers: NIC driver, then netfilter (where iptables rules run), then the application. iptables is the most common host firewall. XDP processes packets at the NIC driver layer, before netfilter sees anything. You cannot write an iptables rule to block something XDP processes first. You cannot track something in conntrack (the kernel’s connection tracking table that powers stateful rules) that XDP redirected before conntrack ran.
LinkPro, discovered during a 2025 Synacktiv incident response on compromised Kubernetes infrastructure, implements a magic-packet knocker in XDP:
// XDP at NIC driver level, before iptables/nftables/conntrack
// Looks for TCP SYN with window_size == 54321 (the knock)
if (tcp->syn && ntohs(tcp->window) == 54321) {
bpf_map_update_elem(&knock_map, &src_ip, ×tamp, BPF_ANY);
}
// For known attacker IPs: rewrite destination port to 2233 (hidden listener)
if (bpf_map_lookup_elem(&knock_map, &src_ip)) {
tcp->dest = htons(2233);
// Recompute TCP checksum
}
A TC (traffic control) egress hook rewrites source ports on outbound responses, making replies appear to come from the original destination. Port 2233 never appears in ss or netstat. The magic SYN looks like a failed connection attempt to any network monitor. The backdoor exists and accepts commands. It is simply unobservable at every layer defenders check.
If you’re on a Kubernetes engagement like we covered in Issue 33, check whether pods have CAP_BPF or CAP_SYS_ADMIN in their security context. Either capability opens this full chain from inside a container, with the hook running against the host kernel.
🔴 Nation-State Deployment: From Telecoms to Ministries
eBPF rootkits aren’t research. They’re in production against live targets right now.
BPFDoor, attributed to China-linked Red Menshen, targets telecom providers across the Middle East and Asia. Active since at least 2021. It uses a classic BPF socket filter (not full eBPF) that inspects raw packets for a hardcoded magic value, activating a reverse shell that bypasses any host firewall. A 2023 variant showed 6x more BPF instructions than the 2022 version, indicating well-resourced ongoing development.
In February 2026, Field Effect documented ShadowGuard: a custom eBPF rootkit deployed against 70+ organizations across 37 countries, including government ministries, diplomatic institutions, and law enforcement. Process hiding, syscall interception, file concealment, kernel-level persistence. The most widespread documented eBPF rootkit campaign by a state actor to date.
The detection asymmetry is the core problem: bpftool, ps, and netstat all make syscalls the rootkit already intercepts. Reliable detection requires reading prog_idr (the kernel’s internal BPF program ID list) directly from kernel memory, bypassing the syscall layer entirely. Tools like Falco or Tetragon, eBPF-native security monitors that register their hooks at startup before an attacker loads anything, can catch rootkit behavior at hook points the rootkit hasn’t captured yet.
📡 Community Radar
Trend Micro: Quasar Linux (QLNX), a Supply Chain RAT with PAM and eBPF Backdoor
Published May 2026. A full-featured Linux RAT distributed via supply chain compromise. The credential harvesting module injects a malicious PAM module via ld.so.preload, capturing cleartext credentials from every SSH login, sudo invocation, and GUI authentication on the host. Combines with browser profile exfiltration, SSH key theft, shell history capture, and clipboard monitoring. The initial infection arrives as a compromised package dependency, then drops the rootkit after install-time script execution. Worth reading alongside this issue to compare the ld.so.preload PAM injection approach against the uprobe approach covered here. Both reach the same function. One modifies disk. One does not.
🎯 Key Takeaways
eBPF rootkits require no binary modification, no new process, and no kernel module. A single uprobe on pam_get_authtok in libpam.so captures cleartext passwords from every sudo invocation, SSH login, and VPN authentication on the system. Standard endpoint detection that monitors new processes, file changes, and library injection sees nothing, because none of those things happened.
The XDP backdoor pattern is the most evasion-resistant post-exploitation technique on Linux. Processing at the NIC driver means every host-based network control is irrelevant. BPFDoor has used a variant of this since 2021 against telecom targets. LinkPro combined it with full process and connection hiding in a documented Kubernetes IR case. The pattern is proven in the wild.
For red team assessments, the useful pre-exploitation question is not “can I find an existing rootkit with userspace tools?” but “does this target have eBPF-native detection running before I can capture the hooks?” Check for Falco or Tetragon in process listings before loading anything. If neither is present, a rootkit loaded post-foothold is effectively invisible to standard incident response procedures.
Practice:
- bad-bpf (pathtofile) - DEF CON 29 educational rootkits: pidhide, sudoadd, exechijack, text-replace
- TripleCross (h3xduck) - complete eBPF rootkit with 100-page technical thesis
- pamspy (citronneur) - cleartext credential harvesting via libpam.so uprobe
- boopkit (krisnova) - eBPF reverse shell backdoor with process hiding
- BPFDoor technical analysis (Sandfly Security) - first deep analysis of APT eBPF deployment
- LinkPro rootkit analysis (Synacktiv) - Kubernetes IR case with XDP knocker and full hiding
- ShadowGuard campaign (Field Effect) - most widespread eBPF rootkit campaign documented to date
- Linux eBPF documentation (kernel.org) - official program types, map types, and helper reference
Thanks for reading, and happy hunting!
— Ruben
Other Issues
Previous Issue
💬 Comments Available
Drop your thoughts in the comments below! Found a bug or have feedback? Let me know.