eBPF 遙測資料與偵測向量

在 Linux 環境中運作的 Endpoint Detection and Response (EDR) agent ,高度依賴 Extended Berkeley Packet Filter (eBPF) 探針(Probe) YARA signature scanning ,來監控 Kernel 遙測資料並偵測未經授權的 Loadable Kernel Modules (LKMs) [1] 。標準的 Security Configuration 會利用 module 初始化時的 tracepoint ,以及檔案監控規則來檢查 taint flags、module signatures 與 filesystem artifacts [1] 。然而,複雜的 rootkit 架構顯示,當 eBPF 內部狀態結構暴露給 user-space 操作時,依賴 Kernel 內事件回報可能產生運作上的漏洞 [1]

Rootkit躲在哪裡?揭密eBPF與Linux Kernel的EDR規避攻擊鏈 | 資訊安全新聞

eBPF 遙測資料與偵測向量

現代 Linux Security agent 會透過 tracepoint attachment 監控 module 載入,例如在 Kernel module 初始化結束時觸發的 tp_btf/module_load hook [1] 。偵測機制會利用查詢規則驗證 module taint flags、signature verification 狀態,以及 filesystem 建立事件 [1] 。這套架構依賴三個主要的偵測層級 [1]

  • YARA Scanning: 對磁碟上的 .ko binary 結構進行靜態檢查,以比對已知 rootkit function 名稱、字串與程式碼模式 [1]
  • eBPF Tracepoint Hooking: 即時擷取 module_load tracepoint,並透過 BPF_CORE_READ(mod, taints) 等 Kernel helper function 取得 module metadata [1]
  • Filesystem Path Rule Monitoring: 追蹤特定副檔名(例如 .ko )的檔案建立事件,並檢查其是否位於指定的白名單建置更新之外 [1]

BPF Map Manipulation Bypass 的序列分析

