1. 概述
在Adobe Reader的XFA(XML Forms Architecture)處理模組AcroForm.api 中,存在一個用於得到任意程式碼執行許可權的漏洞,編號為CVE-2013-0640。另一個是 CVE-2013-0641,該漏洞存在於 Adobe sandbox 的 broker 程式中,用於從sandbox 中逃逸,獲取高許可權,這兩個漏洞同時被用在了一個樣本中。CVE-2013-0640的POC可在古河的文章下獲取( https://bbs.pediy.com/thread-163035.htm )。
2. 實驗環境
版本為11.0.1的Aodbe Reader,XPSP3,Win7,OD,windbg,IDA
3. 漏洞觸發原因
漏洞觸發的基本過程是這樣:AcroForm.api中的記憶體分配函式Alloc在分配 Adobe Reader的 XML表單中的新節點NewObject後,由一個存在缺陷的函式bugFun來呼叫這個新節點,一開始分配的時候 NewObject 的大小為40h位元組,但是當缺陷函式bugFun再去引用這個節點NewObject的時候,訪問的卻是[NewObject+44h]的地方,多訪問了4個位元組,而這個位置也是一個物件的指標,這個漏洞的產生原因就是bugF函式在引用節點NewObject的時候沒有對可引用的範圍做限制,造成了越界訪問。
4. 分析記錄
這裡先是分析了CVE-2013-0640的POC樣本,這個POC用來演示CVE-2013-0640的觸發和利用,之後再分析了真實樣本。
首先將OD設定為實時偵錯程式,接著開啟POC,不久OD就會捕獲異常,程式停在了“0x88888888”,由於這個函式地址"0x88888888"是偽造的,也是不存在的,於是函式沒有正常返回,可以看到是“0x208a54d3”呼叫了函式,在上面的指令中可以看到取函式地址的過程,如下圖所示:
接著用windbg來分析,感覺用windbg下斷點更方便。用Windbg附加Aodbe Reader後,直接g命令執行,crash的完整彙編程式碼如下:
對應的虛擬碼:
可以對比著看上圖OD在crash的現場,在”0x208A54b9“的地址處獲得了[NewObject+44h]的虛表指標,在這之前esi的值由”0x208A5488“處的mov esi,[eax]賦予,而eax的值最初由”0x208A5478“處的呼叫函式生成返回,就是這個函式”0x208A5514“生成了新節點NewObject,具體的分配函式是”0x208A5514“的一個子函式:這裡可以看到新節點NewObject的大小為40h,但是上圖中”0x208A54b9“處卻引用了偏移44h的位置 ,多訪問了四個位元組:
而這個 [NewObject+44h]的地址存放的也是某個物件的指標,該物件的結構如下:
struct NewObject+44t {
+0 虛表
+4 referencecount //當引用計數為 0 時,物件將被析構
+30 unknown
+48 MemberArray //一個陣列結構的指標 }
重新用windbg附加Adobe Reader,一開始有說到漏洞函式存在的模組為AcroForm.api,於是用模組斷點”sxe ld AcroForm.api"對漏洞函式所在的模組下斷點,成功之後根據上面的分析在”0x208a54b9“處下下斷觀察[NewObject+44h]的虛表指標被覆蓋的過程:
接著通過記憶體寫斷點"ba w4 0c0c0c20"來跟蹤虛表地址被覆蓋的過程,如下圖所示:
5. 樣本構造
(這部分內容參考古河的分析: https://bbs.pediy.com/thread-163035.htm )
通過以上分析可知,只要能控制[NewObject+44h]處該物件指標的值,以及這個值所指向的記憶體區域的內容,就能控制 EIP,首先將[NewObject+0x44]處的值設為 0x0c0c0c20,於是 0x0c0c0c20 將被當 成一個 Member44_t 物件的指標進行後續操作,同時我們將 0x0c0c0c20 處的記憶體佈局成如下形式:
1. [0x0c0c0c20] = 0x0c0c0c28, 在析構物件呼叫解構函式時將被使用。
2. [0x0c0c0c24] = 1, 該物件的引用次數,將其設定為 1,於是在 “dec dword ptr [ecx+4]” 的減操作完成後,析構動作立即發生。
3. [0x0c0c0c28] = 0x88888888,這個是虛表中第一個函式,最後 crash 時 EIP 會指向 0x88888888
現在的問題是我們如何精確地控制[NewObject+0x44]處的值,使其變成 0x0c0c0c20? 這個是可以辦到的,關鍵在於 AcroForm.api 中自帶的堆記憶體分配機制。 AcroForm.api 有自己的堆分配管理機制(可選),這個機制和 Windows 自身的很像,它針對大小為 0 ~ 0x100 的記憶體分配有專門的 0x100 個 freelist,用來 儲存已經釋放的記憶體塊(當然沒有真的呼叫 free 去釋放,同時還有另外的一個 freelist 存放其它大小的 已釋放記憶體塊。
當一個記憶體分配請求到來時,先檢查 freelist 中有沒有合適的記憶體塊,如果找到合適的 則直接返回這個塊;如果沒有正好合適的大小,則嘗試將較大的已釋放記憶體塊切成小一點的記憶體塊返 回,實在找不到了才會新分配記憶體。這裡還有一個要點是,對於 freelist 中的記憶體塊,在釋放和重新分 配返回時,不會將原有的內容進行清理。 利用這個自定義的記憶體分配機制,我們現在可以精確地控制[NewObject+0x44]處的內容了。首先分 配一系列較大的記憶體塊,將這些大記憶體塊填充為 0x0c0c0c20。 然後分配一些和 NewObject 大小相同的塊,這樣做的目的是為了將 freelist 中已有的快取用完。然後 我們釋放所有的大記憶體塊,緊接著觸發漏洞。在漏洞觸發時,NewObject 被分配,由於大小相同的 freelist 已經被清空,於是它會嘗試從我們剛剛釋放的某個大記憶體塊中切割出一個 0x40 的小記憶體塊並返 回,換句話說,返回的 NewObject 地址其實是落在我們剛剛釋放的某個大記憶體塊中間的。於是在越界 訪問 NewObject+0x44 時,得到的值是我們一開始在大記憶體塊中填充的值,即 0x0c0c0c20。
整個過程如下圖:
控制eip的示意圖:
6. POC中的javascript分析
“NewObject”的生成和越界訪問“NewObject+44h”的過程可以參考POC樣本中的javascript程式碼來理解,關鍵程式碼如下:
第一步:解析獲取節點
xfa.resolveNode("xfa[0].form[0].form1[0].#pageSet[0].page1[0].#subform[0].field" + index.toString() + "[0].#ui[0]");
uiListNodes.push(node);
//解析並獲取 XFA 表單中的一個UI節點,這個節點就是在彙編程式碼中“0x208A5478”處生成的節點物件“NewObject”
var node = xfa.resolveNode("xfa[0].form[0].form1[0].#pageSet[0].page1[0].#subform[0].field" + index.toString() + "[0].#ui[0].#choiceList[0]");
choiceListNodes.push(node);
//解析並獲取上面得到的UI節點下面緊鄰的choiceList節點,這個節點就是“NewObject+44h”處的那個物件
}
第二步:再次解析引用的時候發生越界訪問
var node = xfa.resolveNode("xfa[0].form[0].form1[0].#pageSet[0].page1[0].#subform[0].field0[0].#ui");
//引用之前已獲取的節點”NewObject“
if ( node == undefined ) {
return false;//如果引用的節點物件未定義,就返回false,啥事沒有
}
try {
node.oneOfChild = choiceListNodes.pop();//否則返回“NewObject+44h”物件的指標(被篡改後的),由於沒有對物件的可引用範圍做限制,會發生越界訪問;
}
catch (e) {
return false;
}
return true;
7. 對真實樣本的分析
之後再分析了完整樣本,完整樣本的分析是在win7中進行的,其中CVE-2013-0640的觸發過程基本與上述分析過程一致,主要的問題就是相關漏洞函式的記憶體地址發生了變化,但是相對偏移地址是不變的,比如XP下的地址"0x208A54d3"在Win7下就會變為"0x67cd54d3",相對偏移地址沒變。在Win7下嘗試下斷點的時候,一開始沒有成功也是因為忽略了不同系統下記憶體地址的變化,之後先在xp下用“uf +地址”命令找到指令所在的真實地址,比如"0x208A54d3"和"0x67cd54d3"的真實地址就是:”AcroForm!PlugInMain+0xa31a5“,這在不同系統下都是一樣的,之後通過真實地址成功下了斷點。另外就是上面分析的POC並沒有ROP鏈,POC只是用來crash,真實樣本中這個漏洞是被實實在在的利用了。
觸發過程跟上面的分析一致,就是地址不一樣,對完整樣本的分析主要是跟進了CVE-2013-0640觸發後過程,通過真實地址”AcroForm!PlugInMain+0xa31a5“下斷,可以看到在“0x67cd54d3“的位置呼叫了偽造的虛擬函式:
如果直接F10執行,函式就會通過rop載入惡意dll,之後就是利用第二個漏洞CVE-2013-0641完成沙盒逃逸,整個逃離過程全是在第一個漏洞引入的dll中完成:
可到對應目錄下找到該DLL及其釋放的木馬:
接著用windbg重新附加Adobe Reader,讓程式繼續斷在“0x67cd54d3“,這次用F8單步跟進,會發現程式開始執行ROP,其中包括了大量的ret指令和pop等操作:
ROP和載入DLL的shell code間是用NOP指令填充的:
shellcode首先會通過GetTempPathW獲得Temp資料夾的路徑:
之後在該路徑下通過fsopen()函式生成指定資料夾和指定的DLL檔案"D.T",可通過fsopen()函式的引數來檢視對應的路徑和檔案:
接著通過fwrite()函式來寫入資料:
最用用fclose()函式結束寫入:
寫入之後通過loadlibrary來載入該檔案:
這個DLL載入完,這個漏洞的使命也達到了,往下就是第二個沙箱逃逸漏洞的觸發和利用。
8. 總結
前期分析的時候過於陷入別人的分析思路,沒有去獨立思考,這樣很不好,他人的思路也要辯證的接納,保持獨立思考,要嘗試自己去勾勒出漏洞觸發和利用的流程,這方面還缺乏很多經驗,一點點來吧。
9. 參考文章:
1、http://www.freebuf.com/vuls/33448.html
2、https://bbs.pediy.com/thread-163035.htm
原文作者:大晟
原文連結:https://bbs.pediy.com/thread-246656.htm
轉載請註明:轉自看雪論壇
看雪閱讀推薦:
1、[原創] 打造Wi-Fi “DOS”攻擊工具——Wi-Fi_deauther
2、[原創]8月29號網鼎杯pwn1-impossible簡單除錯過程
3、[原創]bindiff基本使用以及小試牛刀
4、[分享]【看雪安全開發者峰會2018】議題:自動逆向機器人
5、[翻譯]ARM彙編簡介(六)堆疊和函式