【Office漏洞 第二弹】CVE-2018-0798及利用样本分析
## 0x01 漏洞描述- **成因**:`EQNEDT32.exe`在解析Matrix record时,并未检查长度,从而造成栈溢出。无论打不打CVE-2017-11882补丁都可以成功触发,使得攻击者可以通过刻意构造的数据内容及长度覆盖栈上的函数返回地址,从而劫持程序流程。
- **影响版本**:Microsoft Office 2007, Microsoft Office 2010, Microsoft Office 2013,Microsoft Office 2016
- **POC**:(https://github.com/houjingyi233/office-exploit-case-study/blob/master/CVE-2017-11882%26CVE-2018-0802%26CVE-2018-0798/cve-2018-0802%20poc%20with%20comments.rtf)
## 0x02 漏洞分析
> 笔者复现及分析环境:Windows 7 Service Pack 1、Microsoft Office 2010、x64Dbg、IDA 7.0(EQNEDT32.exe已打CVE-2017-11882补丁,但笔者分析时关闭了ASLR)
#### 0x02.1 静态分析
漏洞位于`sub_443E34`内:
![图片1 sub_443E34](https://s1.ax1x.com/2020/04/03/GNwF2Q.png)
其调用了两次`sub_443F6C`,但`sub_443F6C`在复制数据时并未检查传递进来的参数:
![图片2 sub_443F6C](https://s1.ax1x.com/2020/04/03/GNdxbt.png)
其中的数据长度可通过`a1`控制,具体计算方法是`(2 * a1 + 9) >> 3`,而其目的地址是由`sub_443E34`传递过来位于其开辟栈空间内的局部变量(`int`型):
![图片3 栈](https://s1.ax1x.com/2020/04/03/GNwpUf.png)
如此一来,便可通过精心构造的数据,覆盖`sub_443E34`函数的返回地址,进而控制执行流。
#### 0x02.2 动态调试
POC地址已于上文给出。直接于`sub_443E34`处设断,成功断下后,直接执行到调用`sub_443F6C`前查看其传递参数:
![图片4 传递参数](https://s1.ax1x.com/2020/04/03/GNdjKA.png)
跟进查看,可以看到其计算后的实际复制数据长度:
![图片5 计算后的复制长度](https://s1.ax1x.com/2020/04/03/GNwEKs.png)
跟进其调用的`sub_416352`可以查看要复制数据:
![图片6 要复制数据](https://s1.ax1x.com/2020/04/03/GNwmV0.png)
直接执行到`sub_443F6C`结束处,可以看到:
![图片7 已覆盖栈上的数据](https://s1.ax1x.com/2020/04/03/GNwQGF.png)
`sub_443E34`再次调用`sub_443F6C`,其执行流程同上:
![图片8 传递参数](https://s1.ax1x.com/2020/04/03/GNwu5T.png)
![图片9 计算后的复制长度](https://s1.ax1x.com/2020/04/03/GNw8M9.png)
![图片10 要复制数据](https://s1.ax1x.com/2020/04/03/GNwJq1.png)
可以看到,已经覆盖栈上`sub_443E34`的返回地址,劫持了执行流:
![图片11 已覆盖栈上的数据](https://s1.ax1x.com/2020/04/03/GNwUIK.png)
回到`sub_443E34`,直接执行到结束处:
![图片12 sub_443E34结束处](https://s1.ax1x.com/2020/04/03/GNw0Re.png)
通过ROP跳转到`WinExec()`:
![图片13 ROP](https://s1.ax1x.com/2020/04/03/GNwrMd.png)
成功弹出计算器:
![图片14 calc](https://s1.ax1x.com/2020/04/03/GNwyqI.png)
## 0x03 Bitter组织某样本分析
> 样本名称:Urgent Action.docx
>
> 样本MD5:02C2A68CE9A35F5F0E1B3456E09D6CC9
通过远程模板注入的方式下载一RTF格式文档:
![图片15 远程URL](https://s1.ax1x.com/2020/04/03/GNwgdP.png)
使用WinHex查看,确为RTF格式:
![图片16 WinHex查看](https://s1.ax1x.com/2020/04/03/GNwWi8.png)
添加`.rtf`后缀后打开文档。直接来到`sub_443E34`调用`sub_443F6C`处:
![图片17 调用sub_443F6C及传递参数](https://s1.ax1x.com/2020/04/03/GNwIMj.png)
此次调用`sub_443F6C`并未发生溢出,其复制数据长度如下:
![图片18 复制数据长度](https://s1.ax1x.com/2020/04/03/GNwTLn.png)
复制内容:
![图片19 复制内容](https://s1.ax1x.com/2020/04/03/GNwbd0.png)
其第二次调用`sub_443F6C`,发生溢出:
![图片20 复制数据长度](https://s1.ax1x.com/2020/04/03/GNwOiT.png)
复制内容:
![图片21 复制内容](https://s1.ax1x.com/2020/04/03/GNwjWF.png)
接下来直接执行到`sub_443E34`结束处,可以看到其劫持执行流:
![图片22 sub_443E34结束处](https://s1.ax1x.com/2020/04/03/GNwzQJ.png)
通过ROP跳转到Shellcode:
![图片23 ROP](https://s1.ax1x.com/2020/04/03/GN0pLR.png)
Shellcode如下:
![图片24 Shellcode](https://s1.ax1x.com/2020/04/03/GN0Pdx.png)
下面开始分析其功能。首先是计算跳转地址:
![图片25 跳转](https://s1.ax1x.com/2020/04/03/GN0io6.png)
跳转之后,通过与计算出的EAX值比较的方式移动指针指向要复制的Shellcode,复制后跳转到Shellcode上执行:
![图片26 复制Shellcode](https://s1.ax1x.com/2020/04/03/GN0kFK.png)
通过PEB手动符号解析定位到`kernel32.dll`:
![图片27 定位kernel32.dll](https://s1.ax1x.com/2020/04/03/GN0AJO.png)
定位`kernel32.dll`中的`GetProcAddress()`函数:
![图片28 寻址GetProcAddress()](https://s1.ax1x.com/2020/04/03/GN0EWD.png)
跳转后执行`GetProcAddress()`:
![图片29 call调用](https://s1.ax1x.com/2020/04/03/GN0eQH.png)
![图片30 获取CreateDirectory调用地址](https://s1.ax1x.com/2020/04/03/GN0myd.png)
之后通过`call`调用的形式给`CreateDirectory()`传递参数:
![图片31 调用CreateDirectory](https://s1.ax1x.com/2020/04/03/GNocex.png)
于C盘创建一名为Temp的文件夹。获取`LoadLibrary()`调用地址:
![图片32 获取LoadLibrary调用地址](https://s1.ax1x.com/2020/04/03/GNoNwV.png)
之后在`call`调用的同时传递参数:
![图片33 call调用并传递参数](https://s1.ax1x.com/2020/04/03/GNo0W4.png)
![图片34 LoadLibrary(urlmon.dll)](https://s1.ax1x.com/2020/04/03/GNoGyn.png)
接着再次`call`调用,先修正内存中的字符串,接着获取`URLDownloadToFile()`函数调用地址:
![图片35 修正字符串](https://s1.ax1x.com/2020/04/03/GNowYF.png)
![图片36 获取URLDownloadToFile()函数调用地址](https://s1.ax1x.com/2020/04/03/GNorl9.png)
通过两次`call`调用来给`URLDownloadToFile()`函数传递参数:
![图片37 传递参数](https://s1.ax1x.com/2020/04/03/GNo2TK.png)
![图片38 未修正的参数](https://s1.ax1x.com/2020/04/03/GNofYD.png)
![图片39 修正参数](https://s1.ax1x.com/2020/04/03/GNo5SH.png)
之后调用`URLDownloadToFile()`函数从http://maq[.]com[.]pk/wehs下载文件到创建的Temp文件夹内,文件名为`smss`:
![图片40 URLDownloadToFile](https://s1.ax1x.com/2020/04/03/GNoo6A.png)
`call`调用的同时向`GetProcAddress()`传递参数,获取`MoveFile()`调用地址:
![图片41 获取MoveFile()调用地址](https://s1.ax1x.com/2020/04/03/GNoHmt.png)
两处`call`调用向`MoveFile()`传递参数:
![图片42 调用MoveFile](https://s1.ax1x.com/2020/04/03/GNoqTf.png)
将`smss`重新命名为`smss.exe`。之后获取`LoadLibrary()`调用地址:
![图片43 获取LoadLibrary调用地址](https://s1.ax1x.com/2020/04/03/GNoXtS.png)
`call`调用的同时传递参数:
![图片44 LoadLibrary(shell32.dll)](https://s1.ax1x.com/2020/04/03/GNoxpQ.png)
获取`ShellExecute()`调用地址:
![图片45 获取ShellExecute()调用地址](https://s1.ax1x.com/2020/04/03/GNTS6s.png)
通过三次`call`调用来给`ShellExecute()`传递参数,最后调用之:
![图片46 调用ShellExecute](https://s1.ax1x.com/2020/04/03/GNTpXn.png)
接下来执行的`smss.exe`,非本文重点,故不分析。
## 0x04 xx组织某样本分析
> 写文章截图的时候中途断过两次,故前后文某些地址(使用这些地址只是为了方便说明)不对应,望读者谅解。
> 另,此样本在打了CVE-2017-11882补丁的机器上无法被成功利用。
直接定位到漏洞触发点:
![图片47 第一次调用sub_443F6](https://s1.ax1x.com/2020/04/03/GNTCmq.png)
![图片48 复制数据长度](https://s1.ax1x.com/2020/04/03/GNTP00.png)
![图片49 复制内容](https://s1.ax1x.com/2020/04/03/GNTkkT.png)
第二次调用`sub_443F6`过程如下:
![图片50 第二次调用sub_443F6](https://s1.ax1x.com/2020/04/03/GNTAtU.png)
![图片51 复制数据长度](https://s1.ax1x.com/2020/04/03/GNTEhF.png)
![图片52 复制内容](https://s1.ax1x.com/2020/04/03/GNTe1J.png)
可以看到,栈上函数返回地址已经被覆盖:
![图片53 已覆盖栈上的数据](https://s1.ax1x.com/2020/04/03/GNTmc9.png)
但其并未直接执行到`sub_443E34`结束处,而是通过给其后调用的函数传参,再次执行`sub_443E34`(其调用函数的具体功能可结合IDA进行分析):
![图片54 调用sub_4428F0](https://s1.ax1x.com/2020/04/03/GNTnXR.png)
![图片55 调用sub_437C9D](https://s1.ax1x.com/2020/04/03/GNTKn1.png)
![图片56 调用sub_416352](https://s1.ax1x.com/2020/04/03/GNTM0x.png)
![图片57 调用sub_43A78F](https://s1.ax1x.com/2020/04/03/GNTQ76.png)
![图片58 再次执行sub_443E34](https://s1.ax1x.com/2020/04/03/GNT1AK.png)
下面来看第二次执行`sub_443E34`时调用`sub_443F6`的情况:
![图片59 第三次调用sub_443F6](https://s1.ax1x.com/2020/04/03/GNT3tO.png)
![图片60 复制数据长度](https://s1.ax1x.com/2020/04/03/GNT8hD.png)
![图片61 复制内容](https://s1.ax1x.com/2020/04/03/GUSZ9I.png)
![图片62 第四次调用sub_443F6](https://s1.ax1x.com/2020/04/03/GNzz1x.png)
![图片63 复制数据长度](https://s1.ax1x.com/2020/04/03/GNzx91.png)
![图片64 复制内容](https://s1.ax1x.com/2020/04/03/GNzjhR.png)
直接执行到`sub_443E34`结束处:
![图片65 sub_443E34结束处](https://s1.ax1x.com/2020/04/03/GNzOAJ.png)
其后执行流程:
![图片66 其后执行流程](https://s1.ax1x.com/2020/04/03/GUSSc6.png)
此处的`jmp 2911D4`值得说明一下,`2911D4`后20字节是在调用`sub_4428F0`时由`qmemcpy((void *)(v5 + 50), a4, 20u);`复制而来,其中源地址是0x18F3EC(可见图片54)。
计算接下来的跳转地址:
![图片67 计算跳转地址](https://s1.ax1x.com/2020/04/03/GUSpjK.png)
跳转到解密Shellcode部分:
![图片68 跳转到解密Shellcode](https://s1.ax1x.com/2020/04/03/GUSCnO.png)
解密Shellcode:
![图片69 解密Shellcode](https://s1.ax1x.com/2020/04/03/GUSPBD.png)
其后执行流程见下图(图中序号仅为表明顺序,并无他意):
![图片70 定位msvcrt.dll](https://s1.ax1x.com/2020/04/03/GUSiHe.png)
手动符号解析定位`msvcrt.dll`(由`cmp`语句比较的ASCII码可计算出)。
![图片71 定位kerner32.dll](https://s1.ax1x.com/2020/04/03/GUSkAH.png)
手动符号解析定位`kerner32.dll`(图中序号接上一张图片)
之后其调用的`sub_299122`如下:
![图片72 sub_299122](https://s1.ax1x.com/2020/04/03/GUSANd.png)
通过遍历`msvcrt.dll`的输入表查找`GetProcAddress`,它并非调用`kernel32.dll`的`GetProcAddress()`,而是`ntdll.dll`的`LdrGetProcedureAddress()`:
![图片73 LdrGetProcedureAddress](https://s1.ax1x.com/2020/04/03/GUSE4A.png)
再一次调用`sub_299122`,此次查找的是`VirtualProtect()`:
![图片74 VirtualProtect()](https://s1.ax1x.com/2020/04/03/GUSe3t.png)
![图片75 其后执行流程](https://s1.ax1x.com/2020/04/03/GUSmgP.png)
![图片76 保存到栈中局部变量](https://s1.ax1x.com/2020/04/03/GUSKu8.png)
调用`GetProcAddress()`返回`msvcrt.clearerr`的地址:
![图片77 GetProcAddress](https://s1.ax1x.com/2020/04/03/GUS1EQ.png)
调用`VirtualProtect()`修改`msvcrt.clearerr`页属性为0x40(PAGE_EXECUTE_READWRITE),大小是0x50:
![图片78 VirtualProtect](https://s1.ax1x.com/2020/04/03/GUSBE4.png)
对`msvcrt.clearerr`进行Inline Hook,修改指令长度为0x50,这解释了之前的修改页属性操作:
![图片79 Inline Hook](https://s1.ax1x.com/2020/04/03/GUS3Nj.png)
其实`msvcrt.clearerr`要实现的功能与`sub_6492C6`相同(详见图片77、78):
![图片80 VirtualProtect](https://s1.ax1x.com/2020/04/03/GUS84s.png)
调用`VirtualProtect()`修改`msvcrt.clearerr`页属性为0x20(PAGE_EXECUTE_READ),大小是0x50。
![图片81 GetProcAddress(GetTempPath)](https://s1.ax1x.com/2020/04/03/GUSY3q.png)
将返回的调用地址加5后,通过遍历`msvcrt.dll`的输入表查找`CreateFile`:
![图片82 查找CreateFile](https://s1.ax1x.com/2020/04/03/GUStg0.png)
![图片83 GetProcAddress(GetFileSize)](https://s1.ax1x.com/2020/04/03/GUSNvV.png)
此次是查找`VirtualAlloc`:
![图片84 查找VirtualAlloc](https://s1.ax1x.com/2020/04/03/GUSauT.png)
接下来所查找函数不一一截图,依次是`ReadFile`、`WriteFile`、`CloseHandle`、`CreateProcess`、`GetModuleFileName`、`ResumeThread`、`TerminateProcess`。
![图片85 GetProcess(GetThreadContext)](https://s1.ax1x.com/2020/04/03/GUSdDU.png)
其后传递给GetProcess的参数不再一一截图,依次是`ReadProcessMemory`、`VirtualQueryEx`、`VirtualProtectEx`、`GetModuleHandle`、`VirtualAllocEx`、`WriteProcessMemory`、`SetThreadContext`、`ZwUnmapViewOfSection`。
调用`GetTempPath()`:
![图片86 GetTempPath](https://s1.ax1x.com/2020/04/03/GUSr59.png)
之后将其于临时文件夹内释放的文件名拼接到路径后:
![图片87 拼接路径](https://s1.ax1x.com/2020/04/03/GUSyCR.png)
打开该文件:
![图片88 CreateFile](https://s1.ax1x.com/2020/04/03/GUS681.png)
其后行为不再一一截图,依次是`GetFileSize`(获取该大小)、`VirtualAlloc(0,0x3E000,0x3000,0x40)`(分配空间)、`ReadFile`(读取文件到分配的空间内)。
解密内存中的文件内容:
![图片89 解密](https://s1.ax1x.com/2020/04/03/GUScgx.png)
遍历文件句柄,找到符合下列条件的文件:
![图片90 遍历](https://s1.ax1x.com/2020/04/03/GUSgv6.png)
![图片91 遍历结果](https://i.loli.net/2020/04/03/FZSuONfGCUoxjYA.png)
调用`VirtualAlloc`分配空间并写入内容(并非解密后的文件内容):
![图片92 分配空间](https://i.loli.net/2020/04/03/kTizGUQou1hW5vF.png)
![图片93 WriteFile](https://i.loli.net/2020/04/03/qMa2IeGiKEQyDgZ.png)
![图片94 CloseHandle](https://i.loli.net/2020/04/03/PYkeXSHEZ6Wi3mB.png)
复制解密后的文件内容:
![图片95 复制文件内容](https://i.loli.net/2020/04/03/knbYiB1U2Gp6384.png)
之后创建一同名进程:
![图片96 创建进程](https://i.loli.net/2020/04/03/LoYvCxK7XNjUmA8.png)
![图片97 创建完成](https://i.loli.net/2020/04/03/3QT1IRvrwkHmnGd.png)
其后的部分行为见下图:
![图片98 GetThreadContext](https://i.loli.net/2020/04/03/85Fu3f6ktAwSWcb.png)
![图片99 ReadProcessMemory](https://i.loli.net/2020/04/03/aZ5XnMrKvdWxpBf.png)
![图片100 VirtualQueryEx](https://i.loli.net/2020/04/03/62peIOYguWkhc8U.png)
将解密后的文件内容写入创建的进程:
![图片101 写入到创建的进程](https://i.loli.net/2020/04/03/7WzE4tseFXvPIOn.png)
最终,结束原进程:
![图片102 结束原来的进程](https://i.loli.net/2020/04/03/lfKRMbWapi5cOTS.png)
解密后的文件会在临时目录释放两个文件("白加黑")并运行之:
![图片103 释放文件](https://i.loli.net/2020/04/03/nDUMtJi9rVbFR8w.png)
## 0x05 参考链接
- [手把手教你复现office公式编辑器内的第三个漏洞](https://www.anquanke.com/post/id/94841) 本帖最后由 天空藍 于 2020-4-7 19:55 编辑
图片较多,文字极少。一篇展示文而已。低价值的文章,不推荐阅读与加精。{:1_925:} 天空藍 发表于 2020-4-7 19:52
图片较多,文字极少。一篇展示文而已。低价值的文章,不推荐阅读与加精。
请问什么样的文章适合加精呢?? 大佬的神帖子 可以,可以,看了楼主两个帖子,很详细。 这神技术贴,难得看完{:1_907:} 大佬牛逼,吾辈楷模啊 学习学习,大有收获 赶紧膜拜下,不要说破,看都晕啊{:1_921:} 厉害呀。膜拜 后排膜拜大佬.jpg 第一期都没看懂