概念說明
1、UAF漏洞
UAF漏洞全稱是use after free,free是指函數是在堆上動態分配空間后不再使用該數據從而被回收。但是由于程序員的一些不適當的操作,會導致攻擊者能夠操控已經被釋放的區域,從而執行一些byte codes。
利用uaf漏洞主要是注意幾點:
1)在free()函數被調用回收buffer之后,指向該地址的指針是否被重置。
2)后續是否存在malloc()函數可以將新申請分配的空間分配到之前被free()回收的buffer區域。
3)利用沒有被重置的指針進行攻擊
2、數據結構
我們先了解一下相關結構體的定義,參考自NT4.0的源碼dcobj.hxx#L97、dcobj.hxx#L236、dcobj.hxx#L1282、dcobj.hxx#L1686,另外HDC猶如其名,handle to device context,圖形設備信息對象的句柄。
3、C++ namespace(命名空間)關鍵字
在C++中,您可能會寫一個名為xyz()的函數,在另一個可用的庫中也存在一個相同的函數xyz()。這樣,編譯器就無法判斷您所使用的是哪一個 xyz()函數。因此,引入了命名空間這個概念,專門用于解決上面的問題,它可作為附加信息來區分不同庫中相同名稱的函數、類、變量等。使用了命名空間即定義了上下文。本質上,命名空間就是定義了一個范圍。
定義命名空間:namespace namespace_name {
// 代碼聲明
}
引用命名空間中的變量:name::code; // code 可以是變量或函數
漏洞背景
2021年10月12日,卡巴斯基博客公開披露了在8月下旬捕獲到的Windows內核提權0day漏洞的相關信息。該漏洞與Windows窗口管理和圖形化設備接口相關(Win32kfull.sys)。通過該漏洞可以讓普通權限用戶提權到system權限。
漏洞現狀
以下版本均受影響。卡巴斯基已經發現在野利用,且官方已發布補丁,請及時安裝相關補丁。
Windows Server, version2004/20H2(Server Core Installation)
Windows 10 Version1607/1809/1909/2004/20H2/21H1
Windows 7 for 32/64-bit Systems Service Pack 1
Windows Server 2008/2012/2016/2019/2022
Windows 11 for ARM64-based Systems
Windows 11 for x64-based Systems
Windows 8.1 for 32/64-bit systems
Windows RT 8.1
漏洞成因
win32kfull的NtGdiResetDC()函數中存在一個use after free漏洞,攻擊者可以利用該漏洞將權限提升到NT AUTHORITY\SYSTEM的權限。
由于此函數調用hdcOpenDCW(),該函數執行用戶模式回調,因此存在該缺陷。在此回調期間,攻擊者可以使用與之前相同的句柄再次調用NtGdiResetDC()函數,這將導致該句柄引用的PDC對象被釋放。
關于對象被釋放這一點,在微軟官方的文章中提到:“調用CreateDC的線程擁有創建的HDC。當這個線程被銷毀時,HDC不再有效。因此,如果您創建HDC并將其傳遞給另一個線程,然后退出第一個線程,第二個線程將無法使用HDC。”
然后,攻擊者可以用自己的對象替換句柄引用的內存,然后將執行傳遞回原始NtGdiResetDC()調用,該調用現在將在沒有適當驗證的情況下使用攻擊者的對象。這可以允許攻擊者操縱內核的狀態,并結合其他利用技術,以NT AUTHORITY\SYSTEM的身份獲得代碼執行。
在win32kfull!NtGdiResetDC中調用了win32kfull!GreResetDCInternal:
在win32kfull!GreResetDCInternal中調用了用戶態的win32kbase!hdcOpenDCW:
在win32kfull!GreResetDCInternal的85行,v11+0xAB8可以設置為我們想要執行的函數,同時v11+0x708和(new_dcobj[0] + 6) + 0x708i64兩個入參的值可以在用戶態函數中被修改。內核函數接受兩個用戶態的參數,因此可以利用此漏洞造成任意地址讀寫。
漏洞利用
根據卡巴斯基的揭露,在執行ResetDC的回調函數時,對相同的句柄再次調用ResetDC,即可觸發漏洞。利用此漏洞需要使用GDI palette對象和一個內核函數達到任意地址讀寫。可以利用NtQuerySystemInformation和EnumDeviceDrivers去泄露內核模塊地址,最終可以進行權限提升。為了便于理解,下文將結合exp的內容進行說明。
步驟0、獲取exp自身的token對象地址
準備后續提權所需的信息。先獲取exp自身的token、特權信息在內核中的地址等信息。
獲取exp自身的token方法如下:
進而篩選出PoolFlag=“ThNm”的堆。
安裝Windows WDK后會自動生成一個pooltag.txt。pooltag.txt里有poolflag相對應的內容。如下圖所示,不同的poolfalg標志代表了不同的數據類型。
步驟1、hook對應的函數
在下圖736行中,調用SetupUsermodeCallbackHook()函數來hook可用打印機驅動的回調表。
hook步驟:
1、枚舉可用打印機并獲取驅動信息。
2、遍歷每個驅動,如果找到目標函數則修改回調表。
步驟2、創建一個全局的DC對象并保存其句柄
步驟3、調用ResetDC函數
調用ResetDC函數,并向其傳遞全局DC的句柄。ResetDC函數執行系統調用NtDgiResetDC及其內部函數GreResetDCInternal并取得傳入的HDC所對應的PDC對象,然后會調用hdcOpenDCW函數。
步驟4、執行hook函數
??malloc()函數存在一個特性是會將新申請分配的空間分配到之前被free()回收的buffer區域。
??free()函數不會處理申請的內存空間的內容。所以數據會被保留下來。
調用ResetDC函數后會執行回調函數,由于已經hook對應的回調函數,所以代碼流程進入HOOK函數。
如上圖所示191行,再次執行ResetDC函數并傳入全局的HDC值,由于第188行已經把globals::should_trigger設為true,所以會按照正常的流程執行。
此時在GreResetDCInternal函數內會對該值對應的PDC對象進行釋放,造成該對象結構的錯誤。
步驟5、第二次ResetDC調用完成
完成調用后繼續運行到下圖所示的70行。
對應的匯編代碼如下:
_guard_dispatch_icall_nop的操作是jmp rax:
向上查看,可以看到rax=*(rbx+0xAD0):
而RBX寄存器的值則是使用由用戶層傳入的HDC創建的DCOBJ對象的指針。此時,所對應的PDC對象已經在第四步的過程中被釋放掉了,但是由于未對該對象進行判斷,此時可以進行任意內核函數的調用。整個過程如下圖所示:
由于前期已經設置token的0x40位置的Privileges,從而給winlogon進程添加SE_DEBUG_PRIVILEGE權限。
通過獲取TheadName從而來泄露其內核地址空間,進而調用RtlSetAllBits函數實現對Fake_RtlBitMapAddr中BitMapHeader buffer的設置。通過申請堆的大小和UAF中堆的大小相同,那么就可能申請到我們的這塊內存,從而進行堆噴射。
如下圖所示,正好獲取到構造好了這塊內存中的數據,最終實現指針的利用,從而達到提權的目的。
步驟6、向winlogon注入啟動cmd的shellcode
視頻演示
https://www.bilibili.com/video/BV1nM4y1P7nC/
參考鏈接:
1、https://hack-big.tech/2019/01/24/uaf漏洞原理實例淺析/
2、https://www.kaspersky.com/blog/mysterysnail-cve-2021-40449/42448/
3、https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-40449
4、https://www.cve.org/CVERecord?id=CVE-2021-40449
5、https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdca
6、https://ti.qianxin.com/blog/articles/CVE-2021-40449-vulnerability-recurrence-process/
轉載自安全客:https://www.anquanke.com/post/id/260841
下一篇:EXE文件內存加載