CVE-2022-21882

微軟在今年一月 Patch 了一個 Win32k EoP CVE-2022-21882

這個漏洞本質上是去年初修補的 CVE-2021-1732 的 Patch Bypass,利用上甚至更簡單

0x01 Patch Diff

透過 BinDiff 比對 Patch 前後的 win32kfull.sys 19041.1387/19041.1466 會發現幾個明顯的修改:

主要的改變在 xxxClientAllocWindowClassExtraBytes / xxxClientFreeWindowClassExtraBytes:

Patch 在 User Mode Callback 前後加上了檢查 ExtraFlag 是否被設置了 0x800 的 Flag

有在關注 Win32k 的話應該會馬上聯想到去年初的 CVE-2021-1732

0x02 CVE-2021-1732 TLDR

CVE-2021-1732 已經有許多分析文章,是個 Logical Bug 造成的 Type Confusion,長話短說:

  1. Windows 中的每個視窗在 Kernel 中會對應到一個 tagWND structure
  2. tagWND 中的 pExtraBytes 欄位會指向一塊對應 Windows Class 中 cbWndExtra 大小的記憶體空間
    pExtraBytes 有兩種不同的存取方式
    Mode1: 指向 User Mode Buffer,此時 tagWND->pExtraBytes 直接指向記憶體位置
    Mode2: 指向 Kernel Mode Buffer,此時 tagWND->pExtraBytes 的值為 Buffer 的記憶體位置與 Kernel Desktop Heap 之間的 offset
    tagWND->ExtraFlag 設置了 0x800 Flag,則代表該視窗為 Mode2
  3. 透過 NtConsoleControl 可以將 Mode1 的 tagWND 切換為 Mode2
  4. 視窗建立過程中,win32k!xxxCreateWindowEx 會透過 xxxClientAllocWindowClassExtraBytes 呼叫 User Mode Callback 申請記憶體空間並將結果存在 tagWNDpExtraBytes 欄位中
  5. 透過 Hook PEB 中的 KernelCallbackTable 可以劫持 xxxClientAllocWindowClassExtraBytes 的執行過程
    在 Callback 中呼叫 NtConsoleControl 切換視窗的 pExtraBytes 存取模式,並回傳任意值給 xxxClientAllocWindowClassExtraBytes,使 pExtraBytes 為設計過的值
  6. 最終的結果就是系統認為這個剛建立的視窗使用 Mode2 存取 ExtraBytes,但實際上此時的 pExtraBytes 已被任意控制,而不是系統認為的與 Desktop Heap 之間的 offset
  7. 對此視窗呼叫 SetWindowLong 就可以在 Kernel Desktop 做相對位置的 OOB Write
  8. 在 Kernel Desktop 排列好視窗,利用 OOB Write 複寫 tagWND 欄位,轉換為任意讀寫

TLDR 的 TLDR: 若呼叫 User Mode Callback 去替 pExtraBytes 申請記憶體空間,tagWND->ExtraFlag 被設置了 0x800 Flag 後就會出事

而微軟的 Patch 也很簡單,在 xxxCreateWindowEx 中呼叫完 xxxClientAllocWindowClassExtraBytes 後馬上檢查 tagWND->ExtraFlag 沒有被設置 0x800 Flag

0x03 Patch Bypass

Patch 在 xxxClientAllocWindowClassExtraBytes / xxxClientFreeWindowClassExtraBytes 上做了 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 一樣的效果

0x04 Exploit

要重新觸發 CVE-2021-1732 的漏洞相當簡單,只需要

  1. 建立一個有設定 cbWndExtra 的視窗
  2. Hook User Mode Callback
  3. 觸發 xxxValidateClassAndSize
  4. 使用與 CVE-2021-1732 相同的手法,在 Callback 中呼叫 NtConsoleControl 切換視窗的 pExtraBytes 存取模式,並控制 pExtraBytes

利用手法上與 CVE-2021-1732 大致相同,偽造 tagWND->spmemu 透過 GetMenuBarInfo 任意讀、SetWindowLong 任意寫
主要只有幾點差異需要注意

  1. CVE-2021-1732 的 Callback 發生在視窗建立過程中,因此需要透過一些技巧預知將建立的視窗 HWND,而 CVE-2022-21882 完全不需要
  2. 參考 CVE-2019-1458 得知可以透過 NtUserMessageCall 觸發 xxxSwitchWndProc,最終執行到 xxxValidateClassAndSize
  3. xxxValidateClassAndSize 中會設置 tagWND->cbWndServerExtra,因此透過 SetWindowLong 透過 OOB 寫值時會需要加上 0x10 offset
  4. 在利用結束前,我們弄壞的 pExtraBytes 會被系統嘗試 Free 掉,需要 hook xxxClientFreeWindowClassExtraBytes 解決

有了這些知識之後就能快速的把 Patch Diff 變成一個穩定的 Win32k EoP 了

Exploit for Windows 10 21H2 / 2021 December Patch: https://github.com/L4ys/CVE-2022-21882

0x05 Summary

仔細分析尚未修正 CVE-2021-1732 時的 win32kfull.sys (19041.867),會發現 xxxSwitchWndProc 等函數並不會觸發 User Mode Callback

而在後來的新版本中因為某些原因微軟新加入的程式碼又使用了xxxClientAllocWindowClassExtraBytes/xxxClientFreeWindowClassExtraBytes
來呼叫 User Mode Callback 申請記憶體,才導致舊的漏洞又出現新的觸發路徑

這個漏洞其實非常顯眼,如果有嘗試分析過 CVE-2021-1732 以及逆向 Win32k 的話應該很容易發現

理所當然的, APT Group 也老早就發現用爽爽了