分析CVE-2015-1641的記錄

分析CVE-2015-1641的記錄

5 人贊了文章

0x1 實驗環境

Win7_32位,Office2007;

0x2 工具

Windbg,OD,火絨劍,UltraEdit,oletools;

0x3 漏洞簡述

word在解析docx文檔的displacedByCustomXML屬性時未對customXML對象進行驗證,可以傳入其他標籤對象進行處理,造成類型混淆,通過在word中嵌入構造好的RTF文件,其中經過精心構造的標籤以及對應的屬性值被displacedByCustomXML解析以及後續函數處理後會造成任意內存寫入。

0x4 樣本hash值

8bb066160763ba4a0b65ae86d3cfedff8102e2eacbf4e83812ea76ea5ab61a31

下載鏈接:

github.com/houjingyi233

0x5 分析記錄

在分析前先做好虛擬機快照,獲取RTF樣本後使用oletools工具中的rtfobj.pyc腳本分析,這裡將文件命名為aa.doc,在cmd命令模式中來到rtfobj.pyc所在的目錄,並將aa.doc一塊放到該目錄,接著執行命令「rtfobj.pyc aa.doc」,得到分析結果如下:一共四個文件,id為0的是「otkloadr.wRAssembly.1」,用來載入 OTKLOADR.DLL 模塊,其功能會在之後分析到:

把其他三個OLE對象分別根據他們各自的id用「rtfobj.pyc-s [id] aa.doc」命令依次分離並保存,其中有兩個是」.doc」文件,現在樣本已經被分離,雙擊執行也不會有危險,

將其中id為2的"aa.doc_object_0002042c.doc"的後綴改為「.docx」後,打開word就會奔潰,可以預測,漏洞就在這個OLE裡面觸發的:

在分析這個樣本的過程中,由於對RTF文件構造方式不熟悉,使得我在構造樣本的時候阻滯了很長時間,東拼西湊就是不能製造crash,後邊把問題放到了看雪論壇,前輩們一下就解決了,其實RTF文件的構造不複雜:將原始rtf 樣本用文本方式打開,搜索 「objdata」 字元串,可找到與 rtfobj.py 提取後相應 id 的內容,手動抽取對應id的內容另存為一個合法的 rtf 文件即可。前面提到,這個RTF樣本一共由四個OLE對象組成,用notepad++打開原始樣本,然後搜索「objdata」 字元串,發現一共有四個這樣的字元串,也就是那四個OLE,觸發漏洞的OLE的id為2,也就是原始樣本中的第三個OLE,長這樣:

抽取複製OEL的時候一定要把括弧成對匹配好,把這個OLE複製出來,即它的開頭到下一個OLE的開頭的部分,然後新建一個文本文檔,並把後綴改為」.rtf」格式,然後在這個文檔中先寫好「{
tf}」,然後粘貼OLE進去即可:

然後把這個RTF文件用word打開,就會奔潰;中間失敗過幾次,後來發現是複製OLE的時候括弧複製多了。

接著來定位漏洞觸發的位置,先打開Windbg,再打開Winword.exe,用調試器附加Winword.exe後運行存在漏洞的OLE文件,程序在「0x67C39d30「這個地址出現異常,導致奔潰,原因是[ECX]引用了一個不存在的地址;

這個指針[7C38BD50]會指向哪裡?,先把這個存在漏洞的OLE解壓,在解壓目錄的資源組織文件「document.xml」中發現如下的代碼:上面ECX的值被內嵌成smarttTag標籤的element的屬性值。

smartTag標籤是一個智能標籤,可對名字、地址等自動識別,displacedByCumtomXml屬性表示此處要替換為另一個customxml標籤,」next」表示後一個,」prev「表示前一個;樣本作者在smartTag的element中構造了0x7C38BD50,Word在解析docx文檔處理displacedByCustomXML屬性時未對customXML對象進行驗證,所以能傳入smartTag標籤對象。

單獨載入觸發漏洞的OLE時索引到一個不存在的地址,因為這個地址位於」 MSVCR71.DLL」中,而這個DLL正是通過第一個OLE對象「otkloadr.wRAssembly.1」引入的。將第一個OLE對象:{objectobjocx{*objdata0105000002000000160000006f746b6c6f6164722e5752417373656d626c792e3100000000000000000001000000410105000000000000}},添加到觸發漏洞的OLE前面,然後重新載入合併後的RTF運行,通過windbg下條件斷點,這裡先記錄下crash發生的時候「0x7C38BD50「所在的模塊地址,待會通過它來下條件斷點:

條件斷點:

bp wwlib!DllGetClassObject+0x50e6".if(ecx=7c38BD50){}.else{gc}"

0x7C38BD50是smartTag標籤的element值,0xFFFFE696(十進位為4294960790)是moveFromRangeStart的值,隨後對這兩個值進行計算得到一個地址0x7C38BD74。計算過程如下:

隨後開始解析第二個smartTag,smartTag標籤的element值此時為0x7C38BD68,moveFromRangeStart的值為0x7C376FC3(十進位為2084007875),計算出的地址為0x7C38A428,最後通過memcpy函數將0x7C376FC3覆蓋到地址0x7C38A428中,在調試器可以看到,0x7C38A428為虛表指針:

而在7C38A428被覆蓋之前,它指向的是kernel32!FlsGetValue:

覆蓋之後的0x7c38a428指向的便是攻擊者想要執行的代碼位置:

