Rootkit躲在哪裡?
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] 。
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:
對磁碟上的
.kobinary 結構進行靜態檢查,以比對已知 rootkit function 名稱、字串與程式碼模式 [1] 。 -
eBPF Tracepoint Hooking:
即時擷取
module_loadtracepoint,並透過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]
。
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]
。
- /* Dynamically insert process TGID into trusted_pids map before module load */
- uint32_t my_tgid = (uint32_t)getpid();
- map_upsert(map_fd, my_tgid); /* Insert TGID into map ID 33 before syscall */
- int ko_fd = open(argv[1], O_RDONLY | O_CLOEXEC);
- syscall(__NR_finit_module, ko_fd, "", 0);
- close(ko_fd);
- 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]
:
- // EQL Detection Rule: Kernel Module Loaded with Tainting Flags
- process where host.os.type == "linux" and event.type == "start" and
- process.action == "load_module" and
- process.Ext.load_module.taint_flags != null
當 eBPF tracepoint 透過
TGID injection
被繞過時,規則
cc3dab79
不會收到任何 event stream
[1]
。然而,次要的 filesystem 規則,例如規則
1965eab8
,仍會監控磁碟區上的
.ko
檔案建立事件
[1]
:
- /* File Creation Rule for Kernel Objects */
- event.category: file and host.os.type: linux and event.type: creation and file.extension: ko and
- not (
- file.path: (/tmp/mkinitramfs* or /var/cache/uptrack/ or /var/tmp/dracut.* or
- /build/ or /var/lib/dkms/* or /var/tmp/portage/ ...)
- or process.executable: ("/sbin/dracut" or process.name: "cpio" or "/usr/bin/pacman" or /snap/...)
- )
由於標準 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] 。