freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

“噩梦公式”二代 | 2018年微软修复的首个Office 0day漏洞(CVE-2018-0802)分析
2018-01-10 16:30:57
所属地 海外

*本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。

简介

2018年1月的微软安全补丁中修复了360核心安全高级威胁应对团队捕获的office 0day漏洞(CVE-2018-0802),该漏洞几乎影响微软目前所支持的所有office版本。这是继360在全球范围内首家截获Office 0day漏洞(CVE-2017-11826)在野攻击以来,发现的第二起利用零日漏洞的在野高级威胁攻击。360核心安全团队一直与微软保持积极沟通,一起推进该0day漏洞的修复,让漏洞得到妥善解决后再披露漏洞信息。该漏洞的技术原理类似于潜伏了17年的“噩梦公式”漏洞(CVE-2017-11882),是黑客利用office内嵌的公式编辑器EQNEDT32.EXE再次发起的攻击,我们将其命名为“噩梦公式二代”(CVE-2018-0802)。

攻击流程分析

我们捕获了多个“噩梦公式二代”的在野攻击,在野样本嵌入了利用Nday漏洞和0day漏洞的2个公式对象同时进行攻击,Nday漏洞可以攻击未打补丁的系统,0day漏洞则攻击全补丁系统,绕过了CVE-2017-11882补丁的ASLR(地址随机化)安全保护措施,攻击最终将在用户电脑中植入恶意的远程控制程序。

Clipboard Image.png

图:“噩梦公式二代”在野样本攻击流程

漏洞细节分析

“噩梦公式二代”为CVE-2017-11882的补丁绕过漏洞,类型为栈溢出,根本原因为微软在“噩梦公式一代”的补丁中没有修复另一处拷贝字体FaceName时的栈溢出。本次漏洞在未打补丁的版本上只会造成crash,但在打补丁的版本上可以被完美利用。下面我们通过poc样本来分析CVE-2018-0802漏洞。

静态分析

与CVE-2017-11882一样,本次漏洞的触发数据位于所提取OLE对象的“Equation Native”流内。图1中红线圈出部分为核心数据,共0x99=153字节。0x08代表font tag,紧随其后的00 01分别代表字体的typeface和style,从33开始直到25 00的区域为Font名称,为栈溢出时拷贝的数据。这部分数据里面包含了shellcode、bypass ASLR的技巧,进程命令行以及相关用于填充的数据,我们将在后面分析它们。

Clipboard Image.png

图1

Equation Native 数据结构

据网上公开的资料,整个“EquationNative”的数据构成为:

EquationNative Stream Data = EQNOLEFILEHDR + MTEFData

其中MTEFData =MTEF header + MTEF Byte Stream。

QNOLEFILEHDR的结构如图2所示:Clipboard Image.png

图2

MTEF header的结构如表1所示,关于这个结构,我们观察到的实际数据和格式规范存在差异,下表中以实际观察到的为主:

偏移量 说明
0 MTEF版本号 0x03
1 该数据的生成平台 0x00表示在Macintosh平台生成,0x01表示Windows平台生成
2 该数据的生成产品 0x00表示由MathType生成,0x01表示由公式编辑器生成
3 产品主版本号 0x03
4 产品副版本号 0x0A

表1 

在攻击样本中,MTEF ByteStream结构如表2所示:

初始SIZE记录
FONT记录
FONT内容
剩余数据

表2

FONT记录及FONT内容结构如表3所示:

成员 说明 备注
tag 0x08 1字节
tface typeface编号 1字节
style 字体样式 1字节
name 字体名称 以NULL结尾的ASCII字符串

表3

补丁绕过分析

CVE-2018-0802的漏洞触发点位于sub_21E39(在IDA中将模块基址设为0),如图3所示,可以看出该函数的功能为根据公式中的字体数据来初始化一个LOGFONT结构体:

Clipboard Image.png

图3 

我们来看一下微软对于LOGFONT结构体的说明(图4)。可以看到这个结构体的最后一个成员为lfFaceName,

Clipboard Image.png

图4:LOGFONT结构体

我们再看一下微软对lfFaceName成员的说明(图5)。可以看到lfFaceName代表了字体的typeface名称,在所分析的版本上,它是一个以空结尾的char型字符串,最大长度为32,其中包含终止符NULL。

Clipboard Image.png

图5 

问题很明显:图3红框内的代码在拷贝字体FaceName时并没有限制拷贝长度,而拷贝的源数据为用户提供的字体名称,目的地址为父函数传递进来的一个LOGFONT结构体地址。我们回溯到sub_21E39的父函数来看一下(图6),可以看到这个地址位于父函数开辟的栈上,是父函数的一个局部变量。攻击者通过构造恶意数据,覆盖了父函数(sub_21774)的返回地址的后两个字节,然后将控制流导向了位于栈上的shellcode。

