背景
??CVE-2021-40449是一個存在于Win32k內核驅動中的UAF漏洞,該漏洞在2021年八月下旬九月上旬被Kaspersky發現用于野外攻擊活動中,通過Hook win32k驅動執行 NtGdiResetDC 程序中發生的用戶模式回呼,完成對目標物件的釋放和占用,最終實作指定內核函式的呼叫,以進行內核記憶體的讀寫操作,修改利用物件的Token權限,實作EOP,
分析
??此次分析是在Windows 10 1809中進行,
??首先在用戶模式呼叫 CreateDC 時,會執行至win32k內核呼叫 win32kfull!NtGdiResetDC ,再執行至 win32kbase!hdcOpenDCW ,呼叫堆疊如下:
......??win32kbase!PDEVOBJ::PDEVOBJ
......??win32kbase!hdcOpenDCW+0x240
......??win32kfull!GreResetDCInternal+0x11a
......??win32kfull!NtGdiResetDC+0xd6
......??nt!KiSystemServiceCopyEnd+0x25
......??win32u!NtGdiResetDC+0x14
......??gdi32full!ResetDCWInternal+0x16b
......??GDI32!ResetDCW+0x31
......??CVE_2021_40449!main
??執行的用戶回呼主要發生在 win32kbase!PDEVOBJ::PDEVOBJ 中,該函式應是一個 PDEV 物件的初始化函式,和 win32kfull!NtGdiResetDC 傳入引數中的 HDC 有關聯,初始化函式中有兩個用戶回呼: PDEVOBJ::EnablePDEV 、 PDEVOBJ::CompletePDEV ,這兩個用戶回呼主要是對 HDC 中的 PDEV 物件進行操作, PDEV 物件通過 PDEV::Allocate 分配記憶體,


??執行完初始化函式,回到 hdcOpenDCW ,繼續執行至 GreCreateDisplayDC ,該函式初始化一個 PDC 物件,并將上面初始化的 PDEV 物件的記憶體地址放到 PDC 偏移 +0x30 處,

??然后回傳 PDC 0 偏移處的 DC 句柄值 HDC ,該值也作為 win32kbase!hdcOpenDCW 的回傳值,回傳值 win32kfull!GreResetDCInternal ,
?? hdcOpenDCW 回傳的 HDC 傳入 DCOBJ::DCOBJ ,回傳 hdcOpenDCW 初始化的 PDC 物件的記憶體地址,

??接著讀取 PDEV 物件 0xAB8 偏移處的函式指標并執行,注意此處的 PDEV 并不是在上一步的 hdcOpenDCW 中初始化的,而是在用戶態呼叫 ResetDC 前,呼叫 CreateDC 生成的,為進行區分,本文中將其稱為 HDC_user ,
?? GreResetDCInternal 的函式引數 HDC_user ,同樣通過 DCOBJ::DCOBJ 回傳 PDC_user 物件,該物件偏移 0x30 處為 PDEV_user 物件的記憶體地址,
??取 PDEV_user 偏移 0xAB8 處函式指標,執行 UMPDDrvResetPDEV ,傳入引數分別為 PDEV_user 和 PDEV_kernel 偏移 0x708 處的指標,指向各自的 DEVMODE 結構,這里同樣會發生一次用戶態函式回呼,不過該回呼不進行考慮,因為此漏洞利用范圍內,被利用的主要是該指標,

??完成 UMPDDrvResetPDEV 回呼后,執行 win32kbase!HmgSwapLockedHandleContents ,該函式會將 PDC_user 和 PDC_kernel 首部的 HDC 值和 PDC 的 參考計數值 進行了互換,從而完成 devmode 修改的功能,

??后面則是將兩個 PDC 物件的參考計數值分別減 1 ,并呼叫 win32kbase!bDeleteDCInternal 將 HDC_kernel 索引到的 PDC 物件偏移 0x30 處指標指向的 PDEV 物件參考計數值減 1 ,值變為 0 ,而又因為之前的 HmgSwap 操作,這里的 PDC 和 PDEV 實際都是用戶傳入的 HDC 原本指向的物件,
??根據MSDN所說,“當該計數器降至零,該物件就會被釋放”、“一旦句柄計數減為零,物件的名稱就會從物件管理器的命名空間中洗掉”,意味著該物件可以被占用,而 hdcOpenDCW 中又存在用戶回呼,在用戶回呼中再對相同的 HDC 執行一次 ResetDC ,那么該 HDC 對應 PDEV 物件參考值將減為 0 ,占用該 PDEV 物件后結束回呼,回到內核,
??至于漏洞的觸發點,在原本的 UMPDDrvResetPDEV 呼叫處,該呼叫發生在 hdcOpenDCW 之后,呼叫函式的地址從 PDEV_user 中獲取,通過占用,可以獲取到修改器呼叫目標為一個內核讀寫函式,

利用
??該UAF漏洞的利用主要為以下幾個步驟:
- 使用 NtQuerySystemInformation 獲取利用行程 Token.Privileges 在內核中的位置;
- 泄露出一個可以用于內核寫的內核函式,這里比較通用是 nt!RtlSetAllBits ;
- 構造一個 Fake_RTL_BITMAP ,作為 nt!RtlSetAllBits 函式引數,大多使用 ThreadName 的方式進行構造,不過同樣也可以手動申請一片用戶態記憶體進行構造;
- HOOK用戶回呼 DrvEnablePDEV (Hook DrvCompletePDEV 雖然可以成功占用,但執行不到漏洞觸發點),在Hook函式中對相同 HDC 再執行一次 ResetDC ,回傳后使用構造的 Fake Palette 去占用被釋放的 PDEV 物件,然后結束當前回呼;
- 漏洞觸發,當前行程權限位全部被啟用,完成提權,
??在Hook函式中完成占用后的記憶體布局前后對比如下所示:

??PDEV物件占用成功后,完成回呼,回傳 GreResetDCInternal ,可以看到成功地呼叫到 nt!RtlSetAllBits ,

?? nt!RtlSetAllBits 中僅將 rcx 作為引數,而漏洞觸發處的第一個引數 rcx 同樣可以通過占用指定,
?? nt!RtlSetAllBits 中取 rcx 地址 0x08 偏移處的 QWORD 作為寫入的目標地址,而 rcx 偏移 0 處的 DWORD 值整除 0x40 后作為計數值,每次向目標地址寫入 rax 暫存器的值, rax 固定為 0xffffffffffffffff ,



??POC代碼,
總結
??這次我分析這個漏洞時嘗試盡量不看網上公開的POC,僅根據Kaspersky的文章尋找漏洞位置,結果花了很多時間,遇到挺多問題的,比如尋找漏洞點時,不會出現 BSOD ,并且 !pool 不能馬上看到物件記憶體狀態變成 free ,還是去瞄了一些公開的POC,確認自己方向沒問題,
??emmm最后好歹自己完成了POC,雖然耗時長且代碼拉胯,相比那些優秀的POC通用性低,但是識訓也很多,起碼漏洞前后附近的代碼各個角落都翻了一遍,而且一些坑下次可以避免,
參考
[1] MysterySnail attacks with Windows zero-day
[2] CVE-2021-40449 Exploitation
本文來自博客園,作者:Bl0od
轉載請注明原文鏈接:https://www.cnblogs.com/zUotTe0/p/15948062.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/435397.html
標籤:其他
