环境

  • win7 + x32dbg
  • 010editor
  • IDA

下载emet包关闭系统aslr,在注册表中添加如下字段使得eqnedt32.exe与x32dbg关联,启动时自动挂载。

image-20200114094406213

漏洞分析

溢出点

打开样本exploit1.rtf之后弹出计算器。

image-20200114094937973

由于已知是栈溢出漏洞,尝试在调用堆栈中寻找线索。

image-20200114095108558

看到最近的返回点是004218e4, 在前一句call的地方和此处分别下打印断点,目的是确定漏洞发生在函数4115a7中。

image-20200114095221494

image-20200114095502166

关闭调试器,断点会自动保存下来,重新打开样本,f9直到最后,看日志中只打印了“start”,说明程序在调用4115a7的过程中流程被劫持。

image-20200114095711477

删除前面两个断点,在4115a7处断下后f8调试看看哪一句出了问题。在步过004115d3处的 call 41160f 时弹出计算器。

image-20200114100331339

在004115d3处下断点,断下后f7步入,发现此函数较长,于是间隔着打上数个断点(多关注有嫌疑的地方,比如rep movsd),并监视saved_ebp的值,每次f9运行到下一个断点处,观察saved_ebp变化,发现在下图eip和上个断点区域saved_ebp从004115d8变成了00430c12,一直到最后ret的时候,这个值没有再被更改过了,所以程序返回到00430c12处

image-20200114103215567

00430c12处调用winexec,后面就弹计算器了。

image-20200114102537874

之前确定有问题的区域00411648到0041165c这段,再来调试一下(关闭时记得先把这个函数里面的断点删掉,下次从外面进来之后再重新打,因为此函数可能被其他函数调用,不是我们想要去的地方)。

单步运行到00411658处,即将进行复制操作 rep movsd,此时ecx=000c, edi=[ebp-28],显然0xc * 4 = 48,大于28,所以ebp和saved_ebp都将被覆盖。

image-20200114103929641

image-20200114104918084

在ida中可以很清楚看出这里是一个不安全的内存拷贝,没有检查长度。

image-20200114105147283

溯源

后面来寻找一下导致溢出的这段数据来自何方。

整理上一节可以得出已知的函数调用栈是:421774 -> 4115A7 -> 41160F(漏洞函数),通过ida查看传参情况发现我们要找的evil_str(溢出数据)是顺着第一个参数传下来的,还得往前找。

最后发现在43B418中,调用4164fa初始化了evil_str,之后通过4214C6 -> 421774 ->….这样一路传到漏洞函数中。

image-20200114110549145

image-20200114110611388

追进去4164fa,貌似是从内存中循环读出的数据。至此好像没办法继续追哪里写数据了

image-20200114111019589

看看文件里面能找到什么吧,hex模式下搜0x41搜不到,text模式下搜’A’搜不到,搞了半天,发现数据是把ascci码直接存在文本文件中的。可以看到我们要找到数据是直接存在于文件中的。

image-20200114151838928

尝试了下修改这段命令也是可以的,不过要调整41的数量保持长度不变。

手动构造样本

首先打开一个word文件,插入一个公式对象(注意这里要先把之前添加的注册表项先去掉或者改个名字,否则会挂上x32dbg),为了后面方便找,我直接在公式中写入了123456789

image-20200114225414365

之后用压缩软件打开,把 word\embeddings路径下的oleObject1.bin文件复制一份出来,用010editor打开,根据从其他样本中观察的特征,搜索0a0a,这附近就有我们之前输入的一串数字。

image-20200114225902281

从1c开始是 Equation Native 的数据,组成结构是:

Equation Native Stream Data = EQNOLEFILEHDR + MTEFData,其中

MTEFData = MTEF header + MTEF Byte Stream

(具体见https://www.anquanke.com/post/id/87311)

图中划线部分是MTEF header,在他之前是EQNOLEFILEHDR ,之后是MTEF Byte Stream。

我们主要关心MTEF Byte Stream,他是由一系列records组成的,每个record包括一个标识字和相应的数据,这里0A表示full size record(并不知道是啥),01表示line (slot) record,后面就是我们输入的一串数字,在这里每个数字的表示为0288+hex(数字)+00这种模式。

image-20200114234455012

对比多个样本,发现他们与这里的不同就是把0A后面的record换成了08(font name record ),我们模仿着换掉并在08后面替换一些规律的字符串。之后拖到压缩文件中替换,进行调试。

调试过程中的一些问题:

  1. 一开始在公式中填充的长度是有影响的,如果过短的话,无论font name填充多长,后面传入漏洞函数的字符串都是被截断以后的,好像截成24,这个长度是不够溢出的,按照 这篇文章的说法,应该是和公式中填充长度称4倍关系,我在第一次出问题后适当增加就ok了,没反复实验了。
  2. 这篇文章中提到当公式中填充数据过多时,数据存储会跨过0a00-0a90这块区域,shellcode到底能放多长,这是个问题

最后调试成功长这样:

image-20200114232457832

思考

  1. 返回地址被覆盖成00420c12(call winexec),为什么正好esp就执行evil_string开头?因为41160f(vuln_function)的前两个参数一个是evil_string,一个是null,正好满足winexec的调用 src
  2. 文件格式太难了,东看西看也没理解全貌

参考

http://rtf2latex2e.sourceforge.net/MTEF3.html

https://blog.csdn.net/qq_38924942/article/details/95325085

https://www.anquanke.com/post/id/87311

https://www.cnblogs.com/goabout2/p/7990667.html