eBPF lets privileged code run inside the Linux kernel without a module, which is why the same technology powers modern EDR, observability and networking, and also a growing class of Linux rootkits. Most hosts and clusters give no thought to who may load BPF programs, which programs are loaded, or how to tell a legitimate agent from a malicious one. This guide covers the BPF privilege model (unprivileged BPF, CAP_BPF, CAP_PERFMON and CAP_SYS_ADMIN), how to treat eBPF based agents as privileged software, the artefacts to inventory and monitor, and a hardening baseline with kernel settings, lockdown and container controls. It is for Linux platform engineers, Kubernetes operators and detection engineers. Broader Linux detection is in the Linux Detection and Response Guide.
Key takeaways
- Turn off unprivileged BPF. Set
kernel.unprivileged_bpf_disabledto 1 (cannot be cleared until reboot) or 2 (admin can re-enable); the upstream default is 0 unless the kernel was built withBPF_UNPRIV_DEFAULT_OFF. - CAP_BPF is a kernel level privilege. Since Linux 5.8 it separates BPF from CAP_SYS_ADMIN, but any process holding it, or CAP_SYS_ADMIN, can load programs that observe or alter the whole host.
- Detect at load time. Once a malicious program is running, tools such as
psandssmay lie. Audit thebpf()syscall and alert on unexpected program loads. - Know your legitimate programs. EDR, CNI plugins, service meshes and profilers all load BPF. An inventory of expected program types, names and owning processes is the baseline every alert depends on.
- Classic BPF matters too. BPFDoor hides behind classic BPF socket filters attached with
SO_ATTACH_FILTER; check sockets withss -0pb, not onlybpftool.
Who can load BPF: the privilege model
A BPF program is bytecode loaded through the bpf(2) syscall (BPF_PROG_LOAD), checked by the in-kernel verifier, compiled and attached to a hook: a kprobe or tracepoint, a network interface (XDP or TC), a socket, a cgroup, or an LSM hook. Programs share data with user space through maps and can be pinned in the BPF file system (/sys/fs/bpf) so they outlive the process that loaded them. The verifier checks memory safety and termination; it does not judge intent. A program that hides a file or rewrites syscall results can be perfectly verifier safe.
| Control | Values and effect | Source |
|---|---|---|
kernel.unprivileged_bpf_disabled | 0: unprivileged bpf() allowed. 1: disabled, cannot be cleared on the running kernel. 2: disabled, admin can write 0 or 1 later. Default 0, or 2 with BPF_UNPRIV_DEFAULT_OFF | Kernel sysctl documentation |
| CAP_BPF (Linux 5.8+) | "Employ privileged BPF operations"; added to separate BPF from CAP_SYS_ADMIN | capabilities(7) |
| CAP_PERFMON (Linux 5.8+) | Performance monitoring, perf_event_open, and BPF operations with performance implications (tracing) | capabilities(7) |
| CAP_SYS_ADMIN | Can perform the same BPF operations as CAP_BPF; capabilities(7) prefers the narrower capability | capabilities(7) |
kernel.perf_event_paranoid | Default 2: kernel profiling, CPU events and raw tracepoints need CAP_PERFMON | Kernel sysctl documentation |
| Kernel lockdown | Lockdown lists BPF and kprobes among blocked features and logs "Lockdown: X: Y is restricted" | kernel_lockdown(7) |
| Container seccomp default | Docker's default profile blocks bpf ("Deny loading potentially persistent BPF programs into kernel") and perf_event_open | Docker seccomp documentation |
Kernel build options also matter. Zscaler's analysis of eBPF malware recommends ensuring CONFIG_BPF_KPROBE_OVERRIDE is not set unless needed, because return value overrides let a program make a syscall fail on demand, for example to stop a tool that would reveal it. Appliance and container host kernels can often drop kprobes and BPF features they do not use.
Privileged access: treating BPF capabilities as admin rights
Because a BPF program can read kernel and process data across the host, a principal that can load one is effectively a kernel administrator for observation purposes, and with the right hooks and helpers, for enforcement too. The BPF LSM, documented in the kernel as a way for "privileged users" to implement system-wide MAC and audit policies, shows the power: a program attached to an LSM hook returns -EPERM and the operation is denied host wide.
- Grant CAP_BPF and CAP_PERFMON, not CAP_SYS_ADMIN: where an agent supports it, run it with the narrow capabilities. See the Linux Capabilities Guide.
- Remove BPF rights from interactive sessions: developers need
bpftraceandbpftoolon test hosts, not on production. Control their use through sudo rules with logging; see the sudo and sudoers Security Guide. - Containers: never give workload containers
CAP_SYS_ADMIN,CAP_BPFor--privileged; keep the runtime default seccomp profile, which blocksbpf. A container that can load BPF can observe or interfere with every other container on the node (escape class risk, ATT&CK T1611). See the seccomp Guide. - Kubernetes: the DaemonSets that legitimately need BPF (CNI, runtime security, observability) should be few, named, in dedicated namespaces and allowed through an admission policy exception rather than by relaxing the policy for everyone. See the Kubernetes Admission Control Guide.
Decision guidance
| Environment | Recommended posture | Trade-off |
|---|---|---|
| General purpose servers with an EDR agent | unprivileged_bpf_disabled=1; only the agent holds BPF capabilities; audit bpf() | Developers lose ad hoc tracing in production; give them a break glass procedure |
| Kubernetes nodes with eBPF CNI or runtime security | Unprivileged BPF off; runtime default seccomp for workloads; admission exceptions for named DaemonSets only | Every new observability tool needs a reviewed exception |
| Fixed function appliances and high assurance hosts | Kernel lockdown, or kernels built without unneeded BPF and kprobe features | Some EDR and performance tools will not run; confirm vendor support first |
| Developer and lab hosts | Unprivileged BPF off, sudo access to bpftrace logged | Minor friction; keeps habits safe |
eBPF tooling, agents and their credentials
The legitimate eBPF footprint on a modern host is large: EDR and runtime security sensors, Kubernetes CNI plugins and service meshes, continuous profilers, and network observability agents. Each runs as root or with BPF capabilities, receives updates from a vendor, and usually holds an enrolment token, API key or client certificate for its management service. That makes the agent a privileged non-human identity on every host it touches.
- Supply chain: the agent's update channel can push new kernel side code to the whole estate. Require signed packages from a trusted repository, staged rollout rings and the ability to pin versions. See the Linux Package Signing and Repository Trust Guide.
- Agent credentials: store enrolment tokens and API keys with file permissions limited to the agent account, rotate them, and scope them to the minimum the console allows. Treat a leaked sensor token as a high severity incident. See the Service Account Security Guide.
- Tamper protection: an attacker with root can unload or blind a BPF based sensor. Monitor for the agent stopping, its programs disappearing and its heartbeat going quiet, as covered in the EDR Tampering and BYOVD Response Playbook.
- Policy engines: tools that enforce with the BPF LSM are powerful but are a single point of failure. Test policies in audit mode and keep a documented rollback.
Logging and detection
Zscaler's eBPF malware analysis puts it bluntly: the best chance to detect eBPF malware is when it loads, because after installation the output of system tools may not be trustworthy. Datadog Security Labs' 2026 research on eBPF rootkits (naming families such as VoidLink and LinkPro) takes the same load time focus. Known techniques include hiding command traffic with XDP and TC programs (ebpfkit), hooking socket options to hide traffic from packet capture (Symbiote), and rewriting user space memory during syscalls (bad-bpf research). In ATT&CK v20 this behaviour maps to T1014 Rootkit, T1547.006 Kernel Modules and Extensions, and, for packet filter triggered backdoors, T1205.002 Socket Filters.
Artefacts to inventory and compare
| Artefact | How to inspect | What looks wrong |
|---|---|---|
| Loaded programs | bpftool prog (type, name, tag, load time, owning PIDs) | Program types or names not in your baseline; programs loaded outside agent install windows |
| Program to process mapping | bpftool perf | kprobe or tracepoint programs owned by an unknown process |
| Pinned objects | List /sys/fs/bpf | Pinned programs or maps that no known agent owns |
| XDP and TC programs | ip link show dev <if>, tc filter show dev <if> | XDP or TC programs on hosts that run no eBPF networking |
| Kprobes | /sys/kernel/debug/kprobes/list | Probes on syscalls used for file, process or network listing |
| Classic BPF socket filters | ss -0pb | Packet sockets with filters held by unexpected processes; Trend Micro uses this to find BPFDoor |
| Kernel log | dmesg -k, journal | Warnings for the bpf_probe_write_user helper; Lockdown denials |
- Syscall auditing: record every
bpf()call that loads a program with an auditd rule on thebpfsyscall, or use your EDR's BPF load event. Alert when the loading executable is not on the approved list. See the Linux Logging and auditd for Defenders Guide. - Helper warning: the kernel prints a warning when a program uses
bpf_probe_write_user. Elastic ships a high severity rule matching that kernel message and maps it to T1547.006 and T1014; expect false positives only from debugging tools. - Configuration drift: alert if
unprivileged_bpf_disableddrops to 0 or if a host that should be locked down reportsnonein/sys/kernel/security/lockdown. - Cross-check from outside the host: because a rootkit can filter local tools, compare network flow data from switches or cloud flow logs with what the host reports. BPFDoor's magic packet is processed in the kernel before host firewall rules, according to Trend Micro, so a host firewall alone does not stop activation.
Metrics
- Unprivileged BPF disabled: percentage of Linux hosts with
unprivileged_bpf_disabledof 1 or 2 (target 100%). - Unknown programs: count of loaded BPF programs not matched to an approved agent, per week (target zero after baselining).
- BPF capable principals: number of services and containers holding CAP_BPF, CAP_PERFMON or CAP_SYS_ADMIN, trending down.
- Load event coverage: share of hosts sending BPF load events to the SIEM.
- Privileged workload exceptions: number of Kubernetes workloads allowed to load BPF, each with an owner.
Hardening baseline
Rollout plan
- Inventory: collect
bpftool prog,bpftool perf,/sys/fs/bpf,ss -0pband capability sets of running services from a representative sample of hosts and nodes. Map each program to its agent and vendor. - Disable unprivileged BPF: set
kernel.unprivileged_bpf_disabled=2in/etc/sysctl.d/first, so it can be reverted without a reboot if something breaks; move to 1 once a pilot ring shows no impact. - Reduce capabilities: move agents from CAP_SYS_ADMIN to CAP_BPF plus CAP_PERFMON where the vendor supports it; remove BPF capabilities from everything else.
- Lock containers: enforce runtime default seccomp and forbid privileged Pods and added CAP_SYS_ADMIN or CAP_BPF outside named exceptions.
- Turn on load time detection: audit
bpf(), ship kernel logs, and build the "unknown program" alert from the inventory. - Consider lockdown and kernel options: on hosts that run no eBPF agent, evaluate lockdown and kernels without
CONFIG_BPF_KPROBE_OVERRIDE. Test in a pilot ring first because lockdown also restricts other debugging features. Kernel settings are in the Linux Kernel Hardening and sysctl Guide. - Rollback: keep a documented procedure to set the sysctl to 0 (only possible from value 2), to grant a capability temporarily, and to boot without lockdown, each needing a change record.
Framework mapping
| Framework | Reference | How it applies |
|---|---|---|
| NIST SP 800-53 Rev. 5 | AC-6 Least Privilege; AC-6(10) Prohibit Non-privileged Users from Executing Privileged Functions | Unprivileged BPF off; narrow capabilities for agents |
| NIST SP 800-53 Rev. 5 | CM-6 Configuration Settings; CM-7 Least Functionality | sysctl baseline; kernels without unneeded BPF and kprobe features |
| NIST SP 800-53 Rev. 5 | SI-4 System Monitoring; AU-12 Audit Record Generation; SI-7 Software, Firmware, and Information Integrity | BPF load auditing, program inventory, lockdown |
| NIST CSF 2.0 | PR.AA-05, PR.PS-01, DE.CM-09 | Enforced permissions, configuration management, runtime monitoring |
| CIS Controls v8.1 | Control 4 Secure Configuration; Control 8 Audit Log Management | Kernel settings baseline and load event logging |
| MITRE ATT&CK v20 | T1014 Rootkit; T1547.006 Kernel Modules and Extensions; T1205.002 Socket Filters; T1611 Escape to Host | Detection content and threat model |
Common misconfigurations
- Unprivileged BPF left on: older images and custom kernels still default to 0. Unprivileged BPF has repeatedly been a route to kernel privilege escalation (ATT&CK T1068), which is why many distributions now turn it off.
- Privileged observability containers everywhere: a profiler or log shipper deployed with
privileged: truebecause it was easier than listing capabilities. Every such Pod can load BPF on the node. - No baseline of legitimate programs: without it, an unknown kprobe looks like any other agent's program, and investigators give up.
- Trusting host tools during an incident: an eBPF rootkit can hide processes and connections from
ps,ssand packet capture. Collect memory and external network evidence; see the Rootkits and Bootkits Guide. - Only looking for eBPF: BPFDoor, attributed by Trend Micro to Red Menshen and seen against telecoms operators, uses classic BPF socket filters that
bpftoolinventories can miss. Includess -0pbin host checks. - Agent tokens in world readable files: the BPF sensor is protected but its enrolment token is not, letting an attacker register rogue sensors or query the console.
Practitioner checklist
- Set
kernel.unprivileged_bpf_disabledto 1 or 2 on every Linux host and alert on drift. - Inventory loaded BPF programs, pinned objects, XDP, TC and socket filters, and map each to an approved agent.
- Run eBPF agents with CAP_BPF and CAP_PERFMON instead of CAP_SYS_ADMIN where supported.
- Keep runtime default seccomp for containers and block privileged Pods outside named exceptions.
- Audit
bpf()program loads and alert on unknown loaders. - Alert on kernel warnings for
bpf_probe_write_userand on Lockdown denials. - Evaluate lockdown and reduced kernel BPF features for hosts that run no eBPF tooling.
- Protect and rotate eBPF agent enrolment tokens and API keys like other privileged machine credentials.
- During incidents, validate host data against external network telemetry.
Standards and references
- Linux kernel documentation: BPF Documentation
- Linux kernel documentation: Documentation for /proc/sys/kernel/
- Linux kernel documentation: LSM BPF Programs
- Linux man-pages: capabilities(7)
- Linux man-pages: kernel_lockdown(7)
- Docker documentation: Seccomp security profiles for Docker
- Trend Micro: Detecting BPFDoor Backdoor Variants Abusing BPF Filters
- Elastic: Suspicious Usage of bpf_probe_write_user Helper (detection rule)
Related NHI Mgmt Group resources: Linux Detection and Response Guide · EDR Guide · Container Runtime Security Guide · Linux Capabilities Guide