Linuxword Global
当前位置: 通信协议 > 商业窃密马的前世今生

0x00 前言

在分析非PE的样本中我们可以发现,有三个漏洞是office样本常客,分别是2017-1182、0802、0798

这几个漏洞非常常见,目前也是office样本漏洞利用的大头。包括很多APT现在都还在使用,比如Gorgon、Gamaredon就尝尝使用0798进行模板注入。

当然 还有一个常见的漏洞是2017-0261 EPS漏洞。

在本文中,我们将详细介绍2017-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上的机器打开,弹出了计算器。

t017ed93d5f705cfe69

调试样本

首先直接打开word程序:

t01e2a8fbb62f89eb14

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

t010aa2ba770ece175a

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

t0184219a23a60943bf

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

t013d8beaf7fd44069a

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

t019c12b50e5eb22eb5

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

t01894fbb621535d6e8

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

t0192d8a20ab3024d32

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

t01cb3f9aee1c35e3d9

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

t01d7a9f8ff6d1a775b

所以我们向上看看,ebp是在哪儿被破坏的。

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

t01d31369e1b762d3a1

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

t0159b529f6c3a5eb55

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

ESI:

t0122a5a5f4977cce20

EDI

t012f1294778d82e034

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

t01113673b677065687

rep movsd dword ptr es:[edi], dword ptr [esi]执行完之后,EDI被成功赋值,可以看到,此时ebp所在的值已经从0018F214变成了41414141

ebp后面的返回地址已经从004115d8更改为了00430c12

t01946907dd4e1e92b0

查看一下00430C12地址:

t0162e8e50016031ede

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

t01bfb414151b94e6d0

所以成功弹出计算器。

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

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

 

0x02 实战样本分析

样本来源于https://app.any.run/

样本MD5:8c166d55a69dda96f56596a783100842

样本下载链接:https://app.any.run/tasks/36dbd94f-f87c-4226-bb6f-cbe8b0e2f73b/

根据VT我们可以知道,该样本属于CVE-2017-11882的利用样本。

t01e8d03d269104ebd3

将样本下载本地并解压。

第一,我们可以在断网的虚拟机中直接尝试运行该样本,看看%appdata% %application% %temp%这几个目录下是否释放了对应的文件。

首先通过火绒剑分别监视WINWORD.exe和EQNEDT32.exe的行为。

t0198db35a899770b03

这里EQNEDT32.exe开始的地方应该就是漏洞开始的行为。

t01661ae324e4902403

之前讲过,11882的漏洞触发一般有两种情况。 一种是在本地Drop并加载一个文件,还有一种是去指定的地址下载文件到本地执行。

t012c05fd41a1dc7ecd

在火绒剑中往下看,很容易就能看到这样的行为。

这里很明显是程序conect 194.180.224.87:80

然后后面http 请求了 abass.ir/kellyx/kellyx.exe

后面这个应该是个下载链接。下载的文件默认情况下的话会保存为kellyx.exe

直接访问这个地址,也是可以下载文件回来的:

t01d192e3c69e4e84eb

因为我用的是家里的网络,我就直接访问了。如果是用公司的网络,请勿直接这样访问恶意地址。

我们首先查询一下这里conect 的194.180.224.87

t014128012c5be0174e

根据奇安信的TI,初步确认这里是个恶意地址。

然后我们查询下后面请求的那个域名:

t01447a4004510a36b0

后面这个域名已经被打上了对应的标签,且看起来,这个域名并不像是属于某个家族,更像是一个恶意软件公共使用的一个地址,就类似于开发使用的github?

于是回到火绒剑中,再往下翻翻其实就可以发现,样本已经成功下载回来了。并且保存的路径的确就是我们之前猜测的%appdata%,保存的名称为:kellye358348.exe

t011389213cc6aa0c6a

我们再往下看看,看是否还有其他的行为。

EQNEDT32.exe的行为到这里就分析完成了,根据火绒剑的监控来看,这个11882的漏洞利用文档,执行完之后其实就是会尝试从目标地址去下载对应的文件到本地并加载执行。

转到%appdata%目录下,可以看到文件的确已经被成功下载回来了。

t01ee1f5df381e83803

按到正常的分析流程,我们这里可以直接分析这个exe了,但是为了调试cve-2017-11882漏洞,所以我们还是调试下这个样本,看看shellcode是如何执行的。

 

