CVE-2018-0798及利用样本分析

简介: •成因:EQNEDT32.exe在解析Matrix record时,并未检查长度,从而造成栈溢出。无论打不打CVE-2017-11882补丁都可以成功触发,使得攻击者可以通过刻意构造的数据内容及长度覆盖栈上的函数返回地址,从而劫持程序流程。

CVE-2018-0798及利用样本分析





1.本篇文章系Gcow安全团队复眼小组的ERFZE师傅原创,未经许可禁止转载.2.本篇文章从CVE-2018-0798漏洞的基础分析入手,分析了其漏洞分成因.以及演示了蔓灵花APT组织以及某样本组织在实际攻击活动之中所使用的样本的超级详细的分析,十分值得样本分析人员以及刚刚接触本漏洞的分析人员学习.3.本篇文章大约2700多字,103张图片,预计阅读时间9分钟.

0x01 漏洞描述

成因EQNEDT32.exe在解析Matrix record时,并未检查长度,从而造成栈溢出。无论打不打CVE-2017-11882补丁都可以成功触发,使得攻击者可以通过刻意构造的数据内容及长度覆盖栈上的函数返回地址,从而劫持程序流程。影响版本:Microsoft Office 2007, Microsoft Office 2010, Microsoft Office 2013,Microsoft Office 2016POC:CVE-2018-0798—— 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


其调用了两次sub_443F6C,但sub_443F6C在复制数据时并未检查传递进来的参数:


图片2 sub_443F6C


其中的数据长度可通过a1控制,具体计算方法是(2 * a1 + 9) >> 3,而其目的地址是由sub_443E34传递过来位于其开辟栈空间内的局部变量(int型):


图片3 栈


如此一来,便可通过精心构造的数据,覆盖sub_443E34函数的返回地址,进而控制执行流。

0x02.2 动态调试

POC地址已于上文给出。直接于sub_443E34处设断,成功断下后,直接执行到调用sub_443F6C前查看其传递参数:


图片4 传递参数


跟进查看,可以看到其计算后的实际复制数据长度:


图片5 计算后的复制长度


跟进其调用的sub_416352可以查看要复制数据:


图片6 要复制数据


直接执行到sub_443F6C结束处,可以看到:


图片7 已覆盖栈上的数据


sub_443E34再次调用sub_443F6C,其执行流程同上:


图片8 传递参数



图片9 计算后的复制长度



图片10 要复制数据


可以看到,已经覆盖栈上sub_443E34的返回地址,劫持了执行流:


图片11 已覆盖栈上的数据


回到sub_443E34,直接执行到结束处:


图片12 sub_443E34结束处


通过ROP跳转到WinExec()


图片13 ROP


成功弹出计算器:


图片14 calc


0x03 Bitter组织某样本分析

样本名称:Urgent Action.docx

样本MD5:02C2A68CE9A35F5F0E1B3456E09D6CC9

通过远程模板注入的方式下载一RTF格式文档:


图片15 远程URL


使用WinHex查看,确为RTF格式:


图片16 WinHex查看


添加.rtf后缀后打开文档。直接来到sub_443E34调用sub_443F6C处:


图片17 调用sub_443F6C及传递参数


此次调用sub_443F6C并未发生溢出,其复制数据长度如下:


图片18 复制数据长度


复制内容:


图片19 复制内容


其第二次调用sub_443F6C,发生溢出:


图片20 复制数据长度


复制内容:


图片21 复制内容


接下来直接执行到sub_443E34结束处,可以看到其劫持执行流:


图片22 sub_443E34结束处


通过ROP跳转到Shellcode:


图片23 ROP


Shellcode如下:


图片24 Shellcode


下面开始分析其功能。首先是计算跳转地址:


图片25 跳转


跳转之后,通过与计算出的EAX值比较的方式移动指针指向要复制的Shellcode,复制后跳转到Shellcode上执行:


图片26 复制Shellcode


通过PEB手动符号解析定位到kernel32.dll


图片27 定位kernel32.dll


定位kernel32.dll中的GetProcAddress()函数:


图片28 寻址GetProcAddress()


跳转后执行GetProcAddress()


图片29 call调用



图片30 获取CreateDirectory调用地址


之后通过call调用的形式给CreateDirectory()传递参数:


图片31 调用CreateDirectory


于C盘创建一名为Temp的文件夹。获取LoadLibrary()调用地址:


图片32 获取LoadLibrary调用地址


之后在call调用的同时传递参数:


图片33 call调用并传递参数



图片34 LoadLibrary(urlmon.dll)


