1. 簡介

Linux Kernel 的網路子系統,特別是流量控制(Traffic Control, TC)框架,在管理網路封包流動方面扮演關鍵角色。在此框架中,使用了多種佇列規則(qdiscs)來調度(Schedule)、整形(Shape)和排列(Prioritize)網路流量。其中一種 qdisc 是 CAKE (Common Applications Kept Enhanced),目的在提供進階的擁塞控制和公平性。這份分析深入探討 Linux Kernel net/sched CAKE qdisc 中發現的一個關鍵 Use-After-Free (UAF) 漏洞,該漏洞可能導致本機權限提升 (LPE) [1] 。我們將探討此缺陷的技術細節、根本原因、觸發機制,以及潛在的攻擊向量,並參考一般的 Kernel 攻擊技術(例如 heap shaping)。

潛藏在 Linux Kernel 中的威脅: 藉由 Qdisc 失效指標達成 UAF 與 Heap Shaping | 資訊安全新聞

2. 技術背景:Qdisc 與流量控制

Linux Kernel 的流量控制系統使用 qdiscs 來管理封包佇列。Qdiscs 大致可分為 classless 和 classful。Classless qdiscs 執行簡單的佇列操作,而 classful qdiscs 則允許階層化組織以及對流量進行更細緻的控制。Hierarchical Fair Service Curve (HFSC) qdisc 是一個 classful qdisc 的例子,提供複雜的頻寬分配和延遲保證。另一方面,CAKE 是一個更現代的 qdisc,將 AQM (Active Queue Management)、流量整形和流量隔離(Flow isolation)等多項功能整合到單一且易於使用的機制中。瞭解這些 qdiscs 與 Kernel 記憶體管理之間的互動,對於理解 UAF 漏洞至關重要。Kernel 記憶體分配器(例如 page allocator (buddy allocator) 和 slab allocators (SLUB, SLAB, SLOB))是 Kernel 中分配與釋放物件的基礎 [2] 。在 Kernel 漏洞中,經常利用 heap shaping 等技術,透過操縱 Kernel 堆積佈局來放置特定物件以進行利用 [2]

3. 根本原因分析

CAKE qdisc UAF 漏洞的核心在於 cake_enqueue 函式中不一致的傳回值。具體來說,即使 cake_enqueue 因為緩衝區限制而捨棄封包,它仍可能傳回 NET_XMIT_SUCCESS 。當 CAKE 被用作像 HFSC 這樣的 classful 父 qdisc 下的子 qdisc 時,這種行為會產生關鍵的去同步化。

請參考以下來自 cake_enqueue 的簡化程式碼片段 [1]

  1. static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
  2. struct sk_buff **to_free)
  3. {
  4. // ... other code ...
  5. if (q->buffer_used > q->buffer_limit) { // [1]
  6. u32 dropped = 0;
  7. while (q->buffer_used > q->buffer_limit) {
  8. dropped++;
  9. cake_drop(sch, to_free); // [2]
  10. }
  11. b->drop_overlimit += dropped;
  12. }
  13. return NET_XMIT_SUCCESS;
  14. }

如程式碼所示,如果 q->buffer_used 超過 q->buffer_limit ,封包會經由 cake_drop 被捨棄 [1] 。然而,該函式無條件傳回 NET_XMIT_SUCCESS 。這意味著父 qdisc(例如 HFSC)會認為封包已成功進入佇列,即使它已被 CAKE 捨棄。這種差異是 UAF 狀態的初始觸發點。

4. 漏洞觸發機制

當 HFSC qdisc 配置在 CAKE qdisc 之上時,就會顯現 UAF 漏洞。導致 UAF 的事件序列如下:

  1. HFSC qdisc 將一個包排入(enqueue)其子 CAKE qdisc 的佇列。
  2. CAKE qdisc 的 cake_enqueue 函式因緩衝區限制而捨棄封包,但傳回 NET_XMIT_SUCCESS [1]
  3. HFSC qdisc 在不知道封包已被捨棄的情況下,繼續執行,彷彿入隊(enqueue)成功。如果這是特定 HFSC 類別的第一個封包,則會呼叫 init_ed 函式,將該類別加入 HFSC 的 active list [1]
  4. 隨後,該 HFSC qdisc 類別被移除,這會呼叫 hfsc_delete_class 。此函式轉而呼叫 qdisc_purge_queue 來清除子 CAKE qdisc [1]
  5. qdisc_purge_queue 呼叫 qdisc_tree_reduce_backlog 。此時,由於 CAKE qdisc 是空的(因為封包已被捨棄), qlen backlog 為 0 [1]
  6. 關鍵問題出現在 qdisc_tree_reduce_backlog 。因為 qlen 為 0,該函式的邏輯阻止了 qlen_notify 被呼叫 [1] 。這意味著 HFSC qdisc 的父類別沒有從其 active list 中移除,從而留下一個指向已釋放 HFSC 類別結構的 dangling pointer。

下圖說明了 qdisc 階層以及 dangling pointer 的產生:

