摘要

這個報告針對一個廣泛部署的防毒軟體解決方案的 Kernel 驅動程式中發現的 Kernel 堆積溢位漏洞(Heap overflow vulnerabilities, 統稱為 CVE-2025-13032),提供了詳細的技術分析。本研究重點在於 aswSnx Kernel 驅動程式,該程式透過許多非特權使用者可存取的 I/O Control (IOCTL) Handle,暴露出一個複雜的攻擊面。該漏洞的核心在於處理使用者提供的資料(特別是 _UNICODE_STRING 結構)時,出現了一個關鍵的 Time-of-Check-Time-of-Use (TOCTOU) 或 double-fetch 缺陷。此缺陷允許 Threat actor 在兩次 Kernel 讀取之間操縱字串的 Length 欄位,從而導致一個可控制的 Kernel 堆積溢位基礎功能(primitive)。此分析強調該漏洞的技術機制和一般的攻擊基礎,可用於將本機權限提升 (Local Privilege Escalation, LPE) 至 System 使用者 [1]。

TOCTOU 陷阱:從 Antivirus 驅動程式剖析 Kernel 堆積溢位 LPE 攻擊手法 | 資訊安全新聞

1. 簡介與攻擊面

現代端點安全解決方案,例如防毒軟體,通常包含 Kernel 層級的組件來實施安全策略並監控系統活動。這些組件通常實作為 Kernel 驅動程式,會形成一個重大的攻擊面。原始文章中詳細描述的研究目標是 aswSnx Kernel 驅動程式,它是安全產品的 sandbox 機制的一部分 [1]。該驅動程式的介面透過大量的 IOCTL 呼叫暴露出來,因其可存取性和複雜性而被確定為一個有希望的目標。對該裝置的 Access Control Lists (ACLs) 進行初步分析後發現,非特權使用者可以存取 aswSnx 裝置,使其成為用於權限提升的本機攻擊的可行目標 [1]。

安全審核的主要目標是透過關注處理使用者控制資料的 Kernel 組件,來識別本機權限提升功能。這種方法優先尋找本機記憶體損壞漏洞,例如緩衝區溢位、整數運算問題和 double-fetch 缺陷,這些通常比深層邏輯錯誤或複雜的 Race Condition 更容易識別和利用 [1]。

2. 漏洞發現:Double-Fetch 缺陷

該漏洞是在一個 IOCTL Handle ( 0x82AC0204 ) 中發現的,該 Handle 負責處理使用者提供的字串。發現的關鍵在於觀察到該驅動程式直接在使用者記憶體上操作,而沒有先擷取並複製提供的結構到 Kernel 記憶體。這種做法引入了經典的 TOCTOU 漏洞,因為惡意使用者程序可以隨時修改使用者空間中的資料,即使在 Kernel 正在積極處理它時也是如此 [1]。

Kernel 使用 ProbeForRead 等函數來驗證使用者空間指標後再存取它們。雖然此函數確保了位址是安全的,可以存取,但如果資料被多次讀取,它不能保證資料的一致性。特定缺陷發生在處理 _UNICODE_STRING 結構時,其中的 Length 欄位被 Kernel 讀取兩次,為 Race Condition 創造了一個時間窗。

2.1. pseudocode 分析

以下源自於有漏洞函數的 pseudocode 說明了 double-fetch 問題 [1]。該漏洞橫跨兩個函數:IOCTL Handle ( sub_14005E99C ) 和一個負責記憶體分配和字串複製的輔助函數 ( sub_140071C6C )。

  1. // IOCTL Handler: sub_14005E99C (Simplified)
  2. __int64 sub_14005E99C(..., _UNICODE_STRING *unicodestring_user, ...)
  3. {
  4. // ...
  5. if ( !a1 && unicodestring_user )
  6. {
  7. // [0] First fetch: ProbeForRead checks the UNICODE_STRING structure itself (0x10 bytes)
  8. ProbeForRead(unicodestring_user, 0x10, 1u);
  9. // [1] Second fetch: ProbeForRead checks the string buffer's accessibility.
  10. // It reads unicodestring_user->Length (Length_1) to determine the size.
  11. ProbeForRead(unicodestring_user->Buffer, unicodestring_user->Length, 1u);
  12. }
  13. // [2] Call to helper function. The pointer to the user-space structure is passed.
  14. v17 = sub_140071C6C((__int64)v18, &v14[v23 + 13], unicodestring_user);
  15. // ...
  16. }
  17. // Helper Function: sub_140071C6C (Simplified)
  18. __int64 __fastcall sub_140071C6C(__int64 a1, _QWORD *a2, _UNICODE_STRING *unicodestring_user)
  19. {
  20. // [3] Third fetch: Reads unicodestring_user->Length (Length_2) to calculate allocation size.
  21. // If Length_2 > Length_1, the allocated pool is too small.
  22. PoolWithTag = ExAllocatePoolWithTag(PagedPool, unicodestring_user->Length + 16, 0x20786E53u);
  23. if ( !PoolWithTag )
  24. return 0xC000009A;
  25. // [4] Fourth fetch: Reads unicodestring_user->Length (Length_3) to determine copy size.
  26. // If Length_3 > allocated size, a heap overflow occurs.
  27. memmove(PoolWithTag, unicodestring_user->Buffer, unicodestring_user->Length);
  28. // ...
  29. }

