SideWinder某次定向攻击事件的一点思考

背景与前言

         根据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,说明数据解析完成。
mark

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

         rtfobj中OleObject.parse是于解析\objdata字段的数据,ole对象的结构如下:
mark

1
2
3
4
5
6
7
8
01050000 \\OleVersion
02000000 \\FormatId
08000000 \\ClassnameSize
5061636B61676500 \\Classname
00000000 \\待使用
00000000
7a3d0500 \\DataSize
020...

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

         第二个objdata是Equation.3对象,但是公式编辑器由于自身的问题,存在多个漏洞,所以如何判断该对象会触发CVE-200174-11882呢,我们看一下未经变形的poc。

1
2
3
4
5
6
7
8
{\object\objdata
01050000
02000000
0b000000
4571756174696F6E2E3300
00000000
00000000
81060000

         根据银雁冰师傅在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结构体如下:

1
2
3
4
5
6
7
8
9
10
struct EQNOLEFILEHDR {
WORD cbHdr; // length of header, sizeof(EQNOLEFILEHDR) = 28 bytes
DWORD version; // hiword = 2, loword = 0
WORD cf; // clipboard format ("MathType EF")
DWORD cbObject; // length of MTEF data following this header in bytes
DWORD reserved1; // not used
DWORD reserved2; // not used
DWORD reserved3; // not used
DWORD reserved4; // not used
};

         根据http://web.archive.org/web/20010304041035/http:/mathtype.com:80/support/tech/MTEF3.htm#Introduction,MTEF header结构体如下:

1
2
3
4
5
6
7
struct MTEFHEADER{
BYTE version
BYTE platform: 0 for MAC ,1 for Win
BYTE product: 0 for Math Type,1 for Equation Editor
BYTE product version:3
BYTE product subversion
}

         MTEF Byte Stream的结构体大致如下:

1
2
3
4
5
6
struct MTEFByteStream{
BYTE SIZE record:大小
BYTE PILE or LINE record:Tag
contents of PILE or LINE:contents of Tag
END record
}

         FONT Record 结构体如下:

1
2
3
4
5
6
{
tag
typeface number
style
font name
}

         我们使用一份非变形的CVE-2017-11882样本的Equation Native Stream Data数据来查看一下上述结构体的对应情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1c00 //cbHdr
00000200 //version
a8c3 //clipboard format
99020000 // cbObject
00000000 //reserved1
48905d00 //reserved2
6c9c5b00 //reserved3
00000000 //reserved4
03 //MTEF_version
01 //MTEF_platform
01 //MTEF_product
03 //MTEF_product version
0a //MTEF_product subversion
0a //SIZE
01 //可有可无
08 //Font[tag]
5a //typeface
5a //style
b844eb7112ba7856341231d08b088b098b096683c13cffe190909090909090909090909090909090909090901421400 //fontname

         了解完常规的poc,接下来看一下这次SideWinder本次攻击使用的真实样本大概是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
objdata
01050000 //OleVersion
02000000 //FormatId
0b000000 //ClassnameSize
4571756174696F6E2E3300 //Equation.3
00000000
00000000
81060000 //SIZE
02 //Equation Editor 3.x
}
c3
37
C7
05
E5 //size
01
08 //tag
11 //typeface
C6 //style
BA36646F1D81C20659D6E28B0A8B29BFBC6B22A681F70C0C64A68B1755FFD205D41275952D05127595FFE0E5B4264200

         EQNETD32.EXE通过OpenStream读取Equation流。
mark

         显然,样本并不是读取了常见的Equation Native,而是01Ole10Native。因为如果是常见的Equation Native流,Equation流开头应该是0x1C开头,此处是以0x02开头。而且解析器在最终读取Equation流已经将}等符号自动屏蔽。
mark

         如图,tag = 8,说明这是一个Font Tag,然后将其传入sub_43A87A函数。
mark

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

RunHTMLApplication如何加载js脚本

         恶意的shellcode最终会调用RunHTMLApplication执行js脚本,但是所传入的参数皆为null,所以RunHTMLApplication是如何加载js脚本的?如下是RunHTMLApplication的函数原型。可见第三个参数为调用的命令行参数。但是此参数为null,RunHTMLApplication是如何解析的呢?

1
2
3
4
5
6
HRESULT RunHTMLApplication(
HINSTANCE hinst,
HINSTANCE hPrevInst,
LPSTR szCmdLine,
int nCmdShow
);

         通过分析mshtml.dll中的导出函数RunHTMLApplication可知,首先会调用GetCmdLine(void)函数,主要是通过GetCommandLineW()函数获取命令行参数,然后通过CreateHTAMoniker函数解析所得到的命令行数据,获取命令行中:之前的内容,然后根据注册表HKCR\SOFTWARE\Classes\PROTOCOLS\Handler\内容调用相关协议处理器解析处理。
mark
mark

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

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

js如何反射加载C#程序

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

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

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

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

参考文章

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

  1. CVE-2017-11882漏洞分析、利用及动态检测
  2. 响尾蛇组织近期针对我国特定机构的APT攻击活动披露
  3. 利用JS加载.Net程序