The tool defenders use to watch Linux is the tool attackers use to hide
EDR on Linux runs through eBPF. So do the rootkits. How they hide from ls, ps and bpftool, what the hook can and cannot reach, and what constrains it.
By Ahmed PingerPublished
13 min read0 views
eBPF was built to make Linux observable. It succeeded, and security tooling took it up. Falco, Tetragon, and several commercial EDR sensors load their detection logic on Linux as eBPF programs, hooking syscalls in kernel space.
That was a reasonable architectural choice until attackers started doing the same thing. A rootkit that hooks the same syscalls the monitoring tools call can filter the results before they reach user space. The malicious process keeps running. ls, ps, netstat, and bpftool report nothing unusual. The rootkit can stay resident for as long as it goes unnoticed. None of the campaigns below was reported to have blinded a co-resident eBPF sensor, which is a harder and separate problem; what they reliably defeat is the user-space enumeration every operator relies on first.
This is not a research scenario. The Atomic Arch campaign, discovered on 11 June 2026, backdoored roughly 408 Arch User Repository packages in its first wave (CSA's count) and, by Sonatype's preliminary analysis, approximately 1,500 packages across waves, pairing a credential stealer with an eBPF rootkit that is deployed when the build runs as root. LinkPro, published by Synacktiv in October 2025 from a compromised AWS environment, used two eBPF modules to hide files, processes, and its own BPF programs, and woke up on a magic TCP packet with window size 54321. Quasar Linux (QLNX), documented by Trend Micro in May 2026, combined a userland LD_PRELOAD rootkit, a userland controller for an eBPF hiding layer (Trend notes the kernel-side eBPF program is not part of that component), and a PAM backdoor that captured plaintext credentials at the moment of authentication.
eBPF rootkits are in production against live targets right now.
Why eBPF made EDR better and simultaneously gave attackers a new weapon
eBPF allows arbitrary programs to run inside the Linux kernel without modifying kernel source code or loading a kernel module. Programs are written in a restricted C-like language, compiled to eBPF bytecode, and verified by the kernel's verifier before execution. The verifier enforces termination, prevents out-of-bounds memory access, and ensures programs cannot crash the kernel.
For defenders this was transformative. Attaching a sensor to a kprobe or tracepoint gave visibility into system calls as they happened, in kernel space, before user-space tools could interfere. No more relying on auditd logs that a rootkit could suppress by overwriting kernel structures. The telemetry was generated at the point of the event.
The same properties that make eBPF valuable for detection make it valuable for attack:
The verifier only checks safety, not intent. An eBPF program that intercepts getdents64 (used by ls and every file listing operation) and filters its results to remove specific filenames is a valid, verifier-approved program. It runs as sanctioned kernel code. It does not appear as a suspicious process, and it does not write to disk. It will not show up in a /proc listing that passes through the hooked syscall, though it is not invisible everywhere: a just-in-time compiled program can still surface as a bpf_prog_* symbol in /proc/kallsyms, covered below.
The same hook points are available to both sides: a rootkit can attach to sys_execve or getdents64 just as a sensor can. What the cited campaigns hid was user-space enumeration; hiding events from a co-resident sensor would be a separate, harder step that none of them was reported to take.
The kernel's BPF subsystem maintains an internal program ID list called prog_idr. bpftool enumerates it via the bpf() syscall. An eBPF rootkit that hooks sys_bpf and filters its own program IDs from the response to any caller makes itself invisible to every tool that relies on that syscall, including bpftool. LinkPro did exactly this.
What the attack chain looks like
The eBPF rootkit requires an initial privilege escalation to load and attach its programs. Loading needs CAP_BPF (added in kernel 5.8) or CAP_SYS_ADMIN; attaching kprobes and tracepoints also needs CAP_PERFMON, and XDP or TC programs need CAP_NET_ADMIN. In practice a rootkit that has reached this point holds root or close to it. Getting to that privilege level is a separate problem. (LinkPro's operators reportedly gained their initial foothold through a Jenkins arbitrary file read, CVE-2024-23897.) What the rootkit provides is what happens after: a persistence and stealth layer that survives most remediation attempts and hides from most detection tooling.
A functional eBPF rootkit at minimum does the following:
Process hiding. Attaches to the getdents64 syscall. When a caller reads directory entries from /proc, the hook intercepts the response and removes entries for any PID the attacker wants hidden. The process continues running. It consumes CPU and memory. top and ps do not show it.
File hiding. The same getdents64 hook applied to filesystem reads removes specific filenames from directory listings. The implant files exist. They run. They are not visible to ls, find, or any tool that reads directory entries through the normal syscall.
Covert network activation. XDP (eXpress Data Path) programs run at the driver level, before the kernel network stack processes a packet; TC (Traffic Control) programs run later, after the socket buffer is allocated, not at driver level. LinkPro used both. Its Knock module watched for a TCP SYN with window size 54321, recorded the source IP in a map for one hour, and during that window redirected that IP's traffic to a local listener on port 2233, giving the operator an on-demand channel. It was not fully hidden: LinkPro's LD_PRELOAD library concealed the port from netstat, but in Synacktiv's analysis ss still showed the port 2233 listener.
Hiding from bpftool. The sys_bpf hook filters the rootkit's own IDs from the BPF_PROG_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID, and BPF_LINK_GET_NEXT_ID commands. A defender running bpftool prog list sees only the legitimate programs. The rootkit programs do not appear.
Credential capture is usually bolted on alongside, but it is not always an eBPF feature. Quasar Linux (QLNX) captured credentials through a PAM backdoor loaded via /etc/ld.so.preload, which Trend Micro describes as a separate component from its eBPF controller; the eBPF controller and the LD_PRELOAD rootkit did the hiding, and the PAM backdoor did the theft. Treat "eBPF rootkit" as the stealth layer, not the whole implant.
What detection tools can and cannot see
The detection problem is architectural. Every tool that enumerates running processes, open files, active network connections, or loaded BPF programs does so by making syscalls. Those syscalls are the same hooks the rootkit has attached to.
ps reads from /proc. The getdents64 hook removes the malicious process's entry. ps reports nothing.
bpftool prog list calls bpf() with BPF_PROG_GET_NEXT_ID. The sys_bpf hook filters the rootkit's IDs. bpftool reports nothing.
netstat reads /proc/net; ss uses the netlink sock_diag interface. Hiding a connection from both requires the rootkit to hook each path. LinkPro hooked only the /proc/net path, which is why ss exposed its listener: run both and compare.
A cheap cross-check the hooks usually miss: grep /proc/kallsyms for bpf_prog_ symbols. Just-in-time compiled programs are listed there when JIT kallsyms export is enabled, and a getdents64 or sys_bpf hook does not touch kallsyms. This is a signal, not proof: the setting is not on by default on every build, kptr_restrict can mask the addresses (not the symbols, for root), and an interpreted rather than JIT-compiled program will not appear. Reasoned from kernel documentation, not run against a live host.
The most reliable detection bypasses the running kernel's syscall layer. A memory image analysed offline reaches the kernel's data structures without going through the bpf() syscall the rootkit intercepts. LiME (Linux Memory Extractor) captures such an image, but it is itself a loadable kernel module: it needs one built for the exact running kernel, it can be blocked by module-signature enforcement or lockdown, and it runs inside the kernel the analyst is trying not to trust. A hypervisor-level or hardware memory snapshot avoids trusting the guest kernel at all and is the stronger option where it is available.
eBPF-native security monitors that load their hooks before the rootkit can catch rootkit behaviour at hook points the rootkit has not yet captured. Falco and Tetragon maintain kernel-level hooks that, if established prior to rootkit deployment, can observe the rootkit's own loading sequence. This is the detection window: the moment the attacker loads their eBPF programs, an existing monitor watching bpf() calls can see that a BPF_PROG_LOAD happened and which process issued it. Falco's default rule for this emits the command, process, user and command line, not the program bytecode. After that window closes, detection becomes significantly harder.
Testing for eBPF rootkit presence
Standard tooling is insufficient. The following approaches give meaningful signal:
Audit BPF program loading events. auditctl -a always,exit -F arch=b64 -S bpf -F a0=5 -k bpf_prog_load records every BPF_PROG_LOAD syscall. The audit record carries the process ID, executable path, command name and login UID, not the program's name or bytecode, so review which executables issue the call: any binary that is not your known EDR, monitoring agent, or container runtime is suspect. Two caveats: the rule only helps if it was loaded before the compromise, and a root-level rootkit can remove it.
Compare bpftool output against pinned objects in bpffs, with a caveat. eBPF objects that have been pinned appear under the bpf filesystem, mounted at /sys/fs/bpf. Listing it shows only pinned programs and maps, not every loaded program, so agreement there does not clear a host. The caveat runs deeper: the same getdents64 hook described earlier can hide entries under /sys/fs/bpf from a directory listing, so an empty or short listing proves nothing. Treat bpffs as one weak signal, never as an inventory.
Load a canary BPF program. Build and load a trivial eBPF program (for example with bpftool prog load or bpftrace), then immediately enumerate with bpftool. Only a missing canary is informative: if it does not appear, the syscall layer is being filtered. A canary that does appear proves little, because a rootkit that hides only its own IDs leaves everyone else's visible, which is exactly what LinkPro did.
Monitor for unexpected CAP_BPF and CAP_SYS_ADMIN grants. BPF program loading requires elevated capabilities. Audit capability grants in your environment and alert on any process acquiring these capabilities that does not correspond to a known legitimate service.
Examine network traffic at the hardware level. A packet capture taken from a physical network tap or a port mirror on the upstream switch will show traffic that an XDP hook has suppressed at the host. Compare host-side captures with switch-side captures for any discrepancy.
Fixing it
No single control eliminates the risk. Defence in depth is the correct frame.
Restrict unprivileged BPF. The kernel.unprivileged_bpf_disabled sysctl stops users without CAP_BPF from calling bpf(). Value 1 disables it without recovery until reboot; value 2 disables it while still allowing an administrator to change the setting later, and is the default on distributions built with CONFIG_BPF_UNPRIV_DEFAULT_OFF. Set it where unprivileged BPF is not needed.
echo 'kernel.unprivileged_bpf_disabled=2' >> /etc/sysctl.d/99-bpf.conf
sysctl --system
This raises the bar for a low-privileged foothold. It does nothing against a rootkit that already has root, which is this post's threat model, so it is a hardening measure rather than a defence against the campaigns above.
Run the kernel in lockdown integrity mode, with Secure Boot as its foundation. eBPF programs are not kernel modules, so module signing does not apply to them. BPF program signing exists from kernel 6.18 (BPF_PROG_LOAD accepts a signature and keyring to verify against), but it is opt-in: it only constrains loading where an administrator has configured a keyring and a policy to require it, and a host without that policy loads unsigned programs freely. What kernel lockdown does constrain is the helper a hiding rootkit leans on: in integrity mode the kernel refuses bpf_probe_write_user, which getdents-style tampering uses to rewrite user-space buffers, and confidentiality mode additionally blocks eBPF reads of arbitrary kernel memory. Secure Boot is the usual prerequisite that lets a distribution enable lockdown in the first place. Treat this as a mitigation, not a fix: it narrows what an unsigned eBPF program can do, it does not block loading, and at least one lockdown bypass through a writable uprobe context has been reported.
Deploy eBPF-native security monitoring before attackers can load their hooks. Falco or Tetragon configured at boot time is in place before an attacker can load hooks of their own, so it can record the attacker's load. Alerts on unexpected BPF_PROG_LOAD events are the early warning for rootkit deployment.
Treat CAP_SYS_ADMIN and CAP_BPF as equivalent to root in your privilege escalation detection logic. Acquiring either capability from a non-admin context is the precondition for every eBPF rootkit deployment.
Include kernel memory forensics in your incident response toolkit. Capture a memory image and analyse it offline on a separate trusted host, which reaches the kernel's data structures directly rather than through the syscalls the rootkit filters. LiME does this but is a loadable module that needs to match the running kernel and can be blocked by lockdown or module-signature enforcement; a hypervisor-level or hardware snapshot avoids trusting the suspect kernel at all. Capture first, analyse elsewhere.
Detection
Monitoring for rootkit presence after it is installed is the hard problem. Monitoring for installation before it completes is more tractable.
Alert on BPF_PROG_LOAD from unexpected processes. On a stable production system, the set of processes that load BPF programs is small and known. Your EDR vendor, your monitoring agent, and your container runtime are expected. Anything else warrants immediate investigation.
Alert on the syscalls that precede a load. A web application server process that suddenly calls capset, bpf, or perf_event_open is not behaving normally. auditd has no direct "capability granted" event, so write syscall rules for these and alert on any process outside the known baseline.
Network baseline discrepancy. Unexplained outbound connections visible from the network layer but absent from host-side ss or netstat output is a strong indicator of XDP or TC hook-based traffic manipulation.
Integrity monitoring of BPF object files. If your legitimate eBPF-based tools ship known-good program bytecode, hash those files and alert on modifications. A rootkit that replaces a legitimate program's bytecode to extend its hook coverage will modify files that should not change.
Take this away
EDR vendors moved to eBPF because it gave better visibility than anything built before it. Attackers noticed. The mechanism defenders rely on for kernel-level telemetry is now the mechanism attackers use to hide from the tools operators reach for first. Any organisation running Linux production workloads should be asking whether their EDR hooks are loaded before anything else at boot, whether BPF_PROG_LOAD events are being logged, and what their incident response plan looks like for a host where the standard process, file, and network inspection commands cannot be trusted.
Sonatype's preliminary analysis put the Atomic Arch campaign at approximately 1,500 packages in June 2026. The tools to deploy this attack class exist and are documented. The tools to reliably detect it after installation are still far less mature.
When a Linux host reaches the point where its own process, file, and network commands can no longer be trusted, that is an incident-response problem rather than a dashboard problem. Assumed Breach's incident response service is built for exactly that host.
Further reading
- Synacktiv: LinkPro, an eBPF rootkit analysis
- Cloud Security Alliance: Atomic Arch, AUR supply-chain eBPF rootkit (June 2026)
- Trend Micro: Quasar Linux (QLNX), a silent foothold in the software supply chain (May 2026)
- Falco project documentation
- Linux kernel: kernel_lockdown(7)
- Linux kernel documentation: bpf_jit_kallsyms sysctl
- Linux kernel documentation: unprivileged_bpf_disabled sysctl
Was this useful?
Get the next write-up by email
New technical write-ups by email when we publish. No sales sequence, and every email has an unsubscribe link.
Prefer a feed? RSS. How we handle your address: privacy notice.
Comments
Loading comments…