關鍵漏洞出現在步驟 [1] 和步驟 [3] 之間。在步驟 [1] 中,Kernel 讀取 Length 欄位(我們稱之為 $L_1$)以驗證緩衝區的可存取性。在步驟 [3] 中,Kernel 再次讀取 Length 欄位(我們稱之為 $L_2$)以確定要分配的 Kernel pool 大小。如果 Threat actor 可以與 Kernel 競速並修改使用者空間的 Length 欄位,使得 $L_2 > L_1$,那麼分配的 pool 大小將基於較大的、惡意的 Length $L_2$。隨後,在步驟 [4] 中, memmove 操作使用對 Length 欄位的最後一次讀取 ($L_3$) 來確定複製大小。透過確保 $L_3$ 夠大,Kernel 複製的資料會超過分配的 pool 大小,導致 Kernel 堆積溢位 [1]。

2.2. Race Condition 和 Primitive

利用這個 TOCTOU 缺陷需要精確的 Race Condition。Threat actor 必須:

  1. 將初始 Length 欄位設定為一個小值 ($L_{initial}$),使其通過步驟 [1] 中的 ProbeForRead 檢查。
  2. 觸發 IOCTL。
  3. 在一個單獨的執行緒中,競速在 Kernel 執行步驟 [3] 和 [4] 之前,將使用者空間的 Length 欄位修改為一個大得多的值 ($L_{final}$)。
如果競速成功,Kernel 會分配一個小的緩衝區(基於 $L_{initial}$),但隨後會將大量資料(基於 $L_{final}$)複製到其中,從而實現越界寫入能力。這種技術是 Kernel exploitation 中的一種常見策略,通常需要像 Heap Spraying 這樣的技術來可靠地控制記憶體佈局並瞄準特定的 Kernel 物件 [2]。此類功能非常有價值,因為它們可用於破壞相鄰的 Kernel 結構,導致控制流 hijacking 或任意讀/寫能力,這是完整的 Local Privilege Escalation (LPE) exploit 的基礎 [3] [4]。

3. 攻擊策略與 Kernel 堆積塑形

成功利用 Kernel 堆積溢位需要仔細的堆積塑形,以確保溢位破壞一個可預測且有用的目標物件。在這種情況下,分配的 pool 使用 tag 0x20786E53u ('Snx '),這是 aswSnx 驅動程式特有的 [1]。

一般的攻擊策略涉及兩個主要階段:

  1. 堆積塑形 :Threat actor 必須對 Kernel 堆積進行梳理,將目標物件放置在有漏洞緩衝區的正後方。這通常是透過分配和釋放特定大小的物件來實現的,以創建受控的記憶體環境。
  2. 目標破壞 :越界寫入功能用於破壞相鄰目標物件的 metadata 或關鍵欄位(例如,函數指標或 Security Token)。
由此而生的能力,即對 Kernel 堆積的任意寫入,是一個強大的工具。一旦 Threat actor 獲得任意讀/寫能力,最後一步通常是找到並修改當前程序的 Security Token,使其與特權程序(例如,System)的 Token 相匹配,從而實現 LPE [3]。

3.1. 漏洞流程圖

下圖說明了有漏洞的流程和 Race Condition 時間窗:

graph TD
    A[User Process: Trigger IOCTL 0x82AC0204] --> B{Kernel: sub_14005E99C};
    B --> C["Kernel: ProbeForRead (Length_1)"];
    C --> D{User Process: Race Condition Window};
    D --> E["Kernel: ExAllocatePoolWithTag (Length_2)"];
    D --> F[Attacker Thread: Modify Length to L_final];
    E --> G["Kernel: memmove (Length_3)"];
    F --> E;
    F --> G;
    G -- If L_3 > Allocated Size --> H[Result: Kernel Heap Overflow];
    H --> I[Exploitation Primitive: Arbitrary Write];
    I --> J[LPE to System];

    style D fill:#450d03,stroke:#333,stroke-width:2px,color:white
    style F fill:#B6FAEF
    style E fill:yellow
    style E fill:Orange
    style H fill:red,stroke:#333,stroke-width:2px,color:lightgrey
        

圖表註釋: Race Condition Window (深紅色方塊) 是關鍵時期,此時 Threat actor 的執行緒 (淡藍色方塊) 必須在 Kernel 的分配 (黄色方塊) 和複製 (橘色方塊) 操作再次讀取該值之前,成功修改使用者空間的 Length 欄位。成功的競速直接導致 Kernel 堆積溢位 (紅色方塊)。

4. 結論

CVE-2025-13032 的發現突顯了 Kernel 驅動程式在沒有健全的輸入驗證和記憶體擷取機制的情況下,直接處理使用者提供資料所帶來的持續安全風險。 aswSnx 驅動程式中的 double-fetch 漏洞提供了一個強大的 Kernel 堆積溢位基礎能力,這是實現完整系統入侵的常見前兆。廠商的快速修補回應強調了此類 Kernel 層級缺陷的嚴重性。本分析可作為一個案例研究,說明在 Kernel 與使用者空間互動中,原子(Atomic)資料處理對於防止 TOCTOU 漏洞和確保系統完整性的重要性。