作者:晏子霜
原文鏈接:http://www.whsgwl.net/blog/CVE-2018-8453_1.html
0x00: Windows10 1709 X64 無補丁

0x01: EXPLOIT編寫
非常感謝A-Team發表的漏洞分析以及 EXPLOIT 編寫文章,閱覽后受益匪淺,因此本文不再闡述漏洞細節,專注于EXPLOIT編寫.
通過上文(CVE-2018-8453從BSOD到Exploit(上))得知,觸發異常是因為調用win32kfull!xxxEndScroll函數釋放了由win32kfull!xxxSBTrackInit函數創建的tagSBTrack結構,接著win32kfull!xxxSBTrackInit函數再次釋放了tagSBTrack導致Double Free.
但是該漏洞的利用方案不唯一,在構造 EXPLOIT 的時候(1703),筆者首先嘗試的是通過釋放Kernel Pool讓保存tagSBTrack結構,接著win32kfull!xxxSBTrackInit函數再次釋放了tagSBTrack結構的塊與其他Free的塊合并使其變成更大的塊,并使用其他保存在Session Pool里的結構重引用這塊內存,通過win32kfull!xxxSBTrackInit函數再次釋放后,再次申請該結構,修改結構的某些位后轉換成ARW Primitives,但是不管筆者怎么操作都沒辦法合并,只能使用另外一種利用方法,因此實驗環境更新為Windows10 1709,不過在1703上利用該漏洞也是一樣的,1709的Poc稍作修改即可在1703上使用.
第二種方法也是A-Team在文章中提到的,通過win32kbase!HMAssignmentUnlock函數將父窗口引用徹底清0后,接著會從被釋放的保存tagSBTrack的內核池中讀取函數指針來調用win32kbase!HMAssignmentUnlock函數來解除引用的窗口,但是由于保存tagSBTrack結構的內核池已經被釋放了,所以我們可以去重引用這塊內存,并將其內容修改,通過win32kbase!HMAssignmentUnlock函數來構造一個任意地址 -1的漏洞,這樣就可以轉換成 ARW Primitives 了.

通過逆向win32kbase!HMAssignmentUnlock函數我們發現,該函數會將[[rcx]+8]處內存+(-1)也就是減1,正好我們可控rdx,這樣我們離System就更進了一步.

如何引用被釋放的內存呢,這塊內存為0x80字節大小,分配在Session Pool中,我們可以通過創建窗口類,設置lpszMenuName屬性,來分配任意大小的塊.

在EXPLOIT中,筆者分配了0x1000個TagCls結構,TagCls中保存指向lpszMenuName結構的指針,該結構分配Pool的大小為0x80,正好通過這個復用tagSBTrack結構被釋放的內存,通過設置MenuName的內容,我們現在已經得到了一個任意地址 - 1 Or -2的一個漏洞了,但是怎么樣才能轉化成任意地址讀寫呢?
這里我們可以考慮使用PALETTE調色板的結構,該GDI函數在Windows10 1709并沒有被Type ISOLaTion保護,并且也被分配在Session Pool中,所以我們可以通過泄露PALETTE結構的地址來覆寫其中的某些結構實現任意地址讀寫.
如何泄露調色板結構在內核中的地址呢,沒錯,還是用我們的lpszMenuName,這里我們通過MenuName創建一個0x1000字節的Pool,接著我們釋放該窗口類,這樣這塊內存就會被釋放為Free狀態了

但是問題來了,即使我們確定PALETTE結構可以重引用被釋放的Pool,但是我們如何獲取到MenuName的地址呢.
我們可以使用HMValidateHandle()函數,該函數有兩個參數,參數1為傳入的Windows Object句柄,參數2為句柄屬性,該函數會返回查找Windows Object結構在用戶態映射下的地址(用戶態桌面堆).
但是獲取了映射的桌面堆地址,此時依然無法獲取到TagCls在內核中的地址,不過TagWnd結構中保存了該結構在內核桌面堆中的地址,我們可以通過 (內核地址-用戶地址)得到一個偏移,通過偏移即可算出任意結構在內核桌面堆中的地址了.

在TagWnd結構中保存了TagCls結構的內核地址,我們只需要用偏移減去TagCls的內核地址就可以獲取到TagCls結構在用戶桌面堆的映射了.
通過保存在用戶桌面堆映射下的TagCls結構找到保存MenuName的地址這樣我們就可以得到PALETTE結構的地址了.

現在我們就要考慮,到底要修改 PALETTE 中的什么結構才能造成ARW PrimItives呢?眾所周知, Palette 結構中的 PFirstColor 指針指向保存調色板項的地址,修改 PFirstColor 指針即可任意地址讀寫.
但是問題來了,我們只能任意地址 -1 Or -2,雖然說可以連續觸發漏洞多次減,但是沒辦法直接修改一塊內存地址的內容為我們想要的值,這樣就不能修改 PFirstColor 了(其實也可以大家可以自行嘗試,畢竟思路是活的,類似修改 PFirstColor 指針指向上面的 PALETTE 結構,利用讀寫范圍去覆寫上面PALETTE 的 PFirstColor 指針),這里筆者修改的是 cEntries 結構,改結構為判斷調色板讀寫范圍的,修改了該結構后,可以導致調色板結構越界讀寫內存(OOB).

這里我們可以看到,筆者設置的 cEntries 為0x1D5,這樣會分配一個0x800字節大小的內核池,我們之前釋放了一個0x1000字節的,分配兩個0x800字節的,重新引用 MenuName 的內存,這樣分配在一起,修改 cEntries 結構造成越界讀寫,修改下面塊的 PFirstColor 即可任意內存讀寫了.

經過兩次-1后, PALETTE 結構的 cEntries 從0x000001d5變成了0xffffffd5,這樣我們就可以越界讀寫后面 PALETTE 結構的內容了!

首先越界讀取第二塊 PALETTE 結構的句柄,保存在 Data 里,判斷越界和池分配是否正常,如果正常再進行下一步,當然如果不正常肯定是藍屏的,除非在該進程中再次觸發漏洞修復異常的Kernel Pool.

通過 SetPaletteEntries() 以及 GetPaletteEntries() 函數,即可在Ring3來任意內存讀寫了,此時我們的任務基本上就完成了,但是還有一點很重要,就是如果這樣結束進程,那么一定會藍屏(BSOD),為什么呢,因為我們用了 TagCls 結構中的 lpszMenuName 來重新引用 tagSBTrack 這個被釋放的內存后,再次被 win32kfull!xxxSBTrackInit 給釋放了,如果我們不做點什么的話,結束進程會立刻藍屏.
不過不要怕,在寫EXPLOIT的時候筆者以及解決這個問題了,筆者創建了一個0x1000個項的 ULONG64 數組,保存了我們用于占位的 TagCls 中指向lpszMenuName 的地址,此時該派上用場了!

接著我們用調色板結構的任意地址讀寫將 TagCls 中保存 lpszMenuName 的結構賦值為0,這樣釋放 TagCls 時系統就會認為沒有申請 lpszMenuName ,因為保存 lpszMenuName 的結構為0.

處理好善后工作后本文就結束了,十分感謝大家的觀看,以及A-Team分享的文章 http://www.whsgwl.net/index.html
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1136/
暫無評論