本文从2019UNCTF里的一道有意思的逆向题目奇妙的RTF入手,对Office2017年的CVE-2017-11882进行调试分析,题目文件我放在附件中,需要的师傅可以下载学习,如果你完全不懂Office漏洞,这篇文章还是可以帮助你打开Office漏洞的面纱,首先我会讲一下身为一个完全不懂office的人如何做出这道题目的,然后在对漏洞的Poc本身进行分析,其原理十分简单,本文的调试环境是
首先题目给我们一个压缩包,解压之后提示我们用Office打开,既然是2017年的,那肯定之前的版本都没有进行修复,所以这里选择安装office2007,然而我们打开什么也没发现
对于一个不了解office结构的二进制选手,肯定想着先把他放进二进制编辑器中观察,显示如下,前面这些内容应该是office特有的格式
不懂office的话那怎么办呢?当然是百度呀,直接百度第一行的内容,下面就有显示相关的漏洞信息,所以我们初步确定漏洞编号CVE-2017-11882
这里用rtfobj工具查看office文件的各种属性,发现各种属性和百度搜索中的文字完全一样,再次确定漏洞编号CVE-2017-11882,工具下载地址 => 这里
通过阅读这篇文章,我们可以得到漏洞存在于EQNEDT21.EXE公式编辑器的sub_41160F函数中,我们将其拖进IDA,找到漏洞点,可以看到
伪代码如下,这里没有对长度进行检验就直接进行复制操作,而这个a1参数是我们可以控制的,所以本质上就可能产生一个栈溢出漏洞
我们静态分析出了漏洞的编号,漏洞的位置,那么动态调试就比较方便了,从网上的各种文章来看,因为Poc都是有弹计算器的,所以它们都是直接在弹计算器函数或者创建线程的函数处下断点,然而这里并没有直接弹计算器,所以我们选择直接在漏洞函数下断点,我们直接用Windbg在程序启动时附加即可,因为是程序启动时附加,所以我们需要设置注册表:
接下来我们直接运行程序就会由Windbg接管,我们在IDA中找到漏洞函数的位置下断即可,这里我们直接在返回地址下断,也就是bp 0x411874
,我们第一次运行发现这里并没有对返回地址进行覆盖
继续运行,这一次覆盖了返回地址,到了shellcode部分
继续单步进入ecx,发现一堆异或的操作
我们把异或在eax的结果记录下来
解码一下即可得到flag
前面是解题过程,这次我们来分析一下具体的poc,首先我们从这里获得弹计算器的样本,我们这次在函数开始的地方下断点,观察参数
接着我们运行直到拷贝函数覆盖返回地址,这里直接被覆盖为调用计算器函数
漏洞原理很简单,这里我们继续深入分析一下,我们同样用rtfobj来分析样本
上图这个命令会从RTF文件中提取所有对象,下图的命令可以查看dump出的bin文件的各种属性的信息
我们用二进制编辑器打开dump出来的bin文件,找到下面的片断,这里就是我们分析的关键点,也就是上图分析得到的 Equation Native 数据流
看不懂没关系,下面我们来分析一下结构, Equation Native 由几个成员组成
Equation Native = EQNOLEFILEHDR + MTEF Header + MTEF Byte Stream
其中 EQNOLEFILEHDR 头的结构为
大致也就是下面样子
struct EQNOLEFILEHDR { WORD cCBHdr; // 格式头长度,大小为0x1C DWORD nVersion; // 固定为0×00020000。 WORD nCf; // 该公式对象的剪贴板格式 DWORD cbObject; // MTEF数据的长度,不包括头部 DWORD nReserved1; // 未使用 DWORD nReserved2; // 未使用 DWORD nReserved3; // 未使用 DWORD nReserved4; // 未使用 };
其结构对应在二进制编辑器中就是下图红框的值
接下来就是 MTEF header 的结构,里面存放一些版本信息
Description | Size (byte) | Value | Comment |
---|---|---|---|
MTEF Version | 1 | 0x3 | MTEFv3 |
Generating Platform | 1 | 0x1 | Windows |
Generating Product | 1 | 0x1 | Equation Editor |
Product Version | 1 | 0x3 | |
Product Subversion | 1 | 0xa |
其结构对应在二进制编辑器中就是下图红框的值
最后就是 MTEF Byte Stream 结构
value | symbol | description |
---|---|---|
0 | END | end of MTEF, pile, line, embellishment list, or template |
1 | LINE | line (slot) record |
2 | CHAR | character record |
3 | TMPL | template record |
4 | PILE | pile (vertical stack of lines) record |
5 | MATRIX | matrix record |
6 | EMBELL | character embellishment (e.g. hat, prime) record |
7 | RULER | ruler (tab-stop location) record |
8 | FONT | font name record |
9 | SIZE | general size record |
10 | FULL | full size record |
11 | SUB | subscript size record |
12 | SUB2 | sub-subscript size record |
13 | SYM | symbol size record |
14 | SUBSYM | sub-symbol size record |
这里我们主要关注的是其中的 FONT 结构,也就是漏洞点,结构如下
Description | Size (byte) | Value | Comment |
---|---|---|---|
Tag | 1 | 0x8 | 0x8 denotes Font record |
Typeface Number | 1 | 0x5a | |
Style | 1 | 0x5a | |
Font Name | Variable, NULL terminated | “cmd.exe /c calc.exe AAAAAAAAAAAAAAAAAAAAAAAA” + 0x00430c12 | Overflow and overwrite return address |
大致也就是下面样子
struct stuFontRecord { BYTE bTag; // 字体文件的tag位0×08 BYTE bTypeFace; // 字体风格0x5a BYTE bStyle; // 字体样式0x5a BYTE bFontName[n] // 字体名称,以NULL为结束符,漏洞点 };
其结构对应在二进制编辑器中就是下图红框的值
分析完结构,利用就很明显了,我们找到偏移之后只需要把最后返回地址修改为我们的shellcode地址即可,网上Poc大多数是用python实现的,例如Embedi师傅Poc的一些步骤:
# python实现对各个部件的构造 import argparse RTF_HEADER = R"""{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Calibri;}} {\*\generator Riched20 6.3.9600}\viewkind4\uc1 \pard\sa200\sl276\slmult1\f0\fs22\lang9""" RTF_TRAILER = R"""\par} """ OBJECT_HEADER = R"""{\object\objemb\objupdate{\*\objclass Equation.3}\objw380\objh260{\*\objdata """ OBJECT_TRAILER = R""" }{\result{\pict{\*\picprop}\wmetafile8\picw380\pich260\picwgoal380\pichgoal260 0100090000039e00000002001c0000000000050000000902000000000500000002010100000005 0000000102ffffff00050000002e0118000000050000000b0200000000050000000c02a0016002 1200000026060f001a00ffffffff000010000000c0ffffffc6ffffff20020000660100000b0000 0026060f000c004d61746854797065000020001c000000fb0280fe000000000000900100000000 0402001054696d6573204e657720526f6d616e00feffffff5f2d0a6500000a0000000000040000 002d01000009000000320a6001100003000000313131000a00000026060f000a00ffffffff0100 000000001c000000fb021000070000000000bc02000000000102022253797374656d000048008a 0100000a000600000048008a01ffffffff6ce21800040000002d01010004000000f00100000300 00000000 }}} """ # 主要关注这里 OBJDATA_TEMPLATE = R""" 01050000020000000b0000004571756174696f6e2e33000000000000000000000c0000d0cf11e0a1 [...] 000000000000000000000000000000000000001c00000002009ec4a900000000000000c8a75c00c4 ee5b0000000000030101030a0a01085a5a4141414141414141414141414141414141414141414141 414141414141414141414141414141414141414141120c4300000000000000000000000000000000 => 覆盖点 00000000000000000000000000000000000000000000000000000000000000000000000000000000 [...] 7cef1800040000002d01010004000000f0010000030000000000 """ [...]
从代码角度修复那就是在拷贝函数之前加上一个判断,判断字体名长度是否在合法区间内从而阻止溢出,从用户角度而言修复方案是比较简单的,有下面几个方法
reg add “HKLM\SOFTWARE\Microsoft\Office\Common\COM Compatibility\{0002CE02-0000-0000-C000-000000000046} ” /v “Compatibility Flags” /t REG_DWORD /d 0x400
reg add “HKLM\SOFTWARE\Wow6432Node\Microsoft\Office\Common\COM Compatibility\{0002CE02-0000-0000-C000-000000000046} ” /v “Compatibility Flags” /t REG_DWORD /d 0x400
[+] 结构体参考:http://rtf2latex2e.sourceforge.net/MTEF3.html#Header
[+] Yanhui Jia师傅的分析文章:https://unit42.paloaltonetworks.com/unit42-analysis-of-cve-2017-11882-exploit-in-the-wild/
[+] 四维创智分析文章: https://www.freebuf.com/column/183551.html