背景与前言
根据360和微步在线公众号披露的情报称,从今年(2020年)11月份起,SideWinder开始策划针对中国的定向攻击。正如360威胁情报中心公众号所说,本次攻击是SideWinder利用邮件等方式传播带有远程模板的恶意文档发动的一次攻击,具体过程如下,首先利用远程模板注入从远程C2服务器上加载含有CVE-2017-11882漏洞的文档,之后执行恶意payload,加载释放在%temp%中的恶意js文件,该js脚本采用内存反射加载的方式执行C#文件,进而部署恶意后门程序。
觉得这次攻击使用的技术较为新奇,顾在闲暇之时复现了一下本次SideWinder定向攻击,在复现过程了,心中产生了如下3个疑惑:
- 1.为何是CVE-2017-11882
如果借用沙箱等动态的方式,通过检测指定的溢出点的数据,自然可以判断出是触发了CVE-2017-11882漏洞。但是如果采用静态分析的方式呢。 - 2.RunHTMLApplication如何加载js脚本
在触发CVE-2017-11882之后执行的恶意的shellcode,最后会通过调用RunHTMLApplication,但是参数皆为null,如何最后执行了释放到%temp%的js脚本 - js脚本是如何反射加载C#程序
本文旨在记录自己在分析中所遇到的种种问题。文中所涉及的样本Hash在微步文章中已给出。各位师傅可自行下载。本人才疏学浅,文中如有错误,请各位积极斧正。
为何是CVE-2017-11882
根据银雁冰师傅的文章,里面详细描述了漏洞产生的原因和构造poc的方法。据文章中所说的漏洞成因“是EQNEDT32.EXE进程在读入包含MathType的ole数据时,在拷贝公式字体名称时没有对名称长度进行校验,从而造成栈缓冲区溢出,是一个非常经典的栈溢出漏洞.”
触发CVE-2017-11882的样本是一个RTF文件,可以采用oletool工具集来查看,但是只能查看其中的对象数据,并不能判断其是触发了那个漏洞。于是,我想到去了解RTF文件格式,继而确定触发漏洞的数据。进而确定是否触发CVE-2017-11882。但是微软的RTF文件格式文档写的较为繁琐,暂时没有从中获取有用的信息。于是我想到可以通过查看rtfobj是如何解析rtf文件的进而学习RTF的文件格式。
通过查看rtfobj.py发现,解析rtfobj是通过RtfObjParser.parse函数解析RTF文件的。在RtfObjParser.parse中,rtfobj是根据”{“,”}”,”\“来判断RTF的层级,如果遇到”{“,则层级(level)+1,如果遇到”}”,则层级-1,如果层级为0,说明数据解析完成。

熟悉RTF恶意样本的知道,RTF没有宏代码,但是可以通过携带的Ole对象来实现类似于宏代码的操作。而\object字段则表示文件中存在其他格式文件,可以镶嵌图片,链接文件,html等文件。其中\objdata字段则表示存储了对象数据,通常可以Ole对象。
rtfobj中OleObject.parse是于解析\objdata字段的数据,ole对象的结构如下:

并且通过rtfobj代码,发现OLe数据是以小端方式进行存储的,并且除去一些和分析无关的字节,其结构可基本总结为filename,sourcefile,tempfile,以及data字段。由此可见,这一个objdata是一个ole对象,包含一个名为1.a的js文件。这并不是能触发漏洞的objdata。在看一下第二个objdata

第二个objdata是Equation.3对象,但是公式编辑器由于自身的问题,存在多个漏洞,所以如何判断该对象会触发CVE-200174-11882呢,我们看一下未经变形的poc。
根据银雁冰师傅在CVE-2017-11882漏洞分析、利用及动态检测中从零开始构造POC所描述的那样,Equation Native结构的构成为Equation Native Stream Data = EQNOLEFILEHDR + MTEFData,而MTEFData = MTEF header + MTEF Byte Stream。
根据银雁冰所提到的参考文献http://web.archive.org/web/20010304111449/http:/mathtype.com:80/support/tech/MTEF_storage.htm#OLE%20Objects,EQNOLEFILEHDR结构体如下:
根据http://web.archive.org/web/20010304041035/http:/mathtype.com:80/support/tech/MTEF3.htm#Introduction,MTEF header结构体如下:
MTEF Byte Stream的结构体大致如下:
FONT Record 结构体如下:
我们使用一份非变形的CVE-2017-11882样本的Equation Native Stream Data数据来查看一下上述结构体的对应情况。
了解完常规的poc,接下来看一下这次SideWinder本次攻击使用的真实样本大概是这样的
EQNETD32.EXE通过OpenStream读取Equation流。

显然,样本并不是读取了常见的Equation Native,而是01Ole10Native。因为如果是常见的Equation Native流,Equation流开头应该是0x1C开头,此处是以0x02开头。而且解析器在最终读取Equation流已经将}等符号自动屏蔽。
、
如图,tag = 8,说明这是一个Font Tag,然后将其传入sub_43A87A函数。

最终在0041774e处获取Font的Name。并最终传入sub_4115A7。显然这已经超过了0x24个字节本身的长度。造成CVE-2017-11882漏洞。


RunHTMLApplication如何加载js脚本
恶意的shellcode最终会调用RunHTMLApplication执行js脚本,但是所传入的参数皆为null,所以RunHTMLApplication是如何加载js脚本的?如下是RunHTMLApplication的函数原型。可见第三个参数为调用的命令行参数。但是此参数为null,RunHTMLApplication是如何解析的呢?
通过分析mshtml.dll中的导出函数RunHTMLApplication可知,首先会调用GetCmdLine(void)函数,主要是通过GetCommandLineW()函数获取命令行参数,然后通过CreateHTAMoniker函数解析所得到的命令行数据,获取命令行中:之前的内容,然后根据注册表HKCR\SOFTWARE\Classes\PROTOCOLS\Handler\内容调用相关协议处理器解析处理。


但是命令行参数是何时被修改的呢,在shellcode中,首先会利用GetCommandLine获取命令行参数,然后通过异或加密的方式加密该参数。最后通过RunHTMLApplication函数中的命令行解释器就可以获取到被修改的命令行参数。


命令行参数很简单,是几行js代码,主要是读取位于%temp%1.a的js脚本内容,并执行之。

js如何反射加载C#程序
光看代码,yMVonTE和dgDDmZRs很显然是用来加密后续需要使用到的一些属性。部分代码的加密逻辑是这样的,首先采用利用变形过的Base64去编码传入的原始字符串,然后再用生成出来的变形的Base64字符串进行两轮循环的加密,由此产生后续可以执行的代码流。

可以直接使用浏览器调试去进一步还原被加密之后的js代码,这里可以参考这篇文章,通过上述代码边解密,边执行的特性,很容易还原整个原始代码,整个流程主要分为两个部分,首先通过xnEVdV()函数去获取csc.exe文件的版本号,分别是V2的版本和V4的版本。

第二歩需要将被Base64编码过的Paload写入内存中。很显然,下一步需要执行的PE文件就在其中。


在公众号中已经介绍了使用到的技术是DotNetToJScript,在其github上下载到了DotNetToJScript项目,并利用VS2015进行编译,利用官网所提供的参数编译得到一份Test.js,通过对比Testjs和去混淆之后的js脚本,发现流程基本一致。都是利用CreateInstance创建一个实例,然后调用方法的方式进行反射注入的。

参考文章
本次攻击还是采用SideWinder过去经常使用的攻击技巧,变化不大,但适合没有接触的师傅共同学习,探讨。