摘要
這份報告針對 Windows 作業系統中 Desktop Window Manager (DWM) 元件內的一個關鍵陣列越界(Out-of-Bounds, OOB) 漏洞 (CVE-2025-55681) 提供了深入的技術分析。該漏洞允許本機攻擊者藉由操作 Windows Composition Engine 的輸入,特別是針對筆刷繪製圖形構建程序,來達成本機權限提升 (LPE)。該錯誤的核心在於對使用者控制的索引缺乏適當的邊界檢查,而該索引被用於存取筆刷物件的內部陣列。分析詳細說明了受影響的呼叫鏈、使用者對陣列索引控制的機制,以及從 OOB 存取衍生出的潛在攻擊構件。
1. Desktop Window Manager 與 Composition Engine 簡介
Desktop Window Manager (DWM) 是一個組合式視窗管理員,負責繪製 Windows 桌面 [3] 的圖形使用者介面。與傳統的堆疊式視窗管理員不同,DWM 藉由為每個應用程式視窗管理專用緩衝區,並將其組合成最終桌面影像,來實現透明、即時縮圖和 3D 視窗切換等視覺效果。此組合程序高度依賴
Windows Composition Engine
,該引擎利用
Windows.UI.Composition
API 來管理視覺樹、動畫和效果 [4]。DWM 程序 (
dwm.exe
) 以高權限執行,這使得其程式碼中的任何漏洞都成為重大的安全疑慮,因為成功的攻擊會直接導致 LPE。
該漏洞於 2025 年底揭露 [1],是在
dwmcore.dll
程式庫中發現的陣列越界問題。這類漏洞特別危險,因為可以用來完成強大的記憶體構件,例如任意讀取和寫入,這是權限提升利用的基礎。
2. 漏洞技術分析
2.1 受影響的函數與呼叫鏈
陣列越界漏洞具體位於
dwmcore!CBrushRenderingGraphBuilder::AddEffectBrush
函數中 [1]。此函數是 DWM 用於構建視覺效果繪製圖形(特別是涉及筆刷的效果)的複雜程序的一部分。通往受影響程式碼的路徑深藏於 DWM 的內部邏輯中,從一個使用者可存取的函數開始:
CVisual::ProcessSetWindowBackgroundTreatment → CWindowBackgroundTreatment::Create → CWindowBackgroundTreatment::CWindowBackgroundTreatment → CWindowBackgroundTreatment::UpdateBackdropFlags → CBrush::GetBrushGraph → CEffectBrush::EnsureBrushGraph → CBrushRenderingGraphBuilder::Build → CBrushRenderingGraphBuilder::Build → CBrushRenderingGraphBuilder::AddBrush → CBrushRenderingGraphBuilder::AddEffectBrush
這條廣泛的呼叫鏈突顯了 DWM 效果處理的複雜性,其中看似無害的 API 呼叫最終可能導致記憶體毀損的基礎。
CBrushRenderingGraphBuilder
類別負責將高階效果描述轉換為低階繪製結構。
2.2 陣列越界條件
關鍵錯誤發生在
CBrushRenderingGraphBuilder::AddEffectBrush
內部,其中使用索引存取內部陣列時沒有經過充分的驗證。如原始分析中所述,受影響的程式碼行如下:
class CBrush* rdi_3 = arg2->BrushArray.m_pData[r12_2]; (2)
在此程式碼片段中,
BrushArray.m_pData
是
CBrush
指標的內部陣列,而
r12_2
是用於提領元素的索引。漏洞源於
r12_2
的值未針對
BrushArray
的邊界進行適當檢查,從而允許攻擊者讀取或寫入該陣列已分配記憶體區域之外的資料。
2.3 使用者對索引的控制
利用此 OOB 條件的關鍵在於控制索引
r12_2
值的能力。分析顯示
r12_2
衍生自 vtable 函數呼叫的傳回值,具體為
wuceffects!Windows::UI::Composition::CompiledEffect::GetInputMapping
[1]。
GetInputMapping
函數從一個最終由使用者控制的結構中擷取索引值。該函數的實作如下所示:
- +0x00 uint32_t Windows::UI::Composition::CompiledEffect::GetInputMapping(Windows::UI::Composition::CompiledEffect* this, uint32_t arg2, uint32_t arg3, bool* arg4)
- +0x00 {
- ...
- +0x43 rax = *(uint8_t*)(&rdx_1[rbx] + 4);
- +0x47 *(uint8_t*)arg4 = rax;
- +0x56 return rdx_1[rbx];
- +0x00 }
此函數傳回的值(即
r12_2
)是從
rdx_1
指向的使用者控制資料結構中讀取的,offset 由
rbx
(衍生自
arg3
)決定。
整個
CompiledEffect
物件(包含
GetInputMapping
存取的資料)是從使用者控制的共享記憶體區塊初始化的。這是藉由
CCompiledEffectTemplate::ProcessUpdate
函數呼叫
wuceffects!DeserializeEffectDescription
達成:
result_1 = DeserializeEffectDescription(SharedMemory, (uint64_t)*(int64_t*)((char*)arg3 + 0x10), &FlattenedEffectGraph); (3)
SharedMemory
緩衝區是由攻擊者的程式碼經由
CSharedSectionBase::ResolveAllocation
映射的,且資料大小也是由使用者控制的 (
(uint64_t)*(int64_t*)((char*)arg3 + 0x10)
)。藉由精心設計此共享記憶體中的資料,攻擊者可以確保
DeserializeEffectDescription
構建出包含索引
r12_2
任意值的
FlattenedEffectGraph
以及隨後的
CompiledEffect
物件,進而觸發
AddEffectBrush
中的 OOB 存取。
3. 資料流與漏洞觸發
為了說明從使用者輸入到 OOB 條件的路徑,下圖視覺化了關鍵的資料流。此流程展示了攻擊者控制效果描述的能力如何最終變成記憶體毀損構件。
SharedMemory] --> B(DeserializeEffectDescription) end subgraph DWM Core C[CCompiledEffectTemplate::
ProcessUpdate] --> B B --> D{FlattenedEffectGraph} D --> E[CBrushRenderingGraphBuilder::
AddEffectBrush] end subgraph Windows Composition Engine F[CompiledEffect::GetInputMapping] --> E end E --> G{Out-of-Bounds Access} D --> F F --> H["r12_2 (User-Controlled Index)"] H --> G style A fill:#f9f,stroke:#333,stroke-width:2px style G fill:#f66,stroke:#333,stroke-width:2px style H fill:#f99,stroke:#333,stroke-width:2px,font-size:12px style F fill:#ccf,stroke:#333,stroke-width:2px,font-size:12px style E fill:#ccf,stroke:#333,stroke-width:2px,font-size:12px style D font-size:12px style B font-size:12px %% Flow Description A -- Data --> B B -- Object Init --> D C -- Call --> B D -- Object Ref --> E E -- Call --> F F -- Index Return --> H H -- Index Use --> G E -- Array Ref --> G
該圖清楚地顯示了序列:攻擊者提供原始資料 (A),該資料被反序列化 (B) 為效果圖形物件 (D)。當 DWM 嘗試構建繪製圖形 (E) 時,它會向效果物件查詢 input mapping (F),從而傳回攻擊者控制的索引 (H)。此索引隨後在未經驗證的情況下用於存取
BrushArray
,導致 OOB 存取 (G)。
4. 利用構件(Primitives)與權限提升
在 DWM 這樣的高權限程序中,陣列越界漏洞對於攻擊者來說是非常有價值的構件。
CBrushRenderingGraphBuilder::AddEffectBrush
中的特定 OOB 存取是一個 OOB 讀取,隨後可能是 OOB 寫入,具體取決於對擷取到的
CBrush
物件進行的後續操作。
4.1 達成任意讀取/寫入
藉由控制索引
r12_2
,攻擊者可以導致程式從與
BrushArray
相鄰或相距甚遠的任意記憶體位置讀取指標。此讀取構件可用於洩漏敏感記憶體位址,例如指向 DWM 堆積或核心位址的指標,從而規避位址空間配置隨機化 (Address Space Layout Randomization, ASLR)。
更關鍵的是,OOB 存取會擷取指向
CBrush
物件 (
rdi_3
) 的指標。如果攻擊者能確保 OOB 索引指向包含偽造物件指標的記憶體位置,則後續對
rdi_3
的操作可能會導致任意函數呼叫。常見的攻擊技術涉及瞄準攻擊者可以控制的記憶體區域(例如,藉由 Heap Spraying 或其他記憶體分配技術),並在該處放置虛假物件結構。
如果 OOB 存取用於寫入,攻擊者可以覆寫相鄰的記憶體結構。例如,將鄰近物件的 vtable 指標覆寫為指向記憶體中受控的 gadget,是在 DWM 程序環境中達成任意程式碼執行的經典方法。鑑於 DWM 以高完整性程序執行,在其中執行程式碼實際上賦予了攻擊者 LPE。
4.2 堆積操作與 Grooming
成功利用此漏洞需要精確的 heap grooming。攻擊者必須以特定方式分配和釋放記憶體區塊,使目標
BrushArray
鄰近某個記憶體區域,當發生 OOB 存取時,該區域會產生指向受控記憶體分頁的指標。由於可以從低完整性程序存取
Windows.UI.Composition
API,這為 DWM 程序內的堆積操作提供了強大的向量,因為這些 API 是 DWM 記憶體分配的主要介面。控制
SharedMemory
區段大小和內容的能力進一步協助了此 grooming 程序。
利用 Payload 通常會涉及:
-
Heap grooming,將受控記憶體塊定位在
BrushArray附近。 -
設計
CompiledEffect資料結構以傳回指向受控區塊的 OOB 索引。 - 觸發 OOB 讀取以擷取虛假物件指標。
- 在虛假物件上觸發方法呼叫,以劫持控制流程並執行最終的 LPE Payload。
5. 緩解與結論
追蹤編號為 CVE-2025-55681 的漏洞已由廠商在安全更新 [2] 中修復。更新 Path 可能在索引
r12_2
用於存取
BrushArray
之前引入了邊界檢查,或者在效果描述的反序列化過程中實施了更嚴格的驗證,以防止建立越界索引。
此案例研究強調了與 Desktop Window Manager 等複雜、高權限元件相關的持續安全風險,特別是那些處理使用者提供資料結構的元件,即使是經由組合 API 間接處理。在未經驗證的情況下依賴使用者控制的資料來衍生陣列索引是一個基本的程式編寫錯誤,在高權限環境中,這會直接導致關鍵的 LPE 漏洞。未來對此類元件的開發和安全稽核必須優先考慮嚴格的輸入驗證和邊界檢查,特別是在資料從低權限環境跨越安全邊界進入高權限環境的地方。