0x03 漏洞调试

经过上面的分析,我们已经知道,2017-11882漏洞的触发点应该在0041160F函数中的00411658这一行。

于是我们可以还是像上面的方式附加QENDT32.exe到调试器然后在0041160F函数入口点设置断点。

成功命中断点:

t017bdc83f493da5c86

我们运行到00411658这一行代码处

分别查看一下ESI 、EDI、以及EBP的值

t015fb61507a3abbd67

EDI的值如下:

t01b7cb6bf61930cfaa

EBP的值如下:

t01dbd393d5d52afbfa

EBP的值这里可以看到0018F214

我们注意到,这个地址其实就在EDI后面

t010917c9aeabcb6463

然后我们继续F8继续往下走,此时记得注意EDI的变化

t01676531186a6c89a5

这里运行下来之后,ESI的值成功赋值给了EDI,且我们可以看到,刚好ebp的值就被覆盖了,这里被覆盖为了2F E0 FF BF

然后我们直接在该函数最末尾的地F4,执行完这个函数,然后返回回来。

往下走几步可以看到如下的语句:

t01497b294fa1f94a39

这里jmp eax 就是跳转到shellcode执行,这里就是关键call

t0156e4fbb88bfb90dd

多调试几次 顺利找到了下载的操作

t0131d16258d1218402

F7进入函数,可疑看到具体执行的代码如下:

t0145b617ac8f0a84e8

这里call eax 由于电脑已经断网了,这里不能正常请求,会抛出异常。

我恢复快照,然后再重新设置网络,调试一下试试。

t011df227b238311c29

样本成功下载之后,进来,可以看到这里直接就尝试CreateProcess 的方式加载执行这个EXE。

t012cf0f3763da55cf9

 

0x04 下载样本分析

样本基本信息

通过CFF 查看样本可以发现,该样本是.NET平台的样本

t01241457f76df992bf

然后我突然发现,这里File Size 和PE Size不同,我突然意识到之前我们看到的那个文件是没有下载完的。所以恢复快照,跳转过来,继续下载,这次下载完成之后,显示1111kb,应该正常了。

t01e08a1fa0fdd4f653

重新加载该文件,可以看到这次正常了。

t01fe41d1d158208e91

我们顺便查看一下资源:

t010c7b3df48fb4e22f

这里有9个资源,等会可能会加载其中的资源。

寻找样本真正入口点

使用dnspy加载这个样本,如下:

t010566bb1aac64f957

入口点函数代码如下:

t01f7ae0c1cc82f22cf

dnpy左边,程序对应了几个这样的namespace:

t0173ed59ebafee8a70

程序分别有三个namespace 分别是

beerparlourbillingsystem
beerparlourbillingsystem.My
beerparlourbillingsystem.My.Resources

这个beerparlourbillingsystem
拆开就是
beer parlour billing system

直译过来就是:啤酒厅计费系统
感觉像是一个正常的程序

先google一波beerparlourbillingsystem:

t0116d1e4caae130f86

这玩意儿居然还真是正常的一个管理程序。

不过源码好像是使用VB写的。

我们下载个源码回来看看。

在vs中打开源码,然后对比一下dnspy中的结构,发现有两个类是原本的程序中没有的

分别是HighScoreSave和TetrisDrop

t01fa269de59956c2d3

其中HighScoreSave如下:

t01c92ecc5bc7f1f390

TetrisDrop如下:

t0197c9888b2bde503d

其中HighScoreSave中的tr函数有点像是一个自定义的解密函数:

t0161d020084ce72f3a

FindForm中好像有操作资源的代码。

t01accc0f2bb03bae55

阅读HighScoreSave的代码,我们可以发现,这里应该是从资源读取数据,并且调用下面的tr函数进行解密。

现在第一个问题解决了,我们可以直接在tr函数中设置断点,应该就可以直接F5跑过来了。

但是我还是好奇,程序到底是怎么过来的。

于是我还是右键转到入口点,继续看入口点的代码。
程序的入口点在beerparlourbillingsystem.My类中

t0160d167590d07238e

此时我注意到,在当前类的最下面有这样一个方法:

t01fb01101c36371d22

这里base.MainForm = MyProject.Forms.Splashscreen;

将当前的MainForm替换为了MyProject类下面的Splashscreen