Clipboard Image.png

图6

分析过程中我们发现一处疑似递归的地方,图7为sub_21774的反汇编代码,可以看到sub_21774先是调用了漏洞函数sub_21E39去初始化一个LOGFONT结构体,然后调用相关API,传入这个结构体,从系统获取到一个字体名称保存到Name。随后,它将获取到的Name和用户提供的lpLogFont作对比,如果不一致(并且sub_115A7函数需要返回False),会再根据a3指定的条件来继续调用或者不调用自身,而a3为sub_21E39函数的第3个参数。

Clipboard Image.png

图7

我们来看一下第3个参数的传参,否则可能存在多次递归,不能有效利用本次溢出。根据之前CVE-2017-11882的调试结果(图8),我们可以看到,在解析用户提供的font数据时,调用sub_21774的函数为sub_214C6。我们回溯到sub_214C6看一下(图9),sub_214C6调用sub_21774前给第三个参数传的值为1,所以图7中的if(a3)为真。我们再来看一下图7,sub_21774递归调用自己时对第3个参数传的值为0,这意味着sub_21774不会再次调用自己,递归层级只会有1级。分析到这里,递归的疑惑解决了。

Clipboard Image.png

图8:CVE-2017-11882触发执行流

 Clipboard Image.png

图9 

分析到这里还有一个问题,那就是在_strcmpi(lpLogfont,&Name)不成立的情况下(如果font数据为用户伪造,此处肯定不成立),sub_115A7会被调用,这意味着会走到CVE-2017-11882的溢出点。在未打11月补丁的版本上,如果要成功利用CVE-2017-11882,CVE-2018-0802的点就不会发生溢出,因为前者需要的溢出长度比后者小很多,且拷贝最后有一个NULL符截断(我们知道溢出到CVE-2017-11882的可控eip只需要0x2C个字节,而通过下文(图11)的分析我们可以知道溢出到CVE-2018-0802的可控eip需要0x94字节)。另一方面,在未打11月补丁的版本上,想要触发CVE-2018-0802,就必然会先触发CVE-2017-11882。总之,CVE-2018-0802在11补丁前的版本上无法利用。

可是,从图10可以看到,在11月的补丁中,微软在CVE-2017-11882溢出点的拷贝前,对拷贝长度进行了0x20的长度限制,并且拷贝完成后,在拷贝最后手动加了一个NULL,从而使CVE-2017-11882失效。这直接导致打补丁前无法被利用的CVE-2018-0802可以被利用了!现在,只要sub_115A7返回False,漏洞就可以得到完美利用,而实际调试发现,sub_115A7返回False。

Clipboard Image.png

图10

动态分析

溢出点的数据拷贝

有了上面的分析,动态分析就变得很简单了。既然本次溢出点会拷贝数据,我们来监控一下每次拷贝时的源字符串和相应的栈回溯,我们先进入OLE数据相关的Load函数(sub_6881),然后在拷贝数据前下断点并进行输出,结果如代码所示:

Clipboard Image.png

Clipboard Image.png

Clipboard Image.png

Clipboard Image.png

Clipboard Image.png 

从日志中可以看到存在两次拷贝,通过栈回溯我们可以知道这两次拷贝正是静态分析中对sub_21174的两次调用。第一次是sub_214c6对sub_21174的调用,第二次是sub_21174对自身的调用。可以看到第一次拷贝时明显发生了栈溢出。这里稍微提一下,cb ce cc e5代表的是宋体。

我们来详细计算一下需要溢出多少长度才能控制父函数(sub_21174)的返回地址(这个问题的结论在“补丁绕过分析”一节已被提及),由图11可知,从lfFaceName(-0x90)溢出到ret_addr(+0x4),一共需要0x94字节,超出0x94部分的字节会逐个从低地址开始覆盖返回地址。

Clipboard Image.png

图11

我们对照poc里面的数据来看一下,如图12所示,蓝色部分为溢出的前0x94字节,25 00 为溢出的最后两个字节,00为终止符,拷贝时遇到00就停止。按照小端地址布局,poc运行时,EIP只会被覆盖低位的2个字节。为什么这样做?答案是为了绕过ASLR。

Clipboard Image.png

图12

Bypass ASLR

我们来看一下为什么区区两个字节就可以绕过ASLR。

