The Extended Berkeley Packet Filter (eBPF) was created to make Linux more observable and secure. It extends kernel functionality without requiring new modules or recompilation, enabling precise monitoring, tracing, and policy enforcement at runtime. For defenders, it promised transparency. For attackers, it opened a new space to hide. . Over the past decade, researchers have confirmed that eBPF’s legitimate capabilities can be turned against the systems they protect. Proofs-of-concept and targeted campaigns have shown that eBPF can intercept traffic, mask activity, and manipulate kernel behavior without surfacing in user-space logs. Operating inside trusted kernel space, eBPF sits below the reach of most audit frameworks and endpoint agents — invaluable for observability, but difficult to monitor once it’s abused. The Linux kernel community has responded with verifier improvements, privilege restrictions, and ongoing hardening efforts. These changes have made unprivileged attacks far less practical, but they haven’t closed the visibility gap. Defenders still struggle to see what happens inside the kernel once eBPF programs are loaded and running. Our research explores that gap: how eBPF became an essential Linux feature, how misuse developed, and why detection continues to lag behind. It draws on verified CVEs, public proofs-of-concept, and 2025 campaign analyses to map eBPF’s evolution from a trusted instrumentation layer into a security blind spot at the core of the Linux kernel. From Observability to Attack Surface eBPF entered the Linux kernel as a leap forward for observability . It lets administrators run verified code directly in kernel space safely, in theory, to trace events, monitor performance, and enforce policy without adding modules or recompiling. The verifier was built to keep that promise, checking every instruction before it could touch kernel memory. But over time, the verifier itself became part of the problem. As new features were added, the logic grewharder to reason about. A single arithmetic slip or pointer check could open paths the verifier was meant to close. What started as a safety net turned into a recurring source of privilege bugs. Key Vulnerabilities Across Kernel Versions CVE-2016-4557 – A flaw in BPF_PROG_LOAD allowed unprivileged users to inject crafted bytecode and gain kernel-level access. CVE-2017-16995 – Pointer mismanagement in the verifier enabled local privilege escalation. CVE-2018-18445 and CVE-2019-7308 – Out-of-bounds and bounds-checking issues exposed kernel memory and granted read/write primitives. CVE-2021-3490 – ALU32 bounds miscalculations reopened escalation paths even in modern kernel versions. Each generation of these flaws followed the same pattern: as eBPF grew more capable, the verifier’s attack surface expanded with it. More complexity meant more places to hide a mistake, and attackers learned how to find them. How eBPF Moved from Research to Real-World Abuse As eBPF matured, researchers began testing how far its capabilities could stretch. What started as observability experiments evolved into controlled demonstrations of stealth and persistence inside the Linux kernel. These weren’t active attacks; they were technical proofs showing what an attacker could do once root access was already obtained. The turning point came with Black Hat’s With Friends Like eBPF, Who Needs Enemies? , which showed how eBPF could run hidden logic directly in kernel space. After that, open-source rootkits appeared: Boopkit, TripleCross, and ebpfkit, each built to test how much control could be achieved without modifying the kernel itself. What These Experiments Exposed Legitimate code, hidden intent. These rootkits stayed within verifier-approved rules, running as sanctioned eBPF programs instead of suspicious modules. Stealth through system design. They used normal kernel hooks, tracepoints, kprobes, and maps for stealth. Persistence, not entry. Noneof these tools exploited eBPF to gain access. They showed how a privileged attacker could remain undetected once inside. For defenders, that’s where the blind spot came into focus. Programs operating at this layer don’t appear in /proc, system logs, or audit trails. Even a secured Linux host can run hostile logic beneath the view of traditional EDR or forensics. This phase didn’t mark a new attack wave. It marked a visibility failure. Once the security community saw how legitimate kernel logic could be repurposed for stealth, the question changed from “Is eBPF dangerous?” to “Why can’t we see it?” How the Linux Security Community Turned eBPF Into a Defensive Tool By the time eBPF-based rootkits moved from research to real deployments, the Linux security community had started to adapt. The first responses weren’t about containment — they were about visibility. If attackers could hide in the kernel, defenders needed a way to see there, too. Red Canary’s analysis of eBPF-based malware captured this shift clearly. It marked eBPF implants as a distinct Linux malware class, noting that they operated entirely within verifier-approved boundaries. No kernel modules, no syscall hooks, and no traces in /proc. The problem wasn’t access; it was observability. That insight shaped the next wave of defensive innovation. Aqua Security’s Tracee used eBPF itself to instrument the kernel for runtime detection, surfacing process creation, privilege changes, and network events without invasive hooks. Red Canary expanded that approach with eBPFmon, designed to watch eBPF activity in real time and flag unapproved programs or maps before they could be abused. The eBPF Foundation’s threat model and verifier audit were built on those lessons, calling for token-based verifier access and stricter limits on unprivileged use. The Linux kernel community, meanwhile, continued to harden verifier logic and tighten boundaries around privileged loading. Together, these changes reflected acoordinated awareness: eBPF wasn’t a threat by design. It was a visibility challenge by omission. eBPF was no longer an obscure subsystem buried in kernel docs. It became part of the active threat landscape and the toolkit used to defend it. Progress is visible but uneven. Many SOC pipelines still can’t correlate eBPF behavior or state across hosts, leaving partial sight lines into the kernel. Linux security isn’t about isolating eBPF; it’s about integrating it responsibly. Visibility, not restriction, remains the real defense. eBPF in the Wild: What BPFDoor Reveals About Linux Visibility Gaps Our latest research confirms that eBPF-based persistence is no longer confined to demonstrations or security conferences. BPFDoor represents the first observed case of an eBPF backdoor operating on production Linux systems — not through kernel exploits, but through legitimate hooks repurposed for control. The implant constructs its communication channel using packet filters, allowing it to monitor and manipulate network traffic directly within the kernel. Commands and responses blend seamlessly with normal traffic. Because it uses eBPF itself as the delivery mechanism, there’s no module load, no filesystem artifact, and almost no process-level footprint. Traditional detection tools that monitor binaries and syscalls see nothing unusual. Forensic review of confirmed infections shows a consistent pattern: persistence through pinned eBPF maps, command execution via hidden network triggers, and control traffic masquerading as routine system activity. Each component fits within verifier-approved behavior, meaning even hardened kernels allow it to run as designed. In response, new community frameworks now focus on surfacing eBPF state in real time. Early prototypes can list loaded programs, trace active maps, and compare signatures against baseline system behavior. They’re not perfect, but they mark a shift toward kernel-layer observability that defenders haven’t had before. The keyinsight from this stage of research isn’t just that eBPF can be abused; it’s that the boundary between instrumentation and intrusion is thinner than anyone expected. BPFDoor didn’t exploit a vulnerability; it exploited design trust. That’s the real challenge for Linux security moving forward: building visibility into the parts of the kernel that were never designed to hide, yet now can. The Visibility Gap: Why Linux Defenders Miss eBPF Activity Most Linux defenses still look in the wrong place. Antivirus and EDR tools monitor user-space processes, file activity, and network sockets, but eBPF operates at a lower level. Once a program attaches to the kernel, it can operate quietly through tracepoints, kprobes, traffic control, or XDP hooks without ever triggering a user-space alert. That invisibility is built into the design. Syslog and auditd don’t log eBPF activity. Even when malicious code runs in kernel space, nothing looks out of place. A defender checking system logs will see silence. Run bpftool prog show, however, and the truth appears: active programs that don’t exist anywhere else in process listings or endpoint dashboards. The LinuxSecurity.com research team confirmed this gap while reviewing open detection frameworks such as Windshock, which notes plainly: “Linux antivirus solutions cannot monitor in-kernel eBPF activity.” The issue isn’t a lack of sensors; it’s that existing telemetry doesn’t reach deep enough to see where eBPF runs. This visibility problem is uniquely Linux. On other platforms, comparable hooks are exposed through monitored APIs or signed driver models. eBPF executes inside trusted kernel space, under the same permissions as the system itself. That makes it both powerful and opaque, a place where normal defensive tooling simply can’t see. Safe Enumeration and Detection Building visibility into eBPF activity doesn’t require intrusive scans or exploit testing — it starts with basic enumeration. Every Linux defender should be able tolist what’s loaded into kernel space and confirm that what’s running matches what should be there. Step 1 — Enumerate Active Programs and Maps Use bpftool to list loaded programs and pinned maps directly from kernel space: bpftool -j prog show > /tmp/ebpf-progs.json bpftool -j map show > /tmp/ebpf-maps.json ls /sys/fs/bpf These commands capture every active eBPF object, even those invisible to user-space process listings or traditional endpoint agents. Step 2 — Correlate Activity with Runtime Tools Pair bpftool with runtime detectors such as Tracee or Falco to flag load and attach events in real time. Compare that telemetry with system logs or audit data to confirm what conventional visibility misses. Step 3 — Establish a Baseline Export eBPF program and map data as JSON artifacts and track them over time. Regular snapshots help identify unauthorized loads or configuration drift before they turn into persistence. All of these steps are non-invasive and safe to run on production systems. They don’t modify kernel state or interfere with running workloads. For enterprise Linux environments, this method is the foundation of eBPF visibility — a low-friction way to prove what’s really running at the kernel level and start closing one of Linux security’s most persistent blind spots. Linux Security Controls to Apply Visibility only matters if it leads to action. Once defenders understand how to identify eBPF activity, the next step is to minimize its potential for abuse without disabling it entirely. These are Linux-native controls that keep eBPF functional for observability while closing the paths attackers use most. Restrict Capabilities and Privileges Limit what processes can do before they ever reach the kernel. Drop CAP_SYS_ADMIN from containers and user namespaces wherever possible. Disable unprivileged eBPF loading with: echo 1 > /proc/sys/kernel/unprivileged_bpf_disabled This ensures only trusted, privilegedprocesses can load kernel-level programs. Hardened Kernel Configuration Enforce mandatory access controls that limit direct BPF system calls. SELinux and AppArmor can both confine eBPF execution through targeted policy modules. These settings block unauthorized probes and ensure only approved daemons can attach to tracepoints or sockets. Enhance Monitoring and Detection Extend runtime monitoring with tools like Tracee or Falco. Create or import rules that flag unexpected eBPF program loads, unloads, or unusual attachment events. When correlated with baseline data, those alerts mark the difference between legitimate observability and suspicious kernel activity. Validate with Forensic Snapshots When compromise is suspected, virtualization or hypervisor snapshots provide a clean way to examine persistent kernel-resident eBPF programs. Memory forensics has proven effective for uncovering these implants in controlled environments. Periodic forensic validation ensures that visibility doesn’t stop when systems are offline. Apply Trusted Guidance The eBPF Foundation’s threat model and verifier audit outlines clear deployment recommendations, including tokenized verifier access and limited unprivileged BPF operations. Following those guidelines keeps implementations aligned with upstream security priorities. With these controls in place, Linux defenders can maintain the visibility eBPF was built for while denying attackers the invisibility they’ve learned to exploit. The Future of eBPF in Linux Security eBPF isn’t going away; it’s becoming essential. Modern observability and security frameworks depend on it to trace kernel events in real time. But as adoption grows, so does attacker opportunity. Malicious eBPF programs can blend with legitimate telemetry agents, making detection harder for defenders focused only on the user space. Future defenses will hinge on stronger verification and provenance controls — kernel-level signing, token-based verifier access, andcontinuous inspection of runtime eBPF state. Research is already exploring these directions, including kernel-level hidden rootkit detection using eBPF itself, where the same framework that attackers abuse becomes the engine for defense. For that to work, Linux needs consistent telemetry standards that reach the kernel layer. SOCs and enterprise defenders must treat kernel-space observability as a first-class requirement, not a niche tool for debugging. eBPF’s future isn’t about restriction; it’s about clarity. The more transparent its operation becomes, the harder it is to hide within it. Securing the Kernel’s Own Security Engine eBPF was built to strengthen Linux visibility and control. It succeeded, but that same capability has created new terrain for attackers. What began as an observability framework has evolved into a critical security surface, one that defenders can no longer afford to ignore. The evidence forms a clear arc from verifier flaws and public rootkits to modern detection frameworks and confirmed operations like BPFDoor. Each phase reinforces one message: Linux security no longer stops at the kernel boundary. Defenders now face a dual responsibility: to use eBPF for visibility while ensuring that visibility extends to eBPF itself. The technology isn’t a threat to be removed, but rather a system to understand, instrument, and monitor. eBPF is the kernel’s own security engine. The challenge, and opportunity, for the Linux community is learning how to secure it with the same precision it was designed to provide. . Research reveals the potential misuse of eBPF in Linux; security measures are evolving to close these visibility gaps.. extended, berkeley, packet, filter, (ebpf), created, linux, observable, secure. . MaK Ulac
Thank you to the Crowdsec project for contributing this article. The official release of CrowdSec v.1.0.X introduces several improvements to the previous version, including a major architectural change: the introduction of a local REST API. . This local API allows all components to communicate more efficiently to support more complex architectures, while keeping it simple for single-machines users. It also makes the creation of bouncers (the remediation component) much simpler and renders them more resilient to upcoming changes, which limits maintenance time. In the new 1.0 release, the CrowdSec architecture has been deeply remodeled: All CrowdSec components (the agent reading logs, cscli for humans, and bouncers to deter the bad guys) can now communicate via a REST API, instead of reading or writing directly in the database. With this new version, only the local API service will interact with the database (e.g. SQLite, PostgreSQL and MySQL). In this tutorial, we are going to cover how to install and run CrowdSec on a Linux server: CrowdSec setup Testing detection capabilities Bouncer set up Observability Set up the environment The machine I used for this test is a Debian 10 Buster t2.medium EC2. To make it more relevant, let’s start by installing nginx: $ sudo apt-get update $ sudo apt-get install nginx Configure the security groups so that both secure shell (SSH) (tcp/22) and HTTP (tcp/80) can be reached from the outside world. This will be useful for simulating attacks later. Install CrowdSec Grab the latest version of CrowdSec: $ curl -s https://api.github.com/repos/crowdsecurity/crowdsec/releases/latest | grep browser_download_url| cut -d '"' -f 4 | wget -i - Here is the installation process : $ tar xvzf crowdsec-release.tgz $ cd crowdsec-v1.0.0/ $ sudo ./wizard.sh -i The wizard helps guide installation and configuration. First, the wizard identifies services present on themachine. It allows you to choose which services to monitor. For this tutorial, go with the default option and monitor all three services: Nginx, SSHD, and the Linux system. For each service, the wizard identifies the associated log files and asks you to confirm (use the defaults again): Once the services and associated log files have been identified correctly (which is crucial, as this is where CrowdSec will get its information), the wizard prompts you with suggested collections. A collection is a set of configurations that aims to create a coherent ensemble to protect a technological stack. For example, the crowdsecurity/sshd collection contains a parser for SSHD logs and a scenario to detect SSH bruteforce and SSH user enumeration . The suggested collections are based on the services that you choose to protect. The wizard’s last step is to deploy generic whitelists to prevent banning private IP addresses . It also reminds you that CrowdSec will detect malevolent IP addresses but will not ban any of them . You need to download a bouncer to block attacks. This is essential to remember: CrowdSec detects attacks; bouncers block them. Now that the initial setup is done, CrowdSec should be up and running. Deter attacks with CrowdSec By installing CrowdSec, you should already have coverage for common Internet background noise. Check it out! Attacking a web server with wapiti Simulate a web application vulnerability scan on your Nginx service using Wapiti, a web application vulnerability scanner. You need to do this from an external IP, and keep in mind that private IPs are whitelisted by default : ATTACKER$ wapiti -u [*] Saving scan state, please wait... Note ======== This scan has been saved in the file /home/admin/.wapiti/scans/34.248.33.108_folder_b753f4f6.db ... On your freshly equipped machine, we can see the attacks in the logs : My IP triggered different scenarios : crowdsecurity/http-path-traversal-probing : detects path traversal probing attempts patterns in the URI or the GET parameters crowdsecurity/http-sqli-probbing-detection : detects obvious SQL injection probing attempts patterns in the URI or the GET parameters Bear in mind that the website you attacked is an empty Nginx server . If this were a real website, the scanner would perform many other actions that would lead to more detections. Checking results with cscli Cscli is one of the main tools for interacting with the CrowdSec service, and one of its features is visualizing active decisions and past alerts. The cscli decisions list command displays active decisions at any time, while cscli alerts list shows past alerts (even if decisions are expired or the alert didn't lead to a decision). You can also inspect a specific alert to get more details with cscli alerts inspect -d (using the ID displayed in the left-hand column of the alerts list). cscli offers other features, but one to look at now is to find out which parsers and scenarios are installed in the default setup. Observability Observability (especially for software that might take defensive countermeasures) is always a key point for a security solution. Besides its "tail the logfile" capability, CrowdSec offers two ways to achieve this: Metabase dashboards , and Prometheus metrics . Metabase dashboard cscli allows you to deploy a new Metabase and Docker . Begin by installing Docker using its official documentation . If you’re using an AWS EC2 instance, be sure to expose tcp/3000 to access your dashboard. cscli dashboard setup enables you to deploy a new Metabase dashboard running on Docker with a random password. Prometheus metrics While some people love visual dashboards, others prefer different kinds of metrics. This is where CrowdSec’s Prometheus integration comes into play. One way to visualize these metrics is with cscli metrics : The cscli metrics command exposes only a subset of Prometheus metrics that are important for system administrators . You can find a detailed description of the metrics in the documentation. The metrics are split into various sections : Buckets: How many buckets of each type were created, poured or have overflowed since the daemon startup? Acquisition: How many lines or events were read from each of the specified sources, and were they parsed and/or poured to buckets later? Parser: How many lines/events were delivered to each parser, and did the parser succeed in processing the mentioned events? Local API: How many times was each route hit and so on? Viewing Crowdsec’s Prometheus metrics via cscli metrics is more convenient but doesn’t do justice to Prometheus. It is out of scope for this article to deep dive into Prometheus, but these screenshots offer a quick look at what CrowdSec's Prometheus metrics look like in Grafana. Defend attacks with bouncers CrowdSec's detection capabilities provide observability into what is going on. However, to protect yourself, you need to block attackers, which is where bouncers play a major part. Remember: CrowdSec detects, bouncers deter. Bouncers work by querying CrowdSec’s API to know when to block an IP . You can download them bouncers directly from the CrowdSec Hub : For this example, use cs-firewall-bouncer . It directly bans directly any malevolent IP at the firewall level using iptables or nftables Note: if you used your IP to simulate attacks, unban your IP before going further: sudo cscli decisions delete -i X.X.X.X Install the bouncer First, download the bouncer from the Hub: $ wget githubusercontent $ tar xvzf cs-firewall-bouncer.tgz $ cd cs-firewall-bouncer-v0.0.5/ The bouncer can be installed with a simple install script: The install script will check if you have iptables or nftables installed and prompt you to install if not. Bouncers communicate withCrowdSec via a REST API, so check that the bouncer is registered on the API. The last command ( sudo cscli bouncers list ) shows our newly installed bouncer. Test the bouncer Warning: Before going further, ensure you have another IP available to access your machine and that you will not kick yourself out. Using your smartphone's internet connection will work. Now that you have a bouncer to protect you, try the test again. Try to access the server at the end of the scan : ATTACKER$ curl --connect-timeout 1 curl: (28) Connection timed out after 1001 milliseconds See how it turns out from the defender’s point of view. For the technically curious, cs-firewall-bouncer uses either nftables or iptables. Using nftables (used on Debian 10 by default) creates and maintains two tables named crowdsec and crowdsec6 (for IPv4 and IPv6 respectively). $ sudo nft list ruleset … table ip crowdsec { set crowdsec_blocklist { type ipv4_addr elements = { 3.22.63.25, 3.214.184.223, 3.235.62.151, 3.236.112.98, 13.66.209.11, 17.58.98.156, … } } chain crowdsec_chain { type filter hook input priority 0; policy accept; ip saddr @crowdsec_blocklist drop } } table ip6 crowdsec6 { set crowdsec6_blocklist { type ipv6_addr } chain crowdsec6_chain { type filter hook input priority 0; policy accept; ip6 saddr @crowdsec6_blocklist drop } } You can change the firewall backend used by the bouncer in /etc/crowdsec/cs-firewall-bouncer/cs-firewall-bouncer.yaml by changing the mode from nftables to iptables (ipset is required for iptables mode). Get involved We would love to hear your feedback about this latest release. If you are interested in testing the software or would like to get in touch with the team, check the following links: our website GitHub repository Gitter . This localAPI allows all components to communicate more efficiently to support more complex archite. crowdsec, thank, project, contributing, article, official, release. . Brittany Day
Get the latest Linux and open source security news straight to your inbox.