所以在上面的MyProject.Application.Run(Args); 启动的实际上不是当前的Form

而是启动的Splashscreen

于是点过来看看Splashscreen:

t019d1b211a3cb95f75

这里通过this.m_Splashscreen的方式反射加载。

我们继续跟进,看看this.m_Splashscreen

t01aed2f4a2fa883468

这里的Splashcreen看起来很正常。

但是这里的Splashcreen是beerparlourbillingsystem类的,我们当前类(入口点所在的类)是beerparlourbillingsystem.My

我点击Splashcreen,进入到beerparlourbillingsystem的Splashcreen:

t01d45af282317a3232

这里首先是Splashcreen的实例化方法Splashcreen():

t011b84ca9cd58e2dd6

Splashcreen()中分别调用了Splashscreen_Load:

t01c7099f96025fc394

和InitializeComponent:

t01cfb07846387c309a

这里Splashscreen_Load方法看起来非常正常,就是设置窗口,并没有其他操作。

但是这里的InitializeComponent,第一行就是:
HighScoreSave highScoreSave = new HighScoreSave();
这里一开始就是实例化了HighScoreSave类。
HighScoreSave就是我们先前看到的,可能有问题的那个类。

然后我们点击过来,可以看到在HighScoreSave类的实例方法中,调用了下面的FindForm方法。

t01742c2036a5c77264

此时我们直接在this.FindForm这一行设置断点,然后F5跑过来就可以了。

成功命中:

t01675a823be8667f0c

然后F11进入到FindForm函数,然后F10单步往下走,就不用进入下面的tr解密函数。

单步执行完成之后,下面的局部变量窗口将会出现tr函数的返回值。

t015cfe21e30390654a

我们展开查看一下这个返回值:

t019f6d878b9943a714

果然是4D5A:

t010ba6484301c0bd72

在这行变量上右键然后保存,存储下这个解密出来的PE并命名为dump_file1

t018e3749f351172b75

接着往下看,程序从资源解密出PE之后,尝试对TetrisDrop.Ng赋值。
第一个赋值语句是:
TetrisDrop.Ng[1] = TetrisDrop.He;

这里He点进去如下:

t01e57edbc0e39092c9

然后赋值Ng[2]
TetrisDrop.Ng[2] = “beerparlourbillingsystem”;

最后将TetrisDrop.Ng作为参数和type一起传递到Activator.CreateInstance函数中。完成dll的调用加载。

t019b3136bb249b342e

这里的Ng赋值为了rxcwON和grI

小结

在这个样本的分析中,可以看到攻击者是将少量的恶意代码插入到了正常的程序中。并且从读取的资源我们可以发现,资源名应该也是攻击者精心构造的。这种方式可以让人一眼看上去,感觉这个程序并没什么毛病。而且程序通过了反射实例化和不同namespace下的同名类名的情况进行加载。算是一个比较隐蔽的加载方式。

 

0x05 dump_file1

dump出来的这个文件是一个带有轻微混淆的C#dll文件,这里的混淆几乎可以忽略不计,所以直接分析即可。

t01ffc5fce4e1712723

过来首先是GodofBeauty类的实例方法:
GODofBeauty.Roman(ugz1, ugz3, projname);

t01f40d3ee9fd44bf3f

这里进来首先调用了Roman函数,Roman函数的具体实现在下面43行的位置。

来到Roman的具体实现:

t01d10540cbb6ac5868

程序首先是生成一个46000到59000的随机数用于sleep,也就是程序会休眠46秒到59秒。这一步应该是为了反沙箱。因为沙箱一般都会有时间限制,slepp是一个比较简单但是有效的反沙箱机制。

休眠之后,程序调用了当前类GODofBeauty的Capitoline方法

t0108ac8bf2c617d337

Capitoline方法看起来好像又是一个操作资源的函数:

t0107a8b9c7578ade6a

资源读取成功之后,会赋值给ughHbnBnaWtlYkx
Bitmap ughHbnBnaWtlYkx = GODofBeauty.Capitoline(ugz1, projname);

然后ughHbnBnaWtlYkx会作为参数传递到Vulcan中:

t0126316d8dfedf8692

而Vulcan执行完成的返回值,会作为参数传递到下面的Greek函数中。
Greek函数也是一个解密函数:

t01627b6933667c2723