首先我们要清楚,补丁文件是开启了ASLR的,如图13所示。这样一来每次加载EQNEDT32.EXE时的基址都是随机的,所以溢出时需要考虑的第一个问题就是如何绕过ASLR。(至于DEP,由图14可以看到,补丁文件的EQNEDT32.EXE未开启DEP,所以正常情况下无需考虑DEP)

不幸的是,攻击者显然对Windows系统机制和防御措施非常了解。因为在Windows平台上,32位进程的ASLR每次只随机化地址的高2个字节,而低2个字节保持不变。假如能在被覆盖的地址的同一片低0xFFFF空间内找到一个ret指令,并且满足形如0xABCD00XY的这种地址(其中ABCD及XY为6个任意16进制数,地址中倒数第二个字节必须为0x00,因为复制完后需要精确截断),就可以直接利用这个ret跳转到栈上。由于无需绕过DEP,所以可以在栈上直接执行shellcode。

Clipboard Image.png

图13:EQNEDT32.EXE的ASLR状态为启用,DEP为非永久DEP

 Clipboard Image.png

图14: EQNEDT32.EXE的DEP状态为停用

更加不幸的是,在EQNEDT32.EXE模块内,微软还真给且仅给了这样一个地址(图15),满足条件的地址有且仅有一个,即20025,eip中被覆盖的两个字节25 00是唯一的,没有第二个满足条件的ret。

Clipboard Image.png

图15

我们来思考一下sub_21174原来的返回地址是什么?当然是sub_214C6调用sub_21174的下一条指令的地址,由图16可以看到这个地址的偏移为214E2,按照图12的覆盖方式,覆盖后的偏移变成了20025,由上面的分析及图17中可以看到,这个地址是一条ret指令。这条指令会弹出sub_214C6给sub_21174的第1个参数,并且将控制流切换到这个值去执行。雪上加霜的是,这第1个参数恰好为lpLogFont,正是用户所提供的FontName。所以ret执行后,控制流会被转移到栈上,并且恰好开始执行用户提供的FontName的第一个字节。

Clipboard Image.png

图16

 Clipboard Image.png

图17

样本A的Shellcode分析

在针对样本A改造的poc中,控制流劫持及shellcode部分的执行如图18所示:

Clipboard Image.png

图18:由于递归的存在,从sub_21774函数中需要返回两次,这解释了前两个ret

         jmpeax指令后会马上调用WinExec,而命令行参数恰好为calc.exe,如图19所示:

Clipboard Image.png

图19

样本B的Shellcode分析

样本B绕过ASLR的方式和样本A一致,但shellcode部分与样本A不一样。样本B的shellcode会通过PEB找到kernel32.dll的导出表(图20和图21),然后通过特定的hash算法(图21)在导出表中搜索比较所需函数的哈希值,所需函数的哈希值在shellcode中所给出。随后,shellcode会将查找到的函数地址保存到之前存放hash值的地方(图22)。

Clipboard Image.png

图20:样本B的shellcode中所给出的hash值及拷贝的路径名称

 Clipboard Image.png

图21:通过hash值在kernel32.dll的导出表中查找所需函数

Clipboard Image.png 

图22:查找函数地址前后栈上数据的对比

 在成功查找到函数并且将地址保存到栈上后,先调用ExpandEnvironmentStringsA函数展开短路径(短路径保存在shellcode中),再调用CopyFileA将payload拷贝到word插件目录下,从而让payload随着word下次启动自动加载到内存。最后调用ExitProcess退出公式编辑器进程(图23)。整个过程并不影响文档的正常打开。

Clipboard Image.png

图23:展开短路径,拷贝文件,退出进程

总结

“噩梦公式二代”(CVE-2018-0802)所使用的0day漏洞堪称CVE-2017-11882的双胞胎漏洞,攻击样本中的一个漏洞针对未打补丁前的系统,另外一个漏洞针对打补丁后的系统,利用两个OLE同时进行攻击,黑客精心构造的攻击完美兼容了系统漏洞补丁环境的不同情况。这个漏洞的利用技巧和Bypass ASLR的方式都带有一定的巧合性,假如EQNEDT32.EXE模块内没有一条满足条件的ret指令可以用来绕过ASLR,假如lpLogFont不是sub_21774的第一个参数,假如CVE-2017-11882的补丁修复方式强制开启了DEP保护,“噩梦公式二代”将没有可乘之机。

最新的360安全产品已可以检测并防止此0day漏洞的攻击,同时我们建议用户及时更新2018年1月的微软安全补丁。

参考

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-0802

*本文作者:360安全卫士,转载请注明来自FreeBuf.COM

# office # 噩梦公式
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者