接着再次call调用,先修正内存中的字符串,接着获取URLDownloadToFile()函数调用地址:


图片35 修正字符串



图片36 获取URLDownloadToFile()函数调用地址


通过两次call调用来给URLDownloadToFile()函数传递参数:


图片37 传递参数



图片38 未修正的参数



图片39 修正参数


之后调用URLDownloadToFile()函数从http://maq[.]com[.]pk/wehs下载文件到创建的Temp文件夹内,文件名为`smss`:


图片40 URLDownloadToFile


call调用的同时向GetProcAddress()传递参数,获取MoveFile()调用地址:


图片41 获取MoveFile()调用地址


两处call调用向MoveFile()传递参数:


图片42 调用MoveFile


smss重新命名为smss.exe。之后获取LoadLibrary()调用地址:


图片43 获取LoadLibrary调用地址


call调用的同时传递参数:


图片44 LoadLibrary(shell32.dll)


获取ShellExecute()调用地址:


图片45 获取ShellExecute()调用地址


通过三次call调用来给ShellExecute()传递参数,最后调用之:


图片46 调用ShellExecute


接下来执行的smss.exe,非本文重点,故不分析。

0x04 xx组织某样本分析

写文章截图的时候中途断过两次,故前后文某些地址(使用这些地址只是为了方便说明)不对应,望读者谅解。另,此样本在打了CVE-2017-11882补丁的机器上无法被成功利用。

直接定位到漏洞触发点:


图片47 第一次调用sub_443F6



图片48 复制数据长度



图片49 复制内容


第二次调用sub_443F6过程如下:


图片50 第二次调用sub_443F6



图片51 复制数据长度



图片52 复制内容


可以看到,栈上函数返回地址已经被覆盖:


图片53 已覆盖栈上的数据


但其并未直接执行到sub_443E34结束处,而是通过给其后调用的函数传参,再次执行sub_443E34(其调用函数的具体功能可结合IDA进行分析):


图片54 调用sub_4428F0



图片55 调用sub_437C9D



图片56 调用sub_416352



图片57 调用sub_43A78F



图片58 再次执行sub_443E34


下面来看第二次执行sub_443E34时调用sub_443F6的情况:


图片59 第三次调用sub_443F6



图片60 复制数据长度



图片61 复制内容



图片62 第四次调用sub_443F6



图片63 复制数据长度



图片64 复制内容


直接执行到sub_443E34结束处:


图片65 sub_443E34结束处


其后执行流程:


图片66 其后执行流程


此处的jmp 2911D4值得说明一下,2911D4后20字节是在调用sub_4428F0时由qmemcpy((void *)(v5 + 50), a4, 20u);复制而来,其中源地址是0x18F3EC(可见图片54)。

计算接下来的跳转地址:


图片67 计算跳转地址


跳转到解密Shellcode部分:


图片68 跳转到解密Shellcode


解密Shellcode:


图片69 解密Shellcode


其后执行流程见下图(图中序号仅为表明顺序,并无他意):


图片70 定位msvcrt.dll


手动符号解析定位msvcrt.dll(由cmp语句比较的ASCII码可计算出)。


图片71 定位kerner32.dll


手动符号解析定位kerner32.dll(图中序号接上一张图片)

之后其调用的sub_299122如下:


图片72 sub_299122


通过遍历msvcrt.dll的输入表查找GetProcAddress,它并非调用kernel32.dllGetProcAddress(),而是ntdll.dllLdrGetProcedureAddress()


图片73 LdrGetProcedureAddress


再一次调用sub_299122,此次查找的是VirtualProtect()


图片74 VirtualProtect()



图片75 其后执行流程



图片76 保存到栈中局部变量


调用GetProcAddress()返回msvcrt.clearerr的地址:


图片77 GetProcAddress


调用VirtualProtect()修改msvcrt.clearerr页属性为0x40(PAGE_EXECUTE_READWRITE),大小是0x50:


图片78 VirtualProtect


msvcrt.clearerr进行Inline Hook,修改指令长度为0x50,这解释了之前的修改页属性操作:


图片79 Inline Hook


其实msvcrt.clearerr要实现的功能与sub_6492C6相同(详见图片77、78):


图片80 VirtualProtect


调用VirtualProtect()修改msvcrt.clearerr页属性为0x20(PAGE_EXECUTE_READ),大小是0x50。


图片81 GetProcAddress(GetTempPath)


将返回的调用地址加5后,通过遍历msvcrt.dll的输入表查找CreateFile


图片82 查找CreateFile



图片83 GetProcAddress(GetFileSize)