所以这里就是读取一个资源,然后解密两次。赋值给array

然后array会作为Assembly实例化的参数:

t014702b38df66feb58

然后assembly经过转换,通过methdInfo.Invoke的方法进行调用。

然后这个程序看起来就执行完毕了。我们直接来调试一下这个dll。

首先我们在
Activator.CreateInstance(type, TetrisDrop.Ng);
这行代码处 F11进入系统空间

t018e41b897de2e5825

继续F11

t01e3408c0679c14b9c

然后F10往下走,当某一行执行完之后,程序跑飞了,就在这一行设置断点,然后重新跑过来。然后F11进去

t0177f5869ee9bb7473

进来之后继续刚才的方法,找到.Invoke或者CreateInstance函数,F11

t0122b9ebc252c454e3

进来之后继续重复刚才的操作,找到InvokeMethod方法
然后F11即可进入到dll空间:

t015da03574d6aea949

然后就是正常的调试流程。

之前已经静态分析过,Capitoline函数用于读取资源,Greek和Vulcan函数用于解密。
这里读取的资源值就是”beerparlourbillingsystem.Resources”

t01fd1a21f5020a0d32

直接F10略过中间解密资源的操作,这里可以看到资源已经成功解密

t0145c5b07e34f70e27

解密的资源又是一个PE,我们保存为dump_file2

t014b0678fb97342dc4

dump出来的这个文件等下会通过Invoke加载,我们先静态分析一下dumpfile2

t013ca4e9c30de6c0e6

 

0x06 dump_file2分析

dump_file2的程序名为Jupiter.dll

t01f5d49b6ac2bb0632

这个样本的混淆就比较严重了。

但是大概看一下,可以发现这就是普通的混淆,直接用de4dot应该就可以去混淆。

去混淆之后大概结构如下:

t0120747c3cbef6decd

先来分析一下这个都是什么。

经过分析,这个dll很明显是一个商业马,有很熟悉的窃取用户信息的操作以及后面的沙箱检测操作。

t01ef8ef6fa4cfaa4a4

主要功能应该在这个去混淆之后名为Class3的类中。

t01703a088d33eb0bab

这里Class3后面有个Token是 0x02000005,这是C#的一种结构,我们是可以点击注释信息中的TOKEN的:

t01cfedfe9dc4619ff4

点击过来可以查看到dnspy解析出来的一些结构信息

t01a237dd00c10475a4

根据这个结构,我们可以顺利的找到去混淆之前的Class3类,这个类的token应该也是0x02000005

t01c6a7fd5b666707a1

然后我们继续看解混淆之后的代码。发现在Class2中又有读取资源和解密的操作
代码和dump_file1中的操作方式很像,但是不同的是,这里读取的是当前文件的资源,而dump_file1中读取的是源文件的资源。

t01cca8b746f6eacb5e

Jupiter.dll的资源列表如下

t01a76daf7d16d82111

这意味着,这里其实还有可能不是真正的核心马,还只是一个loader。

分别分析一下这几个函数,找找调用位置。

t0168519305b92eb567

跳转过来发现,程序还是首先通过smethod读取资源,然后分别通过smethod2和smethod1进行解密。

解密之后会将数据传递给byte_0。

t0117c3bf71cb02db78

查看一下byte_0的调用如下:

t01de46309396a2359a

byte_0又会传递过来给Assembly,然后通过Invoke,和上一层的loader几乎一模一样。

所以我们现在知道了,我可以直接在这里设置断点拿到byte_0的数据,也可以在smethod1函数return的地方设置断点,拿到最后的这个文件。

所以我们现在需要做的是:去混淆的文件中找到smethod_11方法或者找到smethod1的return,然后在我们正在调试的样本中找到对应的位置设置断点。

我们注意到,smethod11是在class3中的,而class3是一个大类,有很多的函数。所以我们要想从混淆的代码中找到smethod11是非常困难的。

t01e7616d29ae42c969

smethod0 在Class2中,该类只有三个方法,相对来说会容易很多

t01cbcaaf3d683f44e0

所以可以尝试去Class2中找一下对应的断点位置。

Class2的Token是02000004,我们去找到这个token的类

t01ac65be9a187a8deb

类中最上面的这个位置,应该对应的就是smethod0了

t012af47abc5b3f42af

