是的,您并沒(méi)有看錯(cuò):這是一篇關(guān)于如何保護(hù)漏洞利用代碼本身的文章,而不是介紹如何保護(hù)您不受漏洞利用代碼攻擊的文章。
我一直有這樣的想法,即如何將漏洞利用代碼提升到一個(gè)新的水平,這不僅僅指漏洞利用過(guò)程,而且還包括漏洞利用之前和之后。這一次,我將編寫一個(gè)文章系列,詳細(xì)介紹如何保護(hù)漏洞利用代碼。危險(xiǎn)無(wú)處不在,包括黑客在內(nèi),因?yàn)樗麄円呀?jīng)失去了很多漏洞利用代碼。
在這個(gè)文章系列中,將探討不同的方法來(lái)檢測(cè)受攻擊的程序(在這種情況下是瀏覽器)是否正在被某種工具所分析,以便我們可以中止漏洞利用代碼,而不至于發(fā)生故障、崩潰和被檢測(cè)到。
為什么?
漏洞利用代碼是有價(jià)值的資產(chǎn),所以你自然希望盡可能長(zhǎng)時(shí)間地保護(hù)和持有它們。此外,大多數(shù)時(shí)候你也不希望被發(fā)現(xiàn)。但是為了實(shí)現(xiàn)這一點(diǎn),你需要想法設(shè)法保證漏洞利用代碼也不會(huì)被發(fā)現(xiàn)。
首先,野外的漏洞利用代碼之所以被發(fā)現(xiàn),通常是由于以下四個(gè)主要原因:
重用了其他漏洞利用代碼的部分,因此被檢測(cè)軟件察覺(jué)
因?yàn)椴豢煽慷罎ⅲ髞?lái)經(jīng)分析而曝光
由于程序被監(jiān)視和分析(蜜罐)而導(dǎo)致崩潰
你分享給了朋友,他在使用過(guò)程中被檢測(cè)到
因?yàn)槲覀冞@篇文章專注于第三個(gè)原因,所以我們首先來(lái)介紹一下PageHeap。
PageHeap是如何工作的?
PageHeap是SDK/WDK中提供的一個(gè)Windows工具,用來(lái)盡快檢測(cè)出進(jìn)程堆中的內(nèi)存破壞情況。
為了實(shí)現(xiàn)這一點(diǎn),它用另一個(gè)堆分配器替換了原來(lái)的那個(gè)分配器。這個(gè)分配器將通過(guò)VirtualAlloc進(jìn)行所有的內(nèi)存分配,使其至少返回一個(gè)指定大小(大多數(shù)系統(tǒng)中為4Kb)的內(nèi)存頁(yè)。
除此之外,返回的地址將指向內(nèi)存頁(yè)的末尾減去指定的大小。 因此,它能使任何堆緩沖區(qū)溢出到內(nèi)存頁(yè)的末尾。
這也防止了許多廣泛使用的技術(shù),這些技術(shù)依賴于特定的堆布局來(lái)以特定方式來(lái)布置內(nèi)存的分配。這些技術(shù)的名稱很多,比如Heap Massaging、堆風(fēng)水等。
所以,由于PageHeap打破了原來(lái)的布局,所以會(huì)導(dǎo)致大部分依賴某種堆棧布局的漏洞利用代碼崩潰。
如果這是您是初次接觸PageHeap的話,不妨在網(wǎng)上多搜索一些相關(guān)的資料,因?yàn)樗拇_是一個(gè)非常方便的調(diào)試工具。
如何檢測(cè)PageHeap?
受PageHeap影響的程序的行為的主要特點(diǎn)是,無(wú)論分配空間的大小是多少,堆分配都會(huì)慢很多。記住,堆早就為盡可能快地分配各種不同尺寸的內(nèi)存對(duì)象而進(jìn)行了相應(yīng)的優(yōu)化。
相反,使用PageHeap時(shí),每次分配都要通過(guò)VirtualAlloc向內(nèi)核提出請(qǐng)求,這就涉及上下文切換并在內(nèi)核中進(jìn)行相應(yīng)的處理。因此,使用PageHeap時(shí),與常規(guī)情況下分配大內(nèi)存塊的過(guò)程相比,它所用的時(shí)間會(huì)跟分配小的內(nèi)存塊的時(shí)間更加接近。
由于window.performance.now()計(jì)數(shù)器在大多數(shù)JavaScript引擎中都支持,因此可以用它來(lái)檢測(cè)這個(gè)時(shí)間的測(cè)量值,并且具有微秒的精度。
因?yàn)镃hrome和Firefox擁有自己的分配器,所以啟用PageHeap不會(huì)引起太多的變化(請(qǐng)記住,它會(huì)劫持原始的malloc / free函數(shù))。在這篇文章中,我們將重點(diǎn)介紹Windows環(huán)境中使用默認(rèn)分配器的兩種瀏覽器:IE11和Edge。
在尋找分配不同數(shù)量字節(jié)的函數(shù)的過(guò)程中,我遇到了Uint8Array,它是一個(gè)在低層使用了ArrayBuffer的TypedArray。
它的用法很簡(jiǎn)單,例如var buf = new Uint8Array(len)。通過(guò)跟蹤該函數(shù),無(wú)論是在IE中還是Edge中,當(dāng)創(chuàng)建ArrayBuffer時(shí),都會(huì)直接使用我們指定的值調(diào)用msvcrt!malloc。
對(duì)于小大值,這里分別使用0x10和0x1000,注意,大的值會(huì)觸發(fā)對(duì)VirtualAlloc的調(diào)用。我這里使用的方法是,嘗試在20ms內(nèi)為小型和大型的內(nèi)存分配任務(wù)分配盡可能多的Uin8Array。
好的,下面看看具體代碼!
function doFor(fun, time) {
var i = 0;
var store = new Array();
var startTime = performance.now();
do {
for(var j=0; j
store.push(fun());
i++;
} while((performance.now() - startTime)
return i;
}
function allocPageBA() {
return new Uint8Array(0x1000);
}
function allocSmallBA() {
return new Uint8Array(0x10);
}
var bigRet = doFor(allocPageBA, ALLOC_TIME);
var smallRet = doFor(allocSmallBA, ALLOC_TIME);
alert(bigRet);
alert(smallRet);
當(dāng)然,這段代碼不是很完美,因?yàn)槲覀冊(cè)跍y(cè)量?jī)?nèi)存分配時(shí)引入其他的分配任務(wù),肯定會(huì)帶來(lái)測(cè)量誤差。
為了解決這個(gè)問(wèn)題,我創(chuàng)建了一個(gè)對(duì)象,并預(yù)先分配了Array,然后將對(duì)象存儲(chǔ)在數(shù)組中,這樣就不會(huì)引起新的分配任務(wù)了。
另外,要防止分配的對(duì)象被垃圾回收器回收而釋放它們的內(nèi)存,這會(huì)在測(cè)量中引入另一個(gè)誤差。
function NoAllocStore(count) {
this.count = count;
this.array = new Array(count);
for(var i=0; i
this.array[i] = 0x41414141;
}
this.index = 0;
}
NoAllocStore.prototype.store = function(obj) {
if (this.index >= this.count) {
alert("bad");
throw false;
}
this.array[this.index] = obj;
this.index++;
}
最后的代碼在這里。為了這項(xiàng)實(shí)驗(yàn),運(yùn)行了許多次(準(zhǔn)確的說(shuō)是250次),并比較了在兩臺(tái)瀏覽器在禁用和啟用PageHeap情況下的分布情況。
在IE11中測(cè)量的分布情況為:

