eBPF (Extended Berkeley Packet Filter) is a very interesting kernel technology that lets users load tiny, sandboxed programs into the Linux kernel to inspect or modify network packets, system calls, and more. The technology was introduced in 2015 to replace the “old” BPF technology of 1992, which was no longer compatible with modern computer architectures (e.g., 64-bit). As usual, the technology was quickly noticed by malware authors, leading to the Bvp47 malware in 2015 and a collection of rootkits, such as Ebpfkit and TripleCross. However, due to the required skills needed to use or exploit eBPF, the malware remains rare (in number). Today, the malware environment mostly consists of two families: Symbiote and BPFDoor, both from 2021.[1]
eBPF malware in 2025 - It’s 2025. Have we gotten rid of BPF malware yet? Well, yes and no. Yes, so far, Fortinet analysts have detected all new variants of those families, and our customers are safe. But “no”, eBPF malware is not something of the past. In 2025, FortiGuard Labs detected 151 new samples of BPFDoor and three of Symbiote.
|
Affected Platforms: Linux Impacted Users: Linux-based network appliances or servers Impact: stealthy malicious C2 communication Severity Level: Medium |
The eBPF malware is not widespread. They were never meant to be, because their development requires much more skill than random scams or ransomware. eBPF malware is another class, and it is particularly dangerous because it is difficult to detect, efficient, and powerful. This makes it a common choice for state-sponsored malware (which is allegedly the case for BPFDoor).
Reversing BPF bytecode - BPF uses its own instruction set. It’s quite different from the well-known x86/ARM. eBPF ISA is intentionally minimal, using fixed-size 64-bit instructions (x86 and ARM instructions both use variable-length instructions).
When reversing malware with BPF bytecode, while the eBPF ISA is not “complex,” it adds another layer of difficulty to the reverse engineering process. A few tools exist: bpftool, an eBPF processor for IDA Pro, or the Capstone engine.
It is perhaps less known that Radare2 supports BPF bytecode natively. For example, let’s analyze a recent Symbiote sample from 30 July 2025. This sample is quite similar to earlier versions (read more here): at some point, the malware attaches a BPF filter to a socket. Except in this recent version, the BPF filter has changed.
Using Radare2, analysts located the eBPF bytecode object within the malicious binary. They knew its length was 352 bytes (by reversing the function that uses the bytecode). Radare2 then switches to the BPF architecture (e asm.arch) and disassembles the bytecode (pd). Radare2 easily disassembles it—ldh (load half word) and jeq (jump if equal) are typical BPF instructions.
C2 communication over non-standard ports - The BPF instructions show the new variant only accepts IPv4 or IPv6 packets for protocols TCP, UDP, and SCTP on ports 54778, 58870, 59666, 54879, 57987, 64322, 45677, and 63227. This is common for botnets: C2 communication over non-standard ports. Some security tools (e.g., basic firewalls, IDS or IPS detection engines) often focus on inspecting known ports (e.g., HTTP) but do not bother with unknown high ports. With traffic such as Symbiote’s, those tools are likely to log no traffic at all, or at best flag a few packets to an “unknown port,” which can be many legitimate things and might not get noticed.
In the original version of Symbiote, the malware accepted only TCP and SCTP packets, not UDP, and only on a smaller set of ports. A longer list of ports usually means the malware performs port hopping, i.e., if a given port is blocked or flagged, the malware uses another port from the list. The longer the list is, the harder it is for network administrators to write efficient rules that block ports without risking a False Positive.
As for accepting UDP packets, this also seems like an intelligent move: UDP is connectionless, so the malware can conveniently hop between UDP ports without establishing or closing a connection. Moreover, a classic IDS is natively more efficient over TCP, as many rules are tuned to TCP handshakes, while identifying malicious UDP sessions is more difficult.
Understanding BPF assembly: lower the bar with AI - Yes, Radare2 disassembles the BPF bytecode, but we still need to understand what the BPF instructions do. In the past, the solution would have been “RTFM.” Today, we can use Artificial Intelligence’s vast knowledge to assist us.
Fortinet provided the BPF bytecode to Claude AI. Note that, at that time, they incorrectly assumed the “decoding” was wrong. It’s just that researchers hadn’t switched to the BPF architecture in Radare2. Claude Sonnet 4.5 explained each instruction: the IPv6 packet jump, cases for SCTP, TCP, and UDP, etc.
The AI concluded with a summary explaining what the BPF bytecode does. Note the well-advised assumption that this is related to malware.
Asking the AI about the BPF bytecode is certainly far quicker than reading the eBPF ISA documentation. Nevertheless, always keep a critical eye on whatever the AI claims. Fortinet encountered several errors:
AI error #1. A different model told me the BPF bytecode was dropping the packet rather than accepting it. This was actually the opposite! AI error #2. In another attempt, it was told that the BPF bytecode was filtering port 2048. This was an error when understanding the bytecode: the hex value 0x0800 (2048) is the EtherType identifier for IPv4 (i.e., accepting IPv4 packets). There is no filtering on port 2048.
Comparing BPFDoor variants - Besides Symbiote, Linux/BPFDoor is the other significant malware family that abuses BPF. It appeared in 2021, but analysts found a significant number of new variants in 2025 (240 samples since 2021, 150 in 2025 alone). What are the differences? Are they just minor, or are there new features?
Let’s ask an AI to scout the area. Researchers set up an MCP server for Radare2: this arms the LLM with a Radare2 disassembler. Then it asked the LLM to compare an original analysis of BPFdoor of 2021 with a recent sample of June 19, 2025.
(82ed617816453eba2d755642e3efebfcbd19705ac626f6bc8ed238f4fc111bb0)
In the screenshot below, see (1) the initial prompt at the top and (2) the MCP server preparing to open the recent sample in the Radare2 disassembler.
The LLM + MCP server reverses the BpfDoor sample, in particular, the function named apply_bpf_filter, which configures a BPF filter on a socket. It then generates a rather extensive markdown analysis (see screen capture below). The cost of this analysis was 0.50 USD.
The most important part of the report boils down to the following table:
|
Feature |
2022 Version (Article) |
2025 Sample (bpfdoor.elf) |
Status |
Notes |
|
BPF Packet Filter |
IPv4 only (0x800) |
IPv4 + IPv6 (0x800, 0x86dd) |
✅ Enhanced |
Major upgrade - now supports IPv6 traffic monitoring |
|
Magic Packet Values |
UDP: 0x7255ICMP: 0x7255TCP: 0x5293 |
‘X’ (0x58) character + colon delimiter |
⚠️ Changed |
Completely different packet identification mechanism |
|
Packet Protocols |
ICMP, UDP, TCP |
ICMP, UDP, TCP (via AF_PACKET) |
✅ Same |
Still monitors all three protocols |
|
RC4 Encryption |
Present |
Present (sym.rc4 @ 0x0000169c) |
✅ Present |
Encryption layer still used |
|
Reverse Shell |
Yes (connect-back) |
Yes (sym.reverse_shell @ 0x00401562) |
✅ Present |
Connects back using /bin/sh |
|
Process Masquerading |
10 fake process names |
None found |
❌ Removed |
No masquerading strings in binary |
|
File Location |
/dev/shm/kdmtmpflush |
Not present |
❌ Removed |
No /dev/shm hardcoded paths |
|
PID Dropper |
/var/run/haldrund.pid |
Not visible |
❌ Removed |
No dropper file references |
|
Timestomping |
Yes (Oct 30, 2008 date) |
Not visible |
❌ Removed |
No timestomp code found |
|
Binary Self-Deletion |
Yes |
Not visible |
❌ Removed |
No self-deletion code found |
|
Environment Wipe |
Yes (clears envp) |
Not visible |
❌ Removed |
No environment clearing code |
|
Firewall Bypass |
iptables redirect rules |
Not visible |
⚠️ Unknown |
No iptables manipulation found |
|
Shell Port Range |
42391-43391 |
Not visible |
⚠️ Unknown |
No port range logic found |
|
Bindshell |
Yes (local shell) |
Only reverse shell visible |
⚠️ Changed |
Simplified to reverse shell only |
The table has been generated by AI. I have manually checked that point 1 (BPF Filter), point 2 (magic value), and point 5 (reverse shell) are true. Point 4 (RC4 encryption) is wrong/a hallucination: I haven’t been able to locate the RC4 code in the sample. The other points have not been checked because they carry less importance for this blog post.
BPF filter adds IPv6 support
In the BPFDoor sample, the BPF filter is set over a raw socket (type 3) in the function apply_bpf_filter.
In apply_bpf_filter, analysts did not have direct BPF bytecode as in Symbiote, but a structure of socket options which are interpreted as a BPF filter because the level is SOL_SOCKET and the optname is SO_ATTACH_FILTER. Specifically, those are actually classic BPF bytes.
The BPF filter structure is represented in C below:
struct sock_filter bpf_filter[] = {
{ 0x28, 0, 0, 0x0000000c }, // load the Ethernet type field
{ 0x15, 0, 4, 0x000086dd }, // is it IPv6? no: jump forward 4
{ 0x30, 0, 0, 0x00000014 },
{ 0x15, 0, 11, 0x00000011 }, // is it UDP? no: jump forward 11
{ 0x28, 0, 0, 0x00000038 }, // get destination port of UDP IPv6
{ 0x15, 8, 9, 0x00000035 }, // accept DNS port
{ 0x15, 0, 8, 0x00000800 }, // is it IPv4? no: jump 8
{ 0x30, 0, 0, 0x00000017 }, // get IPv4 protocol
{ 0x15, 0, 6, 0x00000011 }, // is it UDP (over IPv4)? no: jump 6
{ 0x28, 0, 0, 0x00000014 }, // get IPv4 fragment offset and flags
{ 0x45, 4, 0, 0x00001fff }, // if fragmented: jump 4
{ 0xb1, 0, 0, 0x0000000e }, // compute IPv4 header length
{ 0x48, 0, 0, 0x00000010 }, // get destination port
{ 0x15, 0, 1, 0x00000035 }, // if DNS, accept, otherwise reject
{ 0x6, 0, 0, 0x00040000 }, // jump here to accept
{ 0x6, 0, 0, 0x00000000 } // jump here to reject
};
Analyst confirmed that the sample now supports IPv6 packets. It will keep only UDP port 53 (DNS) traffic, over IPv4 or IPv6. This is a subtle way for malware to hide its presence: DNS traffic is frequent and usually not suspicious. The malware uses this for its own communications.
Conclusion - In 2025, Fortinet had new variants of Symbiote and BPFDoor, two malware families that abuse BPF. The reverse engineering of these samples and their comparison with older variants show that malware authors are enhancing their BPF filters to increase their chances of evading detection. Symbiote uses port hopping on high UDP ports, and BPFDoor provides IPv6 support.
Fortinet thanked Geri Revay for introducing analysts to eBPF Malware.
IOCs
dcfbd5054bb6ea61b8f5a352a482e0cf7e8c5545bd88915d3e67f7ba01c2b3d4 Linux/Symbiote.B!tr
82ed617816453eba2d755642e3efebfcbd19705ac626f6bc8ed238f4fc111bb0 Linux/BpfDoor.F!tr
This article is shared at no charge for educational and informational purposes only.
Red Sky Alliance is a Cyber Threat Analysis and Intelligence Service organization. We provide indicators-of-compromise information via a notification service (RedXray) or an analysis service (CTAC). For questions, comments, or assistance, please contact the office directly at 1-844-492-7225 or feedback@redskyalliance.com
- Reporting: https://www.redskyalliance.org/
- Website: https://www.redskyalliance.com/
- LinkedIn: https://www.linkedin.com/company/64265941
Weekly Cyber Intelligence Briefings:
REDSHORTS - Weekly Cyber Intelligence Briefings
https://register.gotowebinar.com/register/5207428251321676122
[1] https://www.fortinet.com/blog/threat-research/new-ebpf-filters-for-symbiote-and-bpfdoor-malware?lctg=141970831
Comments