然后下面还有两个函数,我们就分别尝试在这几个return都去设置断点。

所以现在,我们需要回到调试窗口中,从dump_file1跳转到dump_file2继续执行。

还是跟我们从原始样本进入到dump_file1的方法一样,先F11进Invoke函数
然后一步一步进来 最后停留在了dump_file2的Class3中

t0179994aa6a5e0e5ee

然后我们分别去找到对应的地方,设置好断点:

t017c6ba488cd87b681

t01a996523d5521ec32

t014b0d34ade8453b4e

然后F5跑起来。这个时候会发现,程序并没有命中我们的断点,而是直接运行结束了。

t01ded759ebb829fdfc

所以猜想是不是程序进来的时候,有地方实例化了这个类,调用了实例方法。简单来说就是这几个函数已经在我们调试过来之前执行完了。

所以这个时候 直接调试原样本,直接F5 不要中断,应该会跑过来的。
(中间有一个sleep,如果之前没patch的话,估计要等1分钟左右

成功命中第一个断点:

t018a694314fba15942

F10返回回来,此时这个资源还是加密状态:

t01d51d54f1129b61c6

然后继续运行,命中第二个断点:

t01acb4a38a8ad0104f

这里可以看到,第一次解密已经完成。

然后继续运行,命中第三个断点,此时PE已经解密出来。

t0142b39f0264743a35

dump这个PE保存为dump_file3
这个dump_file3是一个exe,我们可以直接调试了。虽然这个也有轻微的混淆,但是这里的混淆还能接受,可以继续分析。

t01e5a1c851eb447bd8

 

0x07 最终木马概述

最终的这个木马就是一个功能完整的商业窃密马了。这里就不详细分析其功能,概要分析一下。

程序首先会调用nq函数,这个函数其实并没有多大意义

t015282d4e5125f47e0

然后程序通过两个永真循环,然后以switch case的方式去执行某些特定的功能:

t016f552e714a1ecbef

后面会设置一个名为uge的计时器。

t010befb0cb68702e8b

然后随便跑一下行为,抓个包。

t013f80db046a96f7b0

这里可以看到程序在跟us2.smtp.mailhostbox.com三次握手建立连接,目标IP为:208.91.199.223
目标端口是587

t01094b47f9905ef334

然后成功拿到目标服务器的账号密码:

t01dedb782b2571c117

然后尝试登陆邮箱:

t0184288f72a9fa6c84

这里账号密码都是经过BASE64编码的,我们直接解码然后尝试登陆就行。
成功登陆,获取到邮件信息

t0110a8176e1602475a

通过查看邮件信息,我们可以确认该马是个窃密马,且不带屏幕截图功能。
该马的主要目的是窃取用户主机上存储的密码信息。
代码结构不像是AgentTesla,但是风格却很相似。
根据情况来看,这个马不是AgentTesla就是HawkEye。极大极大的概率是AgentTesla

「梦想一旦被付诸行动,就会变得神圣,如果觉得我的文章对您有用,请帮助本站成长」

赞(0) 打赏
一分也是爱

支付宝扫一扫打赏

微信扫一扫打赏

标签:

上一篇:

下一篇:

相关推荐

0 条评论关于"商业窃密马的前世今生"

最新评论

    暂无留言哦~~

博客简介

本站CDN采用VmShell免费提供离中国大陆最近的香港CMI高速网络做支撑,ToToTel打造全球最快速的边沿网络支撑服务,具体详情请见 :https://vmshell.com/ 以及 https://tototel.com/,网站所有的文件和内容禁止大陆网站搬迁复制,谢谢,VPS营销投稿邮箱: admin@linuxxword.com,我们免费帮大家发布,不收取任何费用,请提供完整测试文稿!

精彩评论

友情链接

他们同样是一群网虫,却不是每天泡在网上游走在淘宝和网游之间、刷着本来就快要透支的信用卡。他们或许没有踏出国门一步,但同学却不局限在一国一校,而是遍及全球!申请交换友链

站点统计

  • 文章总数: 2341 篇
  • 草稿数目: 13 篇
  • 分类数目: 6 个
  • 独立页面: 0 个
  • 评论总数: 2 条
  • 链接总数: 0 个
  • 标签总数: 6116 个
  • 注册用户: 139 人
  • 访问总量: 8,657,414 次
  • 最近更新: 2024年4月28日