下面看看混合分布詳情:

非常明顯的是,啟用了PageHeap的時(shí)候分布更為密集,因此它對(duì)分配時(shí)間影響更大。 這可以歸因于內(nèi)存分配比堆分配更耗時(shí),使得代碼的其他部分的耗時(shí)在整體上就不那么顯著了。
當(dāng)然,您可能想要了解每次調(diào)用malloc后執(zhí)行的總指令數(shù)(ring0和ring3),這時(shí)就需要使用系統(tǒng)仿真器或調(diào)試器了,這可以作為一項(xiàng)練習(xí)留給讀者自己完成。
對(duì)于Edge瀏覽器來(lái)說(shuō),分布是非常相似的,但你會(huì)注意到它們更加分散,3x是一個(gè)保守和非常好的閥值:

對(duì)于IE和Edge瀏覽器來(lái)說(shuō),我們將其閥值分別設(shè)置為2x和3x,在此閥值以下被視為啟用了PageHeap,否則就可以認(rèn)為沒(méi)有啟用PageHeap。
您可以使用detect.html來(lái)檢測(cè)自己的IE或Edge瀏覽器,如果結(jié)果大相徑庭的話,請(qǐng)通知在下。此外,如果您有興趣,也可以查閱檢索和分析數(shù)據(jù)的相關(guān)代碼,其地址位于https://github.com/snf/exploit/tree/master/anomalies/pageheap 。
小結(jié)
事實(shí)證明,只需要40ms的時(shí)間,您就可以迅速確定PageHeap是否存在,從而決定是否繼續(xù)使用漏洞利用代碼了。當(dāng)然,這只是一個(gè)實(shí)驗(yàn),結(jié)論未必絕對(duì)可靠,同時(shí)還需要在不同的cpus和虛擬化技術(shù)下做進(jìn)一步的測(cè)試。但是別忘了,這只是ItWorksInMyPC(TM)項(xiàng)目中的一部分,還有更精彩的項(xiàng)目在等著您呢。
|