此次是查找VirtualAlloc


图片84 查找VirtualAlloc


接下来所查找函数不一一截图,依次是ReadFileWriteFileCloseHandleCreateProcessGetModuleFileNameResumeThreadTerminateProcess


图片85 GetProcess(GetThreadContext)


其后传递给GetProcess的参数不再一一截图,依次是ReadProcessMemoryVirtualQueryExVirtualProtectExGetModuleHandleVirtualAllocExWriteProcessMemorySetThreadContextZwUnmapViewOfSection

调用GetTempPath()


图片86 GetTempPath


之后将其于临时文件夹内释放的文件名拼接到路径后:


图片87 拼接路径


打开该文件:


图片88 CreateFile


其后行为不再一一截图,依次是GetFileSize(获取该大小)、VirtualAlloc(0,0x3E000,0x3000,0x40)(分配空间)、ReadFile(读取文件到分配的空间内)。

解密内存中的文件内容:


图片89 解密


遍历文件句柄,找到符合下列条件的文件:


图片90 遍历



图片91 遍历结果


调用VirtualAlloc分配空间并写入内容(并非解密后的文件内容):


图片92 分配空间



图片93 WriteFile



图片94 CloseHandle


复制解密后的文件内容:


图片95 复制文件内容


之后创建一同名进程:


图片96 创建进程



图片97 创建完成


其后的部分行为见下图:


图片98 GetThreadContext



图片99 ReadProcessMemory



图片100 VirtualQueryEx


将解密后的文件内容写入创建的进程:


图片101 写入到创建的进程


最终,结束原进程:


图片102 结束原来的进程


解密后的文件会在临时目录释放两个文件[白加黑]并运行白exe以执行下一步的恶意行为:


图片103 释放文件


0x05 参考链接

手把手教你复现office公式编辑器内的第三个漏洞——https://www.anquanke.com/post/id/94841


相关文章
|
4月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
251 5
|
10月前
|
应用服务中间件 Linux 网络安全
nginx安装部署ssl证书,同时支持http与https方式访问
为了使HTTP服务支持HTTPS访问,需生成并安装SSL证书,并确保Nginx支持SSL模块。首先,在`/usr/local/nginx`目录下生成RSA密钥、证书申请文件及自签名证书。接着,确认Nginx已安装SSL模块,若未安装则重新编译Nginx加入该模块。最后,编辑`nginx.conf`配置文件,启用并配置HTTPS服务器部分,指定证书路径和监听端口(如20000),保存后重启Nginx完成部署。
3046 8
|
C++
C++ Qt开发:QUdpSocket网络通信组件
QUdpSocket是Qt网络编程中一个非常有用的组件,它提供了在UDP协议下进行数据发送和接收的能力。通过简单的方法和信号,可以轻松实现基于UDP的网络通信。不过,需要注意的是,UDP协议本身不保证数据的可靠传输,因此在使用QUdpSocket时,可能需要在应用层实现一些机制来保证数据的完整性和顺序,或者选择在适用的场景下使用UDP协议。
663 2
在Linux中,如何把系统中不用服务关掉?
在Linux中,如何把系统中不用服务关掉?
|
Python
python第三方库-字符串编码工具 chardet 的使用(python3经典编程案例)
这篇文章介绍了如何使用Python的第三方库chardet来检测字符串的编码类型,包括ASCII、GBK、UTF-8和日文编码的检测示例。
620 6
|
算法 C语言
数据结构与算法——拓扑排序(引例、拓扑排序、伪代码、代码、关键路径问题)
数据结构与算法——拓扑排序(引例、拓扑排序、伪代码、代码、关键路径问题)
258 0
|
前端开发 API
[译] 实战 React 18 中的 Suspense
[译] 实战 React 18 中的 Suspense
|
移动开发 前端开发 项目管理
基于jeecg-boot的nbcio-boot亿事达企业管理平台发布
基于jeecg-boot的nbcio-boot亿事达企业管理平台发布
201 0
|
安全 PHP
【攻防世界】file_include (PHP伪协议+过滤器)
【攻防世界】file_include (PHP伪协议+过滤器)
|
开发工具 数据安全/隐私保护 C++
windows openssl安装和基本使用(代码演示)
本文主要讲到了openssl的基本使用方法,开发环境为windows,开发工具为VS2019.本文主要是说明openssl如何使用,不介绍任何理论知识,如果有不懂的,请自行百度。个人建议下一个everything查询工具,真的很好用,比window自带的查询快了很多,可以查询自己想要的文件
934 0
windows openssl安装和基本使用(代码演示)