查看原文
其他

CVE-2017-11882理论以及实战样本分析

顾何 看雪学院 2021-03-07

本文为看雪论坛精华文章

看雪论坛作者ID:顾何



最近看了一点非PE的样本,发现CVE-2017-11882这个漏洞在实战中的使用非常频繁,有很多在活的APT组织都还在使用,之前都是直接去捕获利用该洞释放的文件,最近有时间,就对11882进行一个完善一点的跟踪。

11882属于Office Equation类型的漏洞,2017年底发现,目前使用较多的有11882,0802,0798三个漏洞,今天先分析一下11882的漏洞原理,以及实战中遇到的两个样本。


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上的机器打开,弹出了计算器。



调试样本



在调试这个样本之前,先以一个简单的例子说明一下程序是如何利用esp和ebp实现函数调用的。

下面是一段很简单的汇编代码,如下图所示,目前eip指向0040108E,指令是call 999.00401005

esp指向0012FF20(当前函数栈顶)

ebp指向0012FF80(当前函数栈底)

 
F7跟进到call里面之后,ebp没变,esp-4变成了0012FF1C。

0012FF1C地址存放了00401093地址,该地址是当前函数的调用完之后应该继续执行的地址。

所以我们大多数情况下可以将call执行理解为push 返回地址 然后jmp 到函数起始地址的两条指令的集合。

 
然后程序通过

push ebp
mov ebp,esp
sub esp,0x44


首先保存之前的ebp,然后把esp的值赋值给ebp,现在esp和ebp就指向同一个地方了。

再通过对esp的sub运算开辟出当前函数使用的新堆栈:



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

mov esp,ebp
pop ebp
retn


的组合指令作为返回。

因为ebp后面就是函数的返回地址,所以这里先通过mov指令,将ebp的值赋值给esp,然后pop ebp,这pop指令执行完之后,esp的值就会+4,esp就会指向之前的返回地址。

再通过ret指令就可以成功返回:


 
好,现在来看看这个样本。

首先直接打开word程序:

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

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

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

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

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

 
返回回来发现是WinExec函数调用的:

 
所以重新按照上面的方法,对WinExec函数下断:

 
此时的00430C12是用户态地址,按道理来讲,ebp应该存放了当前函数的返回地址,但是这里是41414141,很明显ebp已经被破坏了。



所以我们向上看看,ebp是在哪儿被破坏的。
 
由于ebp被淹没,我们这里往上看,找找其他的返回地址:

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

 
往下走几步在00411658的地方发现这样一条指令:

rep movsd dword ptr es:[edi], dword ptr [esi]


该指令执行完之后,ebp就被41414141覆盖,而该指令的功能是将esi的值传送到edi所指的位置。我们分别查看一下esi和edi的值:

ESI:

 
EDI:

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

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

 
查看一下00430C12地址:

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



所以成功弹出计算器。


eqnedt32.exe


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

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

 
F5看一下:



漏洞的原理很简单,漏洞的导致原因是strcpy函数在使用的时候,未对参数的长度进行判断和限制,从而导致栈溢出。

而至于流程到底是怎么过来的,以及文档格式的其他分析之后再单独写一个文章介绍。


实战样本分析


 

原始样本


样本hash:14F28BD8361AE90DBFABCB31767A356B

VT上搜索该样本,可以发现样本已经被打上了11882的标签:

 
我们来调试一下,看看这个到底是不是11882的利用。

winhex打开,该文档的确是rtf:

 
我们通过上面的分析已经知道,11882的漏洞触发点在0041160F函数中,具体的位置应该是00411658。

就还是通过之前的方法附加EQNEDT32.exe,然后在0041160F函数设置一个断点:

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

 
往下走,来到触发漏洞的地方:

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

EDI的地址为0018F1A8,目前内容如下:

 
执行语句后EDI内容如下:

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

 
代码解密出来之后,会尝试通过 URLDownloadToFileA.函数从短网址http://bit.ly/33fuZgy 下载文件到本地并通过WinExec执行。

短网址解析出来为:
http://gessuae.ae/wp-includes/fonts/lav.jpg

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

 
通过WinExec执行下载的文件:

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


后续payload


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

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

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

 
该样本结构如下:

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


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

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

 
查看一下当前array,是一个大的字节流:

 
F10单步往下走,得到返回值:

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

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

 
内存如下:

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

 
dump.exe的入口如下:

 
可以看到这里dump.exe是经过混淆的,尝试使用de4dot去混淆。

去混淆之后函数结构如下,比之前好了一点点,然后尝试在main函数设置断点:

 
提示异常:

 
这里其实不应该直接在main函数设置断点的,我偷了个懒,尝试直接在main函数设置断点。

但其实在main函数之前,可能还会运行其他的内容,比如 public static 属性的变量赋值,比如下面的代码:

 
这里可以看到,main函数中只有一个输出语句。在最后声明了一个static的变量str1,但是并未引用过str1。但是运行代码之后可以发现,由于static变量str1的声明,会导致str1的赋值会在main函数之前执行,这是由C#的编译顺序决定的。
 
所以在该样本中,应该也是有类似的操作,导致代码还没有跑到main函数就抛出异常了。

终于在4970处找到了异常的原因:

 
并且在一个长语句中,代码多次调用了smethod_0函数:

这里可以看到smethod_0函数是ECB模式AES的解密函数,参数1是base64编码的字符串,参数2是AES解密所使用到的key。

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

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

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

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

 
过了之后直接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函数:

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

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

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

 
后面的内容都差不多,就不截图了,样本还会获取的信息有:
  • FTP凭证

  • Opera浏览器隐私信息

  • Outlook隐私信息

  • UC浏览器隐私信息

  • 360浏览器隐私信息

  • 猎豹浏览器隐私信息

  • Firefox隐私信息

 
最后会将收集到的信息打包,通过FTP或者SMTP的形式上传:




- End -






看雪ID:顾何

https://bbs.pediy.com/user-757351.htm 

*本文由看雪论坛  顾何  原创,转载请注明来自看雪社区




推荐文章++++

实战栈溢出漏洞

栈溢出原理和利用

堆栈别乱用

栈溢出漏洞--GS和DEP

CVE-2019-0708 bluekeep 漏洞研究分析详细完整版



好书推荐






公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



“阅读原文”一起来充电吧!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存