啟用 tracepoint suppression 的主要機制,是修改維護受信任 Thread Group IDs (TGIDs) 的內部 eBPF hash maps [1] 。當 module_load tracepoint 觸發時,BPF 程式會使用呼叫端的 TGID 查詢內部 lookup table(例如 elastic_ebpf_events_trusted_pids [1] 。如果 TGID 存在於 map 中,eBPF 程式便會提早終止執行,阻止遙測資料產生 [1]

sequenceDiagram autonumber participant UserSpace as User-Space Loader Process participant BPFMap as eBPF Hash Map (trusted_pids) participant Kernel as Linux Kernel (finit_module) participant BPFProg as eBPF Probe (module_load) participant Agent as EDR Telemetry Service UserSpace->>UserSpace: 1. Discover target eBPF program & map ID UserSpace->>BPFMap: 2. Insert self TGID (map_upsert) UserSpace->>Kernel: 3. Invoke finit_module() syscall Kernel->>Kernel: 4. Execute do_init_module() Kernel->>BPFProg: 5. Trigger tp_btf/module_load tracepoint BPFProg->>BPFMap: 6. Lookup current TGID via bpf_map_lookup_elem BPFMap-->>BPFProg: 7. TGID match found BPFProg->>BPFProg: 8. Early exit / Abort telemetry collection Kernel-->>UserSpace: 9. Syscall completes successfully UserSpace->>BPFMap: 10. Remove TGID (map_delete) Note over BPFProg,Agent: No process.Ext.load_module event emitted

BPF Bytecode Inspection 的解構分析

對擷取出的 eBPF instructions 進行分析後,可以發現用來繞過 tracepoint event emission 的確切條件分支 [1] 。這組 instruction sequence 會取得呼叫端的 PID/TGID、進行轉換,然後執行 hash map lookup [1]

int module_load(unsigned long long ctx):
; int BPF_PROG(module_load, struct module *mod)
0: (79) r1 = *(u64 *)(r1 +0)
; if (bpf_ksym_exists(bpf_preempt_disable))
1: (7b) *(u64 *)(r10 -24) = r1
2: (18) r1 = 0x0
; u32 pid = bpf_get_current_pid_tgid() >> 32;
4: (85) call bpf_get_current_pid_tgid#257632
5: (77) r0 >>= 32
6: (63) *(u32 *)(r10 -16) = r0
7: (bf) r2 = r10
8: (07) r2 += -16
; u32 *val = bpf_map_lookup_elem(&elastic_ebpf_events_trusted_pids, &pid);
9: (18) r1 = map[id:33]
11: (85) call __htab_map_lookup_elem#299168
12: (15) if r0 == 0x0 goto pc+1
13: (07) r0 += 56

在第 4 行, bpf_get_current_pid_tgid() 會將目前作用中的 TGID 擷取至暫存器 r0 [1] 。第 9–11 行會查詢 map ID 33(代表 elastic_ebpf_events_trusted_pids [1] 。第 12 行會檢查 r0 中的 lookup pointer;如果 lookup 回傳非 null pointer,表示 TGID 存在,程式執行便會導向提早結束的 sequence [1]

Dynamic Map Interception 的 Loader 策略

由於 BPF map identifiers 會在 Security agent 初始化時動態配置,因此靜態 hardcoding map identifiers 會導致 agent 重新啟動後發生執行失敗 [1] 。可運作的 loader 程式會透過系統介面動態列舉作用中的 BPF objects,在執行 module insertion 前,識別符合特定 key size 條件(例如 key_size == 4 )的目標 hash maps [1]

  1. /* Dynamically insert process TGID into trusted_pids map before module load */
  2. uint32_t my_tgid = (uint32_t)getpid();
  3. map_upsert(map_fd, my_tgid); /* Insert TGID into map ID 33 before syscall */
  4. int ko_fd = open(argv[1], O_RDONLY | O_CLOEXEC);
  5. syscall(__NR_finit_module, ko_fd, "", 0);
  6. close(ko_fd);
  7. map_delete(map_fd, my_tgid); /* Remove TGID immediately after loading */

在觸發 __NR_finit_module 之前先執行 map_upsert ,loader 可確保 Kernel 在 do_init_module() 期間觸發 tp_btf/module_load 時,lookup 能夠成功並觸發提早結束路徑 [1] 。在 system call 完成後立即透過 map_delete 移除 TGID,可讓後續程序操作恢復預設的偵測行為 [1]

偵測規則分析與排除繞過

EDR 規則會同時評估程序事件與 filesystem artifacts [1] 。規則 cc3dab79 會檢查 tracepoint 遙測資料,並在 Kernel module taint flags 被填入時進行比對 [1]

  1. // EQL Detection Rule: Kernel Module Loaded with Tainting Flags
  2. process where host.os.type == "linux" and event.type == "start" and
  3. process.action == "load_module" and
  4. process.Ext.load_module.taint_flags != null

當 eBPF tracepoint 透過 TGID injection 被繞過時,規則 cc3dab79 不會收到任何 event stream [1] 。然而,次要的 filesystem 規則,例如規則 1965eab8 ,仍會監控磁碟區上的 .ko 檔案建立事件 [1]

  1. /* File Creation Rule for Kernel Objects */
  2. event.category: file and host.os.type: linux and event.type: creation and file.extension: ko and
  3. not (
  4. file.path: (/tmp/mkinitramfs* or /var/cache/uptrack/ or /var/tmp/dracut.* or
  5. /build/ or /var/lib/dkms/* or /var/tmp/portage/ ...)
  6. or process.executable: ("/sbin/dracut" or process.name: "cpio" or "/usr/bin/pacman" or /snap/...)
  7. )

由於標準 Kernel module 編譯工具(例如 Dynamic Kernel Module Support 或 DKMS)會在標準更新下產生有效的 modules,因此 EDR 設定會明確排除 /var/lib/dkms/* 等更新,不對其中的建立事件產生警示 [1] 。將經過混淆的 module artifacts 直接編譯至白名單更新,可繞過 filesystem 建立監控規則 [1]

偵測向量與緩解措施比較矩陣

偵測機制 目標向量 / 條件 採用的規避技術
YARA Signature Scanning .ko 檔案中的靜態字串模式、hook 名稱與 symbols 自動化原始程式碼混淆與隨機化 identifier 名稱
Rule cc3dab79 (BPF Module Load) 透過 eBPF tracepoint 的 process.Ext.load_module.taint_flags != null finit_module 前將 loader TGID 插入 trusted_pids map
Rule 05cad2fb (Syslog Alert) Kernel syslog stream 中未簽署 module 的 taint 訊息 In-kernel syslog hooks 過濾 module 專用的 taint 字串
Rule 1965eab8 (.ko Creation) 主機 filesystem 上新的 .ko 檔案建立事件 將建置輸出導向白名單更新( /var/lib/dkms/

結論

依賴 eBPF 進行主機 Security 的架構,必須將 user-space 對內部 BPF map objects 的 Access Control 納入考量 [1] 。當 Security agent 與 user-space 程序共享可變動的 BPF maps,卻未強制執行嚴格的權限驗證時,遙測探針便容易受到狀態操作影響 [1] 。完整的防禦措施需要將 eBPF 狀態變更嚴格綁定至經過認證的 agent 程序,並將主機 Kernel logs 與 out-of-band monitoring channels 進行交叉比對 [1]