0x00 前言

最近看了一点非PE的样本,发现CVE-2017-11882这个漏洞在实战中的使用非常频繁,有很多在活的APT组织都还在使用,之前都是直接去捕获利用该洞释放的文件,最近有时间,就对11882进行一个完善一点的跟踪。
11882属于Office Equation类型的漏洞,2017年底发现,目前使用较多的有11882,0802,0798三个漏洞,今天先分析一下11882的漏洞原理,以及实战中遇到的两个样本。

0x01 POC复现

这个漏洞的poc有很多,这里使用的是https://github.com/Ridter/CVE-2017-11882/
通过python Command_CVE-2017-11882.py -c “cmd.exe /c calc.exe” -o test.doc命令生成test.doc
然后在安装了office2013上的机器打开,弹出了计算器。
image.png

调试样本

在调试这个样本之前
先以一个简单的例子说明一下程序是如何利用esp和ebp实现函数调用的
下面是一段很简单的汇编代码,如下图所示,目前eip指向0040108E,指令是call 999.00401005
esp指向0012FF20(当前函数栈顶)
ebp指向0012FF80(当前函数栈底)
image.png

F7跟进到call里面之后,ebp没变,esp-4变成了0012FF1C
0012FF1C地址存放了00401093地址,该地址是当前函数的调用完之后应该继续执行的地址
所以我们大多数情况下可以将call执行理解为push 返回地址 然后jmp 到函数起始地址的两条指令的集合
image.png

然后程序通过
push ebp
mov ebp,esp
sub esp,0x44
首先保存之前的ebp,然后把esp的值赋值给ebp,现在esp和ebp就指向同一个地方了
再通过对esp的sub运算开辟出当前函数使用的新堆栈
image.png

可以看到,现在ebp指向0012FF18,该地址里面存的的值是0012FF80,是之前的ebp,该地址后面就是函数的返回地址

程序最后通过
mov esp,ebp
pop ebp
retn
的组合指令作为返回
因为ebp后面就是函数的返回地址,所以这里先通过mov指令,将ebp的值赋值给esp,然后pop ebp,这pop指令执行完之后,esp的值就会+4,esp就会指向之前的返回地址
再通过ret指令就可以成功返回
image.png

好,现在来看看这个样本。
首先直接打开word程序:
image.png

在插入选项下,选择对象,然后插入Microsoft公式3.0
image.png

现在启动x32dbg,选择文件->附加:
image.png

因为我们这里知道文档运行之后会打开计算器,所以附加上EQNEDT32.exe之后通过分别给CreateProcessA和CreateProcessW设置断点
image.png

F9跑起来,然后使用word打开poc生成的test.doc文档,打开之后成功断下来:
image.png

在返回地址处设置断点,返回回去发现计算器已经弹出了,这里可以通过ebp查看一下CreateProcess的调用
image.png

返回回来发现是WinExec函数调用的
image.png

所以我们重新按照上面的方法,对WinExec函数下断:
image.png

此时的00430C12是用户态地址,按道理来讲,ebp应该存放了当前函数的返回地址,但是这里是41414141,很明显ebp已经被破坏了。
image.png
所以我们向上看看,ebp是在哪儿被破坏的。

由于ebp被淹没,我们这里往上看,找找其他的返回地址:
image.png

这里最近的一个返回地址是在00411837,返回回去,往上翻,找到00411837所在函数的起始地址,设置一个断点,重新运行,让程序断在这个函数里面。
image.png

往下走几步在00411658的地方发现这样一条指令:
rep movsd dword ptr es:[edi], dword ptr [esi]
该指令执行完之后,ebp就被41414141覆盖
而该指令的功能是将esi的值传送到edi所指的位置。
我们分别查看一下esi和edi的值:
ESI:
image.png

EDI:
image.png

值得注意的是,此时ebp的值是0018F1D0
很明显该地址在EDI所指的地址后面
image.png

rep movsd dword ptr es:[edi], dword ptr [esi]执行完之后,EDI被成功赋值,可以看到,此时ebp所在的值已经从0018F214变成了41414141
ebp后面的返回地址已经从004115d8更改为了00430c12
image.png

查看一下00430C12地址:
image.png

OK原来是这样跳过来的,过来执行WinExec函数之后,又在里面调用了CreateProcess函数,函数的参数为:
image.png

所以成功弹出计算器。

eqnedt32.exe

我们把EQNEDT32.exe拷贝出来,先查看下属性,可以看到,这是一个2000年发布的程序,一直没有被更新过。
image.png

通过对上面样本的分析,我们已经知道该漏洞的触发点是在00411658处
我们通过IDA打开eqnedt32.exe并跳转到该地址处
image.png

F5看一下
image.png
原来漏洞的导致原因是strcpy函数在使用的时候,未对参数的长度进行判断和限制,从而导致栈溢出。
而至于流程到底是怎么过来的,以及文档格式的其他分析之后再单独写一个文章介绍。

实战样本分析

原始样本

样本hash:14F28BD8361AE90DBFABCB31767A356B
VT上搜索该样本,可以发现样本已经被打上了11882的标签:
image.png

我们来调试一下,看看这个到底是不是11882的利用。
winhex打开,该文档的确是rtf:
image.png

我们通过上面的分析已经知道,11882的漏洞触发点在0041160F函数中,具体的位置应该是00411658
就还是通过之前的方法附加EQNEDT32.exe,然后在0041160F函数设置一个断点:
image.png

F7进入到函数内部,堆栈初始化之后正常:
image.png

