Lazy Project
Published on

CVE-2022-21882 - Win32k EoP Vulnerability

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

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

0x00 - Patch Diff

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

bindiff

主要的變動在 xxxClientAllocWindowClassExtraBytesxxxClientFreeWindowClassExtraBytes:

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

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

0x01 - CVE-2021-1732 TLDR

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

  1. Windows 中的每個視窗在 Kernel 中會對應到一個 tagWND structure
  2. 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
  3. 透過 NtUserConsoleControl 可以將 User-Pointer Mode 的 tagWND 切換為 Kernel-Offset Mode
  4. 視窗建立過程中,win32k!xxxCreateWindowEx 會透過 xxxClientAllocWindowClassExtraBytes 呼叫 User-Mode Callback 申請記憶體並存在 pExtraBytes
  5. Hook PEB 中的 KernelCallbackTable 可以劫持 xxxClientAllocWindowClassExtraBytes 的執行過程 在 Callback 中呼叫 NtUserConsoleControl 切換視窗的 pExtraBytes 存取模式,並偽造回傳值,使 pExtraBytes 為設計過的值
  6. 由於 tagWND->ExtraFlag 被設置了 0x800 Flag,系統會認為 ExtraBytes 儲存的是 Kenrel Offset,但實際上此時的 pExtraBytes 已被任意控制
  7. 對此視窗呼叫 SetWindowLong 就可以在 Kernel Desktop 做相對位置的 OOB Write
  8. 在 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

要觸發這個漏洞相當簡單,只需要

  1. 建立一個有設定 cbWndExtra 的視窗
  2. Hook User-Mode Callback
  3. 觸發 xxxValidateClassAndSize
  4. 使用與 CVE-2021-1732 相同的手法,在 Callback 中呼叫 NtUserConsoleControl 切換視窗的對 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 寫值時會需要 加上 0x10 offset
  1. 在利用結束前,我們弄壞的 pExtraBytes 會被系統嘗試 Free 掉,可以 hook xxxClientFreeWindowClassExtraBytes 解決

有了這些知識之後就能快速的把 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

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

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

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