Symbiote & BPFdoor Malware

31007098262?profile=RESIZE_400xeBPF (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.

31007098474?profile=RESIZE_710xUsing 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.

31007098486?profile=RESIZE_400xFortinet 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.

31007098493?profile=RESIZE_400xAsking 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.

31007098877?profile=RESIZE_584xThe 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.

31007098895?profile=RESIZE_710xThe 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.

31007099257?profile=RESIZE_584xIn 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.

31007099088?profile=RESIZE_710xThe 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    

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

E-mail me when people leave their comments –

You need to be a member of Red Sky Alliance to add comments!