往下走,来到触发出发的地方:
image.png

通过之前的分析可以知道,这里是会将ESI地址所指的值赋值给EDI所指的值,而EDI所指的值后面就是EBP的地址,如果没有对ESI所指的值长度进行限制,这里拷贝过去之后就会覆盖掉EBP的地址

EDI的地址为0018F1A8,目前内容如下
image.png

执行语句后EDI内容如下:
image.png

这里的0018F1A8一看就是shellcode
我们跳转过来看一下,代码如下:
image.png

代码解密出来之后,会尝试通过 URLDownloadToFileA.函数从短网址http://bit.ly/33fuZgy 下载文件到本地并通过WinExec执行
短网址解析出来为:http://gessuae.ae/wp-includes/fonts/lav.jpg
image.png

保存路径为:%LOCALAPPDATA%\X098765432198.exe
image.png

通过WinExec执行下载的文件
image.png

执行之后调用ExitProcess退出EQNEDT32.exe
image.png

后续payload

在vt上查找下载地址:http://gessuae.ae/wp-includes/fonts/lav.jpg
image.png

成功找到名为lav.jpg的攻击文件,且这里可以看到该文件是.net平台的
image.png

下载到本地查壳可以发现,样本由C#编写
image.png

该样本结构如下:
image.png

通过对main函数的分析可以得知:

  1. 程序首先会尝试通过 Assembly.GetExecutingAssembly().GetManifestResourceStream函数获取名为compressed的资源
  2. 成功获取之后会复制资源到arrany
  3. 将arrany传入到Decompress函数
  4. 创建新线程,start参数为经过Decompress函数处理过的array

image.png

我们查看一下Decompress函数的内容可以发现该函数是一个解密函数:
image.png

在调用Decompress函数处设置断点:
image.png

查看一下当前array,是一个大的字节流
image.png

F10单步往下走,得到返回值
image.png

展开返回值,很明显解密出来的内容是个PE文件
image.png

此时可以选中该变量,然后鼠标右键->在内存窗口中显示:
image.png

内存如下:
image.png

保存为dump.exe,可以看到该文件依旧是由C#编写:
image.png

dump.exe的入口如下:
image.png

可以看到这里dump.exe是经过混淆的,尝试使用de4dot去混淆:
去混淆之后函数结构如下,比之前好了一点点,然后尝试在main函数设置断点:
image.png

提示异常
image.png
这里其实不应该直接在main函数设置断点的,我偷了个懒,尝试直接在main函数设置断点
但其实在main函数之前,可能还会运行其他的内容,比如 public static 属性的变量赋值,比如下面的代码:
image.png

这里可以看到,main函数中只有一个输出语句
在最后声明了一个static的变量str1,但是并未引用过str1
但是运行代码之后可以发现,由于static变量str1的声明,会导致str1的赋值会在main函数之前执行,这是由C#的编译顺序决定的。

所以在该样本中,应该也是有类似的操作,导致代码还没有跑到main函数就抛出异常了。
终于在4970处找到了异常的原因
image.png

并且在一个长语句中,代码多次调用了smethod_0函数:
这里可以看到smethod_0函数是ECB模式AES的解密函数,参数1是base64编码的字符串,参数2是AES解密所使用到的key
image.png

现在在4970行地方设置断点,代码成功断下来:
image.png

往后跟两步之后定位到了异常原因,原来是因为我这个虚拟机卸载了网卡,所以在GetExternalIP的时候导致异常
image.png

尝试通过DownloadString函数访问”http://ifconfig.me/ip
这里应该是通过该地址获取本机出口IP地址。

这里直接将DowinloadString方法替换试试:
image.png

过了之后直接F10运行,组装出来变量如下:
“+——————- Client INFO ——————-+\r\nIP: 192.168.1.1\r\nHWID: 0F8BXXXXXX0906EA\r\nOwner Name: WIN-IHXXXXXXIMB\r\nFull OS Name: Microsoft Windows 7 家庭普通版 \r\nOS Platform: Win32NTOS Version: 6.1.7601.65536\r\nSystem Boot Mode: Normal\r\nPhysical Memory: 2.80 GB Available Of 4.09 GB \r\nVirtual Memory: 1.90 GB Available Of 2.04 GB \r\nDate: 2019/12/27 12:01:46\r\n————————————————————-“

可以看到,样本首先会获取的信息有:

  1. 计算机出网IP
  2. 计算机HWID
  3. OwnerName
  4. Full OS Name
  5. OS Platform
  6. OS version
  7. System Boot Mode
  8. Physical Memory
  9. Virtual Memory

接着就会运行到Main函数:
image.png

Smethod_14用于获取Chrome浏览器的隐私信息:
image.png

Smethod_16用于获取”C:\Users\Shyt\AppData\Roaming.purple\accounts.xml”的信息
通过查询可以的知,该目录对应程序pidgin
image.png

Smethod_4用于获取@”C:\Users\Shyt\AppData\Local\Vivaldi\User Data\Default\login data”
也就是Vivaldi软件
image.png

smethod_17用于获取FTP凭证信息:
image.png

Smethod_15用于获取Opera浏览器的隐私信息
image.png

Smethod_18用于获取Outlook的隐私信息:
image.png

image.png

Smethod_10用于获取UC浏览器的隐私信息:
image.png

smethod_1获取360浏览器的隐私信息
image.png

Smethod_0用于获取猎豹浏览器的隐私信息:
image.png

获取Firefox凭证信息:
image.png

最后将获取到的信息打包,通过FTP或者SMTP的形式上传
image.png