接著往下執行會先經過一大片地址為「7C342404」的「ret」,然後進入ROP鏈,再往後就是shellcode。

將id為1的OLE解壓後,在解壓目錄的「activeX1.bin「中看到用來堆噴的數據塊:nop指令上面的是ROP鏈,heapspary前會使用大量地址為0x7c342404 的ret-sled,

通過XML來載入「activeXL.bin」的堆數據:

在堆噴數據看到ROP鏈之前有大量的「ret sled」,可以通過這個地址來下斷點進入shellcode,方法為:在進入MSVCR71.dll後,通過「uf 7C342304 」命令來查看它所在的模塊地址:MSVCR71!calloc+0xb1,並在該處下斷;然後用「bd」命令禁用之前的條件斷點,F10執行後就能來到地址0x7C342404。

如下圖,當程序在「0x7C342404」處斷下時,通過「Memory」窗口輸入Virtual為「ESP」可以看到堆噴數據,如果不能進入到堆噴數據,有兩種解決方案:可以將虛擬機的內存設置為2G;或者找到rop鏈在堆中的位置,接著在執行「ret」準備跳進堆噴數據的時候將棧頂值修改為ROP鏈的起始位置;

執行完「0x7C342404」處的「ret」後,便可以接著跟到ROP鏈,F10單步執行:然後通過NOP進入shellcode。

在Windbg中分析shellcode的時候一直沒能順利跟下去,於是在OD中來分析,斷點還是在「0x7C342404」:打開OD和Winword.exe後,用OD附加Winword.exe,接著把完整樣本拖進word,這個時就可以下斷點,因為shellcode是最後執行,在這之前要先進行堆噴,再觸發漏洞,時間上來得及。

當程序在「0x7C342404」處斷下後,發現很久也跟不到ROP鏈,這時可以藉助之前Windbg的分析結果,發現ROP鏈的首個位置為「0x7C3651EB」,然後在該位置下斷,同時把「0x7C342404」處的斷點禁用,接著F9執行,便能來到ROP鏈的開頭。

在ROP鏈中通過調用VirtualProtect關閉起始地址0x090008b4後的DEP保護:

接著執行地址0x090008b4處的「nop+shellcode」:

首先獲取kernel32.dll基址:

接著比較API的hash值來獲取所需API函數,這樣的好處在於減少了shellcode的大小:

接著用VirtualAlloc分配可執行的內存空間,為執行shellcode做準備:

接著通過調用 GetFileSize遍歷進程中打開的文件句柄,獲取打開的樣本文件的句柄:

CreateFileMapping創建該文件的共享文件數據:

MapViewOfFile來獲取共享數據的內存地址:

判斷幾個標誌位是否為「{
t,0xfefefefe,0xfe,0xffffffff」,是則將shellcode拷貝到VirtualAlloc分配的可執行的內存空間:

但是在之後的分析中,多次調試也沒有跟到真實shellcode的位置,於是嘗試用Windbg來分析,根據上一階段的分析可知,第一階段的shellcoed先判斷幾個標誌字元串是否為「{
t,0xfefefefe,0xfe,0xffffffff」,是的話就將真實shellcode拷貝到VirtualAlloc分配的可執行的內存空間,如上圖所示:順利的話就會執行「0x090009F9」(偏移0x09F9)地址處的指令。

然後關掉OD,打開Windbg和Winword.exe,用Windbg附加Winword.exe,先在「0x7C342404」,即「ret」鏈的位置下斷,然後將原始樣本附加到Winword.exe,執行後來到「ret」,接著在「0x7C3651EB」,即ROP鏈的開頭下斷,並用「bd 0」禁用之前的斷點,執行後來到ROP鏈的開頭,根據上面的分析,真實shellcode會在偏移「0x09F9」處開始執行,用「bc 0」,「bc 1」先將之前的斷點刪除,再在「0x090009F9」下斷,執行後順利來到第二階段的shellcode:

接著將後面的4KB的數據,即第二部分 shellcode,拷貝到函數 VirtualAlloc 申請的具有可執行許可權的內存中,然後跳轉過去執行:

之後會在這部分shellcode的偏移0x2e處解密shellcode,解密的大小為0x3CC位元組:

接著根據標誌字元串「0xBABABABABA」獲得payload的起始位置,然後xor 0xCAFEBABE解碼payload,終止標誌為0xBBBBBBBB:

接著創建文件,釋放payload:

可以根據API參數在UE編輯器看到路徑和創建的文件「svchost.exe」:

接著用WinExec函數執行「svchost.exe」:

WinExec函數執行後,在監控軟體(火絨劍)中可以看到釋放的惡意程序,它的作用主要是進行信息的竊取:

在惡意 payload 執行後會對樣本中的部分數據進行異或操作,目的是使其成為看上去正常的word,這部分機器碼的開頭是「0xBB」,結尾是「0xBCBC」,

重寫之後看到的word就是正常打開的空白文檔:

原始樣本所在的word文檔:

本文由看雪論壇 大晟 原創,轉載請註明來自看雪社區

原文來源:[分享]分析CVE-2015-1641的記錄-『二進位漏洞』-看雪安全論壇


推薦閱讀:

CVE-2015-3636android內核 UAF漏洞分析
Windows內核漏洞利用教程 第7部分:未初始化的堆變數
從開發角度淺談CSRF攻擊及防禦
CVE-2018-7600漏洞分析

TAG:網路安全 | 漏洞挖掘 | 信息安全 |