graph TD A[Packet Enqueue] --> B{HFSC Qdisc} B --> C{"CAKE Qdisc (Child)"} C -- Drops Packet, Returns NET_XMIT_SUCCESS --> B B -- Believes Enqueue Success --> D[HFSC Class Added to Active List] D --> E[HFSC Class Removal] E --> F{hfsc_delete_class} F --> G{qdisc_purge_queue} G --> H{qdisc_tree_reduce_backlog} H -- qlen=0, No qlen_notify --> I[Dangling Pointer to Freed HFSC Class] I -- Later Access --> J[Use-After-Free]

5. 攻擊分析

HFSC active list 中指向已釋放 HFSC 類別的 dangling pointer 可被用於攻擊利用。當隨後呼叫 hfsc_dequeue 時,它會嘗試使用此 dangling pointer 來存取已釋放的 HFSC 類別 [1] 。這種 UAF 狀態允許攻擊者操縱已釋放的記憶體區域。利用過程通常涉及:

  1. Heap Shaping: 在 UAF 發生之前,攻擊者可以使用 heap shaping 技術來控制記憶體佈局。這包括分配和釋放特定大小的 Kernel 物件,以確保當 HFSC 類別被釋放時,它原先佔用的記憶體可以被攻擊者控制的資料重新分配 [2] 。例如,透過瞭解 Kernel 的 slab allocator 行為,攻擊者可以分配將會佔用已釋放 HFSC 類別記憶體的物件。這讓攻擊者能控制已釋放記憶體的內容,有效地將 UAF 轉換為受控的讀取/寫入基本功能(primitive)。hoeasys.com 上關於 ksmbd CVE-2025-37947 的文章提供了 Kernel 利用中 heap shaping 的絕佳概覽,詳細說明了攻擊者如何操縱記憶體分配器(page allocator 和 slab allocators)來放置特定物件以進行利用 [2] 。這涉及預先分配和控制堆積記憶體,以確保越界寫入(out-of-bounds writes)可以精確地損毀或操縱關鍵資料結構,或是透過耗盡特定的 free lists 來強迫分配器將目標物件放置在所需的位置 [2]
  2. RIP 控制: 當在 hfsc_dequeue 內存取已被釋放的 cl->qdisc 時,會發生 UAF。原始文章強調 qdisc_dequeue_peeked 藉由引用已釋放的 sch 來呼叫 sch->dequeue [1] 。藉由控制已釋放 Qdisc 結構的內容(透過 heap shaping),攻擊者可以用任意位址覆蓋 dequeue 函式指標。當 sch->dequeue(sch) 被呼叫時,它將執行攻擊者控制的程式碼,導致在 Kernel 空間執行任意程式碼,並最終實現本機權限提升。

以下來自 qdisc_dequeue_peeked 的程式碼片段說明了 RIP 控制點 [1]

  1. static inline struct sk_buff *qdisc_dequeue_peeked(struct Qdisc *sch)
  2. {
  3. struct sk_buff *skb = skb_peek(&sch->gso_skb);
  4. if (skb) {
  5. // ... other code ...
  6. } else {
  7. skb = sch->dequeue(sch); // [1]
  8. }
  9. return skb;
  10. }

在此程式碼中,如果 sch->gso_skb 為空,執行路徑會導致呼叫 sch->dequeue(sch) 。如果 sch 指向一個已被攻擊者重新控制的已釋放記憶體區域,攻擊者就可以覆蓋 Qdisc 結構內的 dequeue 函式指標。這允許攻擊者將執行流重新導向到任意位址,從而完全控制 Kernel 的執行環境。

6. 緩解與結論

Linux Kernel net/sched CAKE qdisc 中的漏洞凸顯了在複雜 Kernel 子系統中一致性狀態管理和錯誤處理的至關重要性。根本原因(來自 cake_enqueue 不一致的傳回值)展示了看似微小的邏輯缺陷在與 qdiscs 的階層性質結合時,如何演變成嚴重的 UAF 漏洞。為了緩解此類問題,Kernel 開發人員必須確保所有函式都能準確反映其運作結果,特別是在處理資源分配和釋放時。

一個潛在的修復方法涉及修改 cake_enqueue ,使其在封包因緩衝區限制而被捨棄時傳回錯誤代碼(例如 NET_XMIT_DROP ),而非 NET_XMIT_SUCCESS 。這將允許父 qdiscs(例如 HFSC)正確處理捨棄的封包,並防止 dangling pointer 的產生。此外,對已釋放物件進行更強健的檢查,以及在 dereferencing 指標前進行更嚴格的驗證,都有助於防止 UAF 狀態被利用。

總之,CAKE qdisc UAF 漏洞提醒我們,在確保複雜作業系統 Kernel 安全方面面臨著持續挑戰。它強調了細緻的程式碼審查、全面的測試以及對組件間互動深入理解的需求,以防止並識別可能具有重大安全影響的微小缺陷。對此類漏洞的利用通常依賴於像 heap shaping 這樣複雜的技術,這強調了 Kernel 內需要強健的記憶體管理和保護機制。