本文對歷史上的微軟IE 瀏覽器的影響較大0day做了梳理,討論了IE漏洞在攻擊防御技術(shù)上的進(jìn)化,以及記錄了此類漏洞前人們在歷史上遺留下來的對抗經(jīng)驗(yàn)和足跡。
在IE瀏覽器攻防已經(jīng)白熱化的進(jìn)入到第三個階段的時候筆者才進(jìn)入到IE瀏覽器攻防方面的研究學(xué)習(xí)。此時代號’Project Spartan’的微軟的Edge瀏覽器從IE11手中接過windows默認(rèn)瀏覽器的重?fù)?dān),使得服役將近20年的IE瀏覽器定格11這個歷史的大版本號上面,自覺對IE瀏覽器漏洞的歷史研究應(yīng)有一篇簡記,可供后來的初入行的安全研究者有所參考,故成此文,疏漏之處再所難免,敬請指正。
0×01 瀏覽器漏洞研究的前置背景
最近幾年,網(wǎng)絡(luò)安全研究的部分重心開始有由PC端向移動端傾斜的趨勢。但是在PC端的安全研究依然以瀏覽器IE/Chrome/Firefox/Spartan,Adobe的Reader/Acrobat/Flash系列作為技術(shù)研究深度的展現(xiàn)。當(dāng)然還有部分用于特定地區(qū)定向攻擊的文件格式漏洞也屬于一些黑客著重關(guān)注的領(lǐng)域,比如日本比較流行的字處理軟件ichiaro和在韓國地區(qū)比較流行的Hancom Office等字處理軟件系統(tǒng)以及WPS等字處理軟件方面的文件格式漏洞。
瀏覽器從誕生之初主要提供簡單的文檔閱讀功能.很少構(gòu)成網(wǎng)絡(luò)安全威脅,但隨著互聯(lián)網(wǎng)的高速發(fā)展,越來越多的功能集被加入到瀏覽器中。瀏覽器不僅需要像操作系統(tǒng)那樣,為閱讀文檔、觀看電影、欣賞音樂等傳統(tǒng)計(jì)算機(jī)應(yīng)用提供基礎(chǔ),也需要為社交網(wǎng)絡(luò)、網(wǎng)絡(luò)購物等新興互聯(lián)網(wǎng)應(yīng)用提供支持。瀏覽器在增加功能集的同時,也就帶來了更多的安全問題。而集成捆綁于系統(tǒng)中的IE瀏覽器可以占據(jù)市場較大份額,自然也成了眾矢之的。針對IE瀏覽器的攻防軍備競賽也就是在這種情況下拉開了帷幕。
0×02 IE瀏覽器漏洞攻防的幾個時代
1.1
緩沖區(qū)溢出和ActiveX控件時代(03年-08年)
03年-08年這是一個階段,這個階段時候的IE漏洞基本是以ActiveX控件造成的漏洞居多以及棧溢出漏洞還有一些簡單的堆溢出漏洞。比如IFRAME標(biāo)簽的單個超長SRC屬性導(dǎo)致的緩沖區(qū)溢出以及類似的棧溢出漏洞。
早一些的階段,常規(guī)的Fuzz 方法,無論是基于變形的還是基于生成的,比較適合應(yīng)用于二進(jìn)制格式的流數(shù)據(jù),特別是那些包含大量C 語言結(jié)構(gòu)類型的文件或網(wǎng)絡(luò)協(xié)議格式。由于格式解析代碼經(jīng)常不加檢查的使用數(shù)據(jù)流數(shù)據(jù)作為內(nèi)存操作的參數(shù),單點(diǎn)的畸形往往就足以觸發(fā)解析代碼中可能存在的處理漏洞:超長數(shù)據(jù)導(dǎo)致的緩沖區(qū)溢出、畸形數(shù)值導(dǎo)致的整數(shù)上溢和下溢、畸形索引值導(dǎo)致數(shù)組的越界訪問、畸形記數(shù)導(dǎo)致過量的內(nèi)存讀寫操作。其中對于超長數(shù)據(jù)導(dǎo)致的緩沖區(qū)溢出,樣本構(gòu)造起來相對簡單,傳入超長的值。所以這種類型的漏洞由于發(fā)掘起來相對簡單。
對早期的IE 0day 漏洞的歷史簡單做一下梳理,只包含了影響比較大的例子(圖1.1.1,圖1.1.2),有些漏洞可能并不是IE 本身的問題,但是以IE 為最主要的利用渠道。
圖1.1.1
圖1.1.2
1.2
時代關(guān)鍵字
防護(hù)方: DEP /ASLR / Stack Cookie
攻擊方: 棧溢出/簡單堆溢出/ROP/HeapSpray
A 緩沖區(qū)溢出
關(guān)于緩沖區(qū)溢出也簡單給出一個例子方便理解:
圖1.2.1
我們在VC6.0的編譯器編譯上述程序,執(zhí)行后結(jié)果如下:
圖1.2.2
可以看到main函數(shù)中只調(diào)用了foo這個函數(shù)。但是實(shí)際運(yùn)行中,bar函數(shù)也被調(diào)用了。
實(shí)際上因?yàn)閒oo函數(shù)處理不當(dāng),并且外部輸入超長,造成了緩沖區(qū)溢出后修改了foo()函數(shù)的返回地址從而導(dǎo)致程序執(zhí)行本不應(yīng)該執(zhí)行的bar()函數(shù)。而如果被覆蓋的返回地址是一串經(jīng)過精心編碼具有后門功能的shellcode,此時計(jì)算機(jī)即可被惡意攻擊者控制。
這只是一個簡單的C程序的范例,表現(xiàn)在瀏覽器當(dāng)中形式上多少有所不同,比方說IE瀏覽器支持的IFRAME標(biāo)簽,IFRAME標(biāo)簽的單個超長SRC屬性構(gòu)造超長數(shù)據(jù)可能就會導(dǎo)致的緩沖區(qū)溢出從而瀏覽器崩潰。
DEP和Security Cookie
從上面的緩沖區(qū)溢出我們可以看到,在特定的環(huán)境下只要控制輸入的數(shù)據(jù)超長然后覆蓋掉返回地址,只要此時程序崩潰就很容易感知目標(biāo)程序存在緩沖區(qū)溢出漏洞。然后在修改這串超長數(shù)據(jù)糅合上恰當(dāng)?shù)膕hellcode精確覆蓋返回地址就可以執(zhí)行我們的惡意代碼。只是留給攻擊者的這樣的大好時光極為短暫。微軟從Windows XP SP2開始提供DEP的支持。DEP全稱是Data Execution Prevention,可以分為硬件的DEP和軟件的DEP。但是目的都是一致的。阻止數(shù)據(jù)頁上代碼執(zhí)行。(圖1.2.3)
圖1.2.3
由于數(shù)據(jù)所在的內(nèi)存頁被標(biāo)識為不可執(zhí)行,即使程序溢出成功轉(zhuǎn)入shellcode的執(zhí)行,這個時候CPU就會拋出異常從而阻止惡意shellcode的執(zhí)行。
從這里也可以看到這一階段的攻擊者只是去覆蓋棧上的返回地址,試圖從棧上將惡意shellcode執(zhí)行起來。
考慮到攻擊是因?yàn)楦采w返回地址產(chǎn)生的,微軟在VS2008和之后的編譯器加入了一個編譯選項(xiàng)GS。也就是Security Cookie也可以稱為Stack Cookie。
圖1.2.4
可以看到在開始的時候會將一個security_cookie提前寫入到棧中。而在函數(shù)返回之前會檢查這個security_cookie是否被篡改。
圖1.2.5
一旦被篡改便會跳轉(zhuǎn)到異常執(zhí)行的流程:
(圖1.2.6)
當(dāng)然這里說的是棧中的情況。在堆溢出中也有相似的防護(hù)措施如Header Cookie。
盯上SEH
當(dāng)攻擊者發(fā)現(xiàn)覆蓋4字節(jié)返回地址(32位系統(tǒng))去執(zhí)行shellcode這種攻擊方法的門檻被抬高之后便又想出了新的方案。覆蓋SEH的Exception handler
其實(shí)在二進(jìn)制的攻防當(dāng)中所有的努力都是為了獲得哪怕一次控制EIP(RIP)的機(jī)會。而SEH恰好符合這個要求可以給攻擊者提供這個機(jī)會。SEH存放在棧內(nèi),故超長的數(shù)據(jù)就可能覆蓋掉SEH ,其中將異常處理函數(shù)的入口地址更改為shellcode的起始地址。由于溢出后產(chǎn)生的錯誤數(shù)據(jù)往往會觸發(fā)異常,而此時shellcode恰好可以得到一次被EIP指向的機(jī)會。
只是留給攻擊者的好時光依然極為短暫。在VS2003(.net)當(dāng)中支持了/SafeSEH的選項(xiàng),用于應(yīng)對針對S E H的攻擊。后來又進(jìn)一步推出了SEHOP。當(dāng)然這些措施需要XP SP2操作系統(tǒng)以及更新的操作系統(tǒng)還有DEP的配合。這兩種防護(hù)措施詳細(xì)展開又要占很多篇幅。感興趣的讀者可以自行學(xué)習(xí)。
需要說明的是64位的windows系統(tǒng)SEH已經(jīng)不是放在棧中了。想要通過棧溢出覆蓋異常處理例程來實(shí)現(xiàn)漏洞利用已經(jīng)是不可能的了。
ROP ASLR和HeapSpray技術(shù)
前面說到DEP技術(shù)即使返回地址被shellcode覆蓋,DEP也會去阻止shellcode的執(zhí)行。但是如果執(zhí)行的代碼是操作系統(tǒng)的庫本身提供的函數(shù)如直接使用libc庫中提供的system()函數(shù)來覆蓋程序函數(shù)調(diào)用的返回地址。然后傳遞重新設(shè)定好的參數(shù)使其能夠按照預(yù)期執(zhí)行。這種繞過DEP的攻擊方式稱為return to libc。
Return-to-libc 攻擊用庫函數(shù)的地址來覆蓋程序函數(shù)調(diào)用的返回地址,這樣在程序返回時就可以調(diào)用庫函數(shù)從而使攻擊得以成功實(shí)施。但是由于攻擊者可用的指令序列只能為應(yīng)用程序中已存在的函數(shù),所以這種攻擊方式的攻擊能力有限。并且攻擊只能在 x86 的 CPU 平臺中實(shí)施而對 x86_64 的 CPU 平臺中無效。這是因?yàn)閤86_64CPU 平臺中程序執(zhí)行時參數(shù)不是通過棧傳遞的而是通過寄存器,而 return-into-libc 需要將參數(shù)通過棧來傳遞。
由于這種 return-to-libc 攻擊方式的局限性,返回導(dǎo)向編程(Return-Oriented Programming, ROP)被提出,并成為一種有效的 return-to-libc 攻擊手段。返回導(dǎo)向編程攻擊的方式不再局限于將漏洞程序的控制流跳轉(zhuǎn)到庫函數(shù)中,而是可以利用程序和庫函數(shù)中識別并選取的一組指令序列。攻擊者將這些指令序列串連起來,形成攻擊所需要的 shellcode 來從事后續(xù)的攻擊行為。因此這種方式仍然不需要注入新的指令到漏洞程序就可以完成任意的操作。同時,它不利用完整的庫函數(shù),因此也不依賴于函數(shù)調(diào)用時通過堆棧傳遞參數(shù)。
一般我們通過immunitydbg配合mona的腳本插件提取rop的指令序列構(gòu)造rop chain。
Rop chain 展示:
圖1.2.7
Rop的出現(xiàn)一度使得在XP時代的攻擊者占據(jù)上風(fēng)。由于dll加載地址的固定。針對不同的IE版本然后提取rop chain的工作雖然讓人感覺無趣但是達(dá)成的攻擊效果的確不錯。但是作為防御一方的微軟的腳步并沒有停止。Vista系統(tǒng)的臃腫繁雜為人所詬病并且市場份額也并不高。但是從Vista系統(tǒng)引入的由Win7沿襲的ASLR機(jī)制卻結(jié)結(jié)實(shí)實(shí)的又一次提高了攻擊的門檻。
在Rop攻擊中,攻擊者可以事先預(yù)知特定的函數(shù)如system或者VirtualProtect的入口地址。這是因?yàn)樵赬P以及2000的操作系統(tǒng)上面,由于kernel32.dll這些動態(tài)鏈接庫加載地址是固定的,所以導(dǎo)致相關(guān)的函數(shù)入口地址也是固定的即攻擊者可以事先確定這些函數(shù)的入口地址。
當(dāng)然Rop這一技術(shù)有一個弊端就是針對不同的操作系統(tǒng)要編寫提取不同的rop chain。這使得兼容性并不是很好。
ASLR全稱Address space layout randomization,是系統(tǒng)級別的特性,率先在Vista操作系統(tǒng)中得到支持。它的原
理就是在 當(dāng)一個應(yīng) 用程序或動態(tài)鏈接庫 ,如 kernel32.dll,被加載時,如果其選擇了被ASLR保護(hù) ,那么 系統(tǒng)就會將其加載的基址隨機(jī) 設(shè)定 。這樣 ,攻擊者就無法事先預(yù)知動態(tài)庫,如 kernel32.dll的基址,也就 無法事先確定特定 函數(shù),如 VirtualProtect的入 口地址 了。
如果感興趣,可以自己寫一段簡單的C代碼打印出VC運(yùn)行庫的加載地址。會發(fā)現(xiàn)每次重啟之后win7下面VC運(yùn)行庫加載地址是變化的,但是XP系統(tǒng)VC的運(yùn)行庫加載地址就是固定的。
Heap Spray
ASLR在新系統(tǒng)上面的應(yīng)用又使得相當(dāng)長的一段時間在緩沖區(qū)溢出利用時代攻擊方陷入了弱勢。但是攻擊者發(fā)現(xiàn)之前很早就被提出的(2001年左右)heap spray正好可以解決這個問題?;驹?005年之后IE漏洞的利用很多都用到了Heap Spray的技術(shù)。
在緩沖區(qū)溢出的時候,我們能夠劫持覆蓋一個地址,從而使得程序崩潰,但是只使得程序崩潰這樣是沒有價值的接下來如何將程序的執(zhí)行流程交接到shellcode的手中,這就變成了一個問題。如果覆蓋到一個固定的地址比0x0C0C0C0C,0x0A0A0A0A,0x0D0D0D0D而恰好從這個地址開始布滿了我們的shellcode。這樣觸發(fā)漏洞的時候就轉(zhuǎn)入了我們的shellcode進(jìn)行了惡意代碼的執(zhí)行。
實(shí)際應(yīng)用當(dāng)中shellcode前面都是要加上一些slidecode的(滑板指令)。為什么要加入滑板指令而不是全用shellcode去填充呢。因?yàn)槿绻雜hellcode執(zhí)行成功,必須要準(zhǔn)確命中shellcode的第一條指令,如果整個進(jìn)程空間都是shellcode,反而精確命中shellcode的概率大大降低了,因?yàn)楸仨氁械谝粭l指令,加上slidecode之后,現(xiàn)在只要命中slidecode就可以保證shellcode執(zhí)行成功了,一般shellcode的指令的總長度在50-100個字節(jié)左右,而slidecode的長度則大約是100萬字節(jié)(按每塊分配1M計(jì)算),那么現(xiàn)在命中的概率就接近99.99%了。因?yàn)楝F(xiàn)在命中的是slidecode,那么執(zhí)行slidecode的結(jié)果也不能影響和干擾shellcode。但是如果單純使用0×90(NOP)指令進(jìn)行填充,因?yàn)楝F(xiàn)在使用較多的攻擊場景是覆蓋虛函數(shù)指針(這是一個多級指針),這種情況下如果你使用0×90來做slidecode,而用0x0C0C0C0C去覆蓋虛函數(shù)指針,那么現(xiàn)在的虛表(假虛表)里面全是0×90909090,程序跑到0×90909090(內(nèi)核空間)去執(zhí)行,直接就crash了。根據(jù)這個流程,你可以看到,我們的slidecode也選取0x0C0C0C0C就可以了。
(圖1.2.8)
大概大量分配內(nèi)存之后分別覆蓋到的地址是這樣的:
0x0A0A0A0A(160M),
0x0C0C0C0C(192M),
0x0D0D0D0D(208M)
網(wǎng)馬里面進(jìn)行堆噴時,申請的內(nèi)存大小一般都是200M的原因,主要是為了保證能覆蓋到0x0C0C0C0C地址。(圖1.2.9)
2.1 UAF時代
由于緩沖區(qū)類漏洞由于發(fā)掘起來相對簡單,在攻防對抗的時間長河中這類漏洞資源很快耗盡。08年之后釋放重利用這樣漏洞利用方式變成了IE漏洞的主流。逐漸在這幾年達(dá)到了高峰。對象畸形操作類的漏洞一般來說觸發(fā)漏洞需要一系列的操作。單個的操作,比方說對象的創(chuàng)建使用刪除都是正常的。導(dǎo)致問題的是對于對象操作的畸形的組合。由于沒有標(biāo)準(zhǔn)的章法可供參考,基于傳統(tǒng)的溢出類漏洞的發(fā)掘手段已經(jīng)不甚適用。
2.2 對象操作類漏洞原理
跟面向過程的編程語言不同,c++支持多態(tài)和繼承。支持這些機(jī)制的核心就是虛表。C++的(虛)函數(shù)指針位于一個全局?jǐn)?shù)組中,形成虛表。而指向這個虛表的指針(VSTR)一般位于對象實(shí)例在內(nèi)存中開始的4個字節(jié)(32位系統(tǒng)).
之后才是類中聲明的數(shù)據(jù)成員,一般按照聲明的先后順序排列。對于存在多態(tài)行為的類,子類的所有實(shí)例共享相同的虛表,但區(qū)別于父類的虛表。對于某個對象,其調(diào)用存在多態(tài)行為的某個函數(shù)時,會先通過虛表指針得到虛表.再根據(jù)函數(shù)在虛表中的偏移來得到相應(yīng)的函數(shù)指針,最后才會調(diào)用函數(shù)。
另外,對象所在的地址一般通過ecx等寄存器傳遞。因此.C++中調(diào)用存在多態(tài)行為的函數(shù)的反匯編代碼類似于如下序列:
(圖2.2.1)
我們以stackexchange上面Polynomial 給出的示范代碼對UAF做一下簡介。
如下例的這樣的一段C++代碼:
圖2.2.2
可以衍生為:
注意,當(dāng)執(zhí)行到Account_GetBalance的時候,由于Account_Destroy 函數(shù)的執(zhí)行myAccount指針指向的內(nèi)存已經(jīng)是一個不確定的狀態(tài)。如果此時能夠可靠的觸發(fā)Account_Destroy函數(shù)。并且填充一塊精心構(gòu)造的內(nèi)容到myAccount指針指向的內(nèi)存,時機(jī)在Account_Destroy執(zhí)行后Account_GetBalance執(zhí)行之前。很多情況下這是可能實(shí)現(xiàn)的。
Account_Create函數(shù)執(zhí)行之后分配了8個字節(jié)的內(nèi)存。Balance和transactionCount分別占據(jù)4個字節(jié),并且返回一個指向他們的指針。這個指針儲存在myAccount變量當(dāng)中。Account_Destroy釋放了這塊內(nèi)存,但是myAccount變量依然指向那個8字節(jié)的內(nèi)存。我們將39 05 00 00 01 00 00 00 這8字節(jié)內(nèi)容可靠的進(jìn)行內(nèi)存分配。由于舊的8字節(jié)內(nèi)存塊已經(jīng)被標(biāo)記釋放,所以內(nèi)存管理器有很大可能會用新分配的內(nèi)存去覆蓋掉舊的內(nèi)存塊到那8個字節(jié)已經(jīng)被釋放的內(nèi)存。這個時候Account_GetBalance函數(shù)被調(diào)用了,他會去讀取那8個字節(jié)的內(nèi)存塊,但是實(shí)際上那8字節(jié)的內(nèi)存塊已經(jīng)被我們覆蓋成了
Balance39050000(1337)
transactionCount01000000(1)
所以我們已經(jīng)越權(quán)執(zhí)行到了下一個函數(shù)。
當(dāng)然具體到IE當(dāng)中,由于對象繁雜,UAF就更為錯綜復(fù)雜。
瀏覽器中跟對象操作類漏洞相關(guān)的對象有DOM ,BOM ,JavaScript對象。我們以DOM對象的分配過程為例。
DOM(文檔對象模型)提供了操作HTML/XML文檔的接口。IE瀏覽器中跟DOM實(shí)現(xiàn)相關(guān)的代碼主要在mshtml dll中。mshtml中的CMarkup類負(fù)責(zé)構(gòu)造整個htmI樹結(jié)構(gòu),其成員函數(shù)CreateElement會調(diào)用全局的CreateElement函數(shù)束構(gòu)造不同標(biāo)簽對應(yīng)的元素。比如
對于每個不同的標(biāo)簽,IE測覽器內(nèi)部有不同的CTagDesc結(jié)構(gòu)。這些CTagDesc結(jié)構(gòu)中的其中一項(xiàng)就是對應(yīng)元素的CreateElement函數(shù)指針。因此,全局的CreateElement函數(shù),會根據(jù)不同的標(biāo)簽柬獲得對應(yīng)的CTagDesc結(jié)構(gòu),然后再從里面取得對應(yīng)該標(biāo)簽的CreateElement函數(shù)指針然后call過去進(jìn)行調(diào)用。具體可參看全局CreateElement函數(shù)的反匯編代碼,如圖2.2.6所示。
這里有一些小細(xì)節(jié),有的時候直接用IDA反匯編如mshtml dll這樣的dll文件的時候沒有找到對應(yīng)的符號表,可以先使用Symbol Type Viewer這樣的小工具將符號表下載下來放到跟dll同目錄然后再使用IDA對相關(guān)的dll文件進(jìn)行反匯編。
接下來,以CObjectElement為例,介紹其創(chuàng)建過程,其他Elenlent的創(chuàng)建過程類似。CObjectElement的初始化是在成員函數(shù)CrcateElement函數(shù)中完成的。創(chuàng)建過程如下:先分配內(nèi)存,然后調(diào)用構(gòu)造函數(shù),最后將返回的對象指針保存在傳入的CElemen**參數(shù)中。反匯編代碼如圖。
圖2.2.9
HeapAlloc進(jìn)行堆內(nèi)存分配,高版本的一些mshtml dll中可能是由ProcessHeapAllocClear這個函數(shù)進(jìn)行內(nèi)存的分配。傳給HeapAlloc的字節(jié)數(shù)是0E0h可知,當(dāng)前IE瀏覽器版本中的CObjectElement大小為E0h。
接下來調(diào)用CObjectEtemem的構(gòu)造函數(shù)完成CObjectElement對象的初始化,構(gòu)造函數(shù)會自動調(diào)用父類的構(gòu)造函數(shù)。調(diào)用完構(gòu)造函數(shù)后.會將新建的CObjcctElemenl對象指針保存在傳入的參數(shù)CElemen**中。這是通過代碼
movecx,[ebp+arg_8]
mov[ecx],eax
完成的。
IE瀏覽器采用引用計(jì)數(shù)束跟蹤DOM對象的生命周期。引用計(jì)數(shù)(Reference Counting)算法對每個對象計(jì)算指向它的指針的數(shù)量。當(dāng)有一個指針指向該對象時計(jì)數(shù)值加1 ,當(dāng)刪除一個指向酸對象的指針時,計(jì)數(shù)值減l。如果計(jì)數(shù)值減為0,說明不存在指向該對象的指針.此時就可以安全的銷毀潑對象。垃圾回收過程就是回收引用計(jì)數(shù)為0的對象。引用計(jì)數(shù)算法的優(yōu)點(diǎn)是算法實(shí)現(xiàn)簡單,并且進(jìn)行垃圾回收時無需掛起應(yīng)用程序,回收速度快。
缺點(diǎn)是出于每一次對對象的指針操作都要對對象的引用計(jì)數(shù)進(jìn)行更新,因此會減緩系統(tǒng)的整體運(yùn)行速度。另外,使用引用計(jì)數(shù)算法的每個對象都需要額外的空問存儲計(jì)數(shù)值。除此之外,引用計(jì)數(shù)算法的最大缺點(diǎn)是無法處理循環(huán)引用。循環(huán)引用指的是兩個對象互相指向?qū)Ψ?。此時兩個對象的引用計(jì)數(shù)都依賴于對方.因此始終無法減至0。
IE瀏覽器實(shí)現(xiàn)引用計(jì)數(shù)的核心就是IUnknown接口。該接口提供了兩個非常重要的特性:生存期控制與接口查詢。對象內(nèi)部通過引用計(jì)數(shù)來實(shí)現(xiàn)對象的生存期控制。調(diào)用程序不甩在意對象的內(nèi)部實(shí)現(xiàn)細(xì)節(jié).通過接口查詢即可獲得指向?qū)ο蟮闹羔槨E瀏覽器中的很多類都繼承于IUnknown。IUnknown有三個方法。
圖2.2.10
以上節(jié)介紹的標(biāo)簽的內(nèi)部實(shí)現(xiàn)CObjectElement類為例.該類最終繼承于CElement。而CElement繼承于CBase,CBase則實(shí)現(xiàn)了IUnknown接口。用戶要查詢
PrivateQueryInterface會先調(diào)用CElement::CreateTearOffThunk函數(shù)退回對象包裝后的指針.然后在接下來調(diào)用CCaret::AddRef函數(shù)(call eCX)增加引用計(jì)數(shù)。
而CElement::CreateTearOffThunk函數(shù)僅僅是簡單的調(diào)用全局的CreateTearOflThunk函數(shù)。全局的CreateTearOflThunk函數(shù)反匯編部分代碼如圖
圖 2.2.11
再來看看釋放引用時所做的工作。對于CElement,用戶不再需要其引用時,調(diào)用CElement::Release即可。CElement::Release是對CElement::PrivateRelease的封裝,而CElemem::PrivateRelease主要的工作是調(diào)用父類CBase的PrivateRelease函數(shù)。CBase::PrivateRelease負(fù)責(zé)減少引用計(jì)數(shù)。
實(shí)際上IE當(dāng)中這種對對象的創(chuàng)建和銷毀的場景比比皆是,這也是在緩沖區(qū)漏洞在IE上面幾近絕跡后UAF中興的基礎(chǔ)。
2.3 時代關(guān)鍵字
Deferred/Delayed Free Control Flow Guard Isolated Heap
上文已經(jīng)簡單的給出一個例子幫助理解UAF的成因和觸發(fā)了。但是由于IE中對象眾多調(diào)用關(guān)系復(fù)雜,微軟作為防守的一方并不能像挖掘緩沖區(qū)溢出漏洞一樣容易的窮舉并修復(fù)所有潛在的漏洞。但是微軟分別以發(fā)布補(bǔ)丁的方式在14年的6月和7月分別引入了隔離堆和延遲釋放的漏洞利用緩解措施。并且在Win8.1Update3和Win10中引入了新的機(jī)制Control Flow Guard。我們簡單記錄說明一下這些機(jī)制。
UAF的觸發(fā)和利用依賴于被釋放的對象的重用。利用的過程必須依賴非法IE對象被確定的分配和釋放。而隔離堆和延遲釋放分別在對象的分配和釋放的時候加入了保護(hù)。
在IE中CVE-2014-0282修補(bǔ)前CTextArea::CreateElement分配內(nèi)存的時候有這樣的代碼
圖2.3.1
漏洞修補(bǔ)之后代碼是這樣的。
圖2.3.2
可以比較明顯的看到存在UAF隱患的對象的內(nèi)存分配已經(jīng)單獨(dú)使用了隔離堆進(jìn)行內(nèi)存分配。
而延遲釋放是這樣的。正常的對象釋放使用HeapFree就立即釋放了,而加入延遲釋放之后需要被釋放的對象會被統(tǒng)一記錄然后根據(jù)規(guī)則再進(jìn)行延遲釋放。
再說一下CFG(Control Flow Guard)這個機(jī)制。CFG的機(jī)制是基于控制流完整性Control-Flow Integrity的設(shè)想。這里通過對二進(jìn)制可執(zhí)行文件的改寫,對jmp的目的地址前插入一個在改寫時約定好的校驗(yàn)ID,在jmp的時候看目的地址前的數(shù)據(jù)是不是我們約定好的校驗(yàn)ID,如果不是則進(jìn)入錯誤處理流程。
圖2.3.3
在Call的過程中會引入一個CFG的校驗(yàn)函數(shù)。CFG需要編譯器和操作系統(tǒng)的雙重配合。當(dāng)這個校驗(yàn)函數(shù)在不支持的操作系統(tǒng)上運(yùn)行的時候直接就return了。當(dāng)在被支持的操作系統(tǒng)(win10和win8.1 update3)的時候就會跳轉(zhuǎn)到一個ntdll里面的一個檢測函數(shù)。檢測的機(jī)制我們不在詳細(xì)展開。
由于在溢出漏洞和UAF的大部分利用當(dāng)中都依賴于覆蓋某個地址然后劫持程序的EIP跳轉(zhuǎn)到我們的惡意代碼的地址進(jìn)行執(zhí)行。CFG在控制非法地址跳轉(zhuǎn)方面直接斬?cái)嗔舜蟛糠致┒蠢玫目赡堋?/p>
3.1 后UAF時代
就目前來看,14年之后由于新的緩解措施的加入使得攻防雙方的優(yōu)勢幾乎一邊的倒向了微軟為首的防守者的陣營。
瀏覽器的漏洞利用已經(jīng)沒有固定的套路。如瀏覽器內(nèi)部的腳本引擎的設(shè)計(jì)錯誤導(dǎo)致從腳本層面突破IE而進(jìn)行漏洞的相關(guān)利用(CVE-2014-6332),對瀏覽器中flash插件的漏洞發(fā)掘利用得到ring3權(quán)限,然后配合對較老字體解析引擎代碼發(fā)掘出來的提權(quán)漏洞再進(jìn)行提權(quán)拿到系統(tǒng)權(quán)限(Hacking Team相關(guān)利用)。漏洞利用方式不一而足,有機(jī)會在修訂簡史的時候一并補(bǔ)充。
0×03 IE漏洞防護(hù)措施關(guān)鍵時點(diǎn)
2015年7月 CFG編譯器支持 VS2015 RTM版本引入/guard開關(guān)對Control Flow Guard特性提供編譯器支持。
2014年11月 CFG系統(tǒng)級別支持 Windows8.1 update3 對Control Flow Guard提供系統(tǒng)層面的支持,之后的windows系統(tǒng)均在操作系統(tǒng)層面支持該特性。
2014年7月 MS14-037補(bǔ)丁發(fā)布引入Delayed Free特性。
2014年6月 MS14-035補(bǔ)丁發(fā)布引入Isolated Heap特性。
2008年1月 SEHOP系統(tǒng)級別支持 發(fā)布vista Service Pack 1補(bǔ)丁包,引入對SEHOP特性的操作系統(tǒng)支持。自vista sp1后的windows系統(tǒng)均在操作系統(tǒng)層面支持該特性。
2007年1月 ASLR系統(tǒng)級別支持 windows vista系統(tǒng)引入對ASLR特性操作系統(tǒng)級別的支持。自vista后的windows系統(tǒng)均在操作系統(tǒng)層面支持該特性。
2006年年初 safeseh/stack cookie/aslr/dep編譯器支持 VS2005引入/safeseh編譯開關(guān)緩和溢出漏洞對seh的攻擊,引入/GS編譯開關(guān)插入Stack Cookie緩和對返回地址的攻擊,加入/dynmicbase編譯開關(guān)引入對ASLR特性的編譯器支持,加入/NXCOMPAT編譯開關(guān)引入對DE特性的編譯器支持。自VS2005之后的編譯器均支持上述編譯開關(guān)。
2004年8月 DEP系統(tǒng)級別支持。微軟推出XP Service Pack 2補(bǔ)丁包引入對DEP特性的操作系統(tǒng)支持,自XP SP2后的windows系統(tǒng)均在操作系統(tǒng)層面支持該特性
分享到微信 ×
打開微信,點(diǎn)擊底部的“發(fā)現(xiàn)”,
使用“掃一掃”即可將網(wǎng)頁分享至朋友圈。