微軟在今年一月 Patch 了一個 Win32k EoP CVE-2022-21882
這個漏洞本質上是去年初修補的 CVE-2021-1732 的 Patch Bypass,利用上甚至更簡單
0x00 - Patch Diff
透過 BinDiff 比對 Patch 前後的 win32kfull.sys
19041.1387 / 19041.1466 會發現幾個明顯的修改:
主要的變動在 xxxClientAllocWindowClassExtraBytes
及 xxxClientFreeWindowClassExtraBytes
:
Patch 在 User-Mode Callback 前後加上了檢查 ExtraFlag
是否被設置了 0x800
的 Flag
有在關注 Win32k 的話應該會馬上聯想到去年初的 CVE-2021-1732
0x01 - CVE-2021-1732 TLDR
CVE-2021-1732
已經有許多分析文章,是個邏輯漏洞造成的 Type Confusion,長話短說:
- Windows 中的每個視窗在 Kernel 中會對應到一個
tagWND
structure tagWND
中的pExtraBytes
會指向一塊對應 Windows Class 中cbWndExtra
大小的記憶體空間 而pExtraBytes
有兩種不同的存取方式- User-Pointer Mode: 指向 User-Mode Buffer,
pExtraBytes
直接指向記憶體位置 - Kernel-Offset Mode: 指向 Kernel-Mode Buffer,
pExtraBytes
儲存的是與 Kernel Desktop Heap 之間的 offset - 若
tagWND->ExtraFlag
設置了0x800
Flag,則代表該視窗為 Kernel-Offset Mode
- User-Pointer Mode: 指向 User-Mode Buffer,
- 透過
NtUserConsoleControl
可以將 User-Pointer Mode 的tagWND
切換為 Kernel-Offset Mode - 視窗建立過程中,
win32k!xxxCreateWindowEx
會透過xxxClientAllocWindowClassExtraBytes
呼叫 User-Mode Callback 申請記憶體並存在pExtraBytes
- Hook PEB 中的
KernelCallbackTable
可以劫持xxxClientAllocWindowClassExtraBytes
的執行過程 在 Callback 中呼叫NtUserConsoleControl
切換視窗的pExtraBytes
存取模式,並偽造回傳值,使pExtraBytes
為設計過的值 - 由於
tagWND->ExtraFlag
被設置了0x800
Flag,系統會認為ExtraBytes
儲存的是 Kenrel Offset,但實際上此時的pExtraBytes
已被任意控制 - 對此視窗呼叫
SetWindowLong
就可以在 Kernel Desktop 做相對位置的 OOB Write - 在 Kernel Desktop 排列好視窗,利用 OOB Write 改寫
tagWND
,轉換為任意讀寫
而微軟的 Patch 也很簡單,在 xxxCreateWindowEx
中呼叫完 xxxClientAllocWindowClassExtraBytes
後馬上檢查 tagWND->ExtraFlag
是否被設置 0x800
Flag
0x02 - Patch Bypass
Patch 在 xxxClientAllocWindowClassExtraBytes
上做了 ExtraFlag
的檢查, 表示除了 CVE-2021-1732
的方法之外,應該存在其他路徑能夠觸發 User-Mode Callback 來設置 0x800
Flag 並修改 pExtraBytes
一個簡單的 Cross Reference 就足夠找到關鍵:
xxxToolTipWndProc
/ xxxSwitchWndProc
/ xxxMenuWindowProc
/ xxxSBWndProc
中會透過 User-Mode Callback 替 pExtraBytes
重新申請記憶體
對照一下不同版本的 win32kfull.sys
可以發現其實這幾個函數都是呼叫 xxxValidateClassAndSize
,只是在這一版中被 inline:
只要能在指定的視窗觸發 xxxValidateClassAndSize
,並攔截 User-Mode Callback,就可以達成與 CVE-2021-1732
一樣的效果
0x03 - Exploit
要觸發這個漏洞相當簡單,只需要
- 建立一個有設定
cbWndExtra
的視窗 - Hook User-Mode Callback
- 觸發
xxxValidateClassAndSize
- 使用與
CVE-2021-1732
相同的手法,在 Callback 中呼叫NtUserConsoleControl
切換視窗的對pExtraBytes
的存取模式,並控制pExtraBytes
利用手法上與 CVE-2021-1732
大致相同,偽造 tagWND->spmemu
透過 GetMenuBarInfo
任意讀、SetWindowLong
任意寫,只有幾點差異需要注意
CVE-2021-1732
的 Callback 發生在視窗建立過程中,因此需要透過一些技巧預知將建立的視窗HWND
,而CVE-2022-21882
則不需要- 參考
CVE-2019-1458
得知可以透過NtUserMessageCall
觸發xxxSwitchWndProc
,最終執行到xxxValidateClassAndSize
xxxValidateClassAndSize
中會設置tagWND->cbWndServerExtra
,因此透過SetWindowLong
寫值時會需要 加上 0x10 offset
- 在利用結束前,我們弄壞的
pExtraBytes
會被系統嘗試 Free 掉,可以 hookxxxClientFreeWindowClassExtraBytes
解決
有了這些知識之後就能快速的把 Patch Diff 變成一個穩定的 Win32k EoP 了
Exploit for Windows 10 21H2 / 2021 December Patch: https://github.com/L4ys/CVE-2022-21882
0x04 - Summary
仔細分析尚未修正 CVE-2021-1732
時的舊版 win32kfull.sys
(19041.867),會發現 xxxSwitchWndProc
等函數並不會觸發 User-Mode Callback
而在後來的新版本中因為某些原因微軟新加入的程式碼又使用了xxxClientAllocWindowClassExtraBytes
及 xxxClientFreeWindowClassExtraBytes
來呼叫 User-Mode Callback 申請記憶體,才導致舊的漏洞又出現新的觸發路徑
這個漏洞其實非常顯眼,如果有嘗試分析過 CVE-2021-1732
以及逆向 Win32k
的話應該很容易發現
理所當然的, APT Group 也老早就發現用爽爽了