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