不在沉默中爆发就在沉默中死亡,处于沉寂状态的 Emotet 僵尸网络是怎样卷土重来的?(二)

xiaohui 恶意软件 2019年11月21日发布
Favorite收藏

导语:本文中,研究人员会举一个Emotet示例的二进制进行分析,探索恶意软件是如何工作的。

在上一节中,研究人员已经研究了Emotet通过运行Microsoft Word文档中混淆VBA宏来删除有效载荷的能力。

本文中,研究人员会举一个Emotet示例的二进制进行分析,探索恶意软件是如何工作的。其中研究人员演示了如何对主要有效载荷进行解压缩,并在调试器中解压缩后遵循其执行进程。 Emotet的执行包括多个阶段的混淆可执行文档,这些可执行文档已加载到内存中,并且在不同的内存区域都具有复杂的代码流。这些设计使分析人员,静态分析引擎和反汇编程序更难以分析木马。

另外,研究人员还描述了如何在Emotet的解压代码中检查注册表项的存在。如果无法访问注册表项,则有效载荷不会解压并运行,研究人员发现阻止对密钥的读取访问是防止Emotet有效载荷运行的有效方法。其中研究人员还会讨论Emotet如何充当其他恶意软件家族传播器,以及这如何表明其运营商已采用了“恶意软件即服务”业务模型。在上一篇文章中,研究人员对名为15.exe的Emotet可执行文档的分析并最终删除了另一个银行木马Trickbot。在安装了Bromium的计算机上,Emotet可以在被隔离的微型虚拟机中运行,而不会损害系统的完整性。由于恶意软件是动态运行的,因此它会产生危害指标(IOC),安全团队可以使用这些指标来防御网络的相同攻击。

EMOTET的封装

封装程序的主要目的是压缩和加密便携式可执行(PE)文档。加密的有效载荷在运行时被解压缩,然后解压缩代码将执行传递给新解压缩的代码。对于恶意软件的开发者来说,封装程序通过使对二进制文档的静态分析更加困难来帮助逃避检测。 Emotet的封装程序是变化的,这使得基于签名的检测工具根据封装程序的足迹对样本进行分析变得更加棘手。

文档名:15.exe

大小:428808字节

MD5:322F9CA84DFA866CB719B7AECC249905

SHA1:147DDEB14BFCC1FF2EE7EF6470CA9A720E61AEAA

SHA256:AF2F82ADF716209CD5BA1C98D0DCD2D9A171BB0963648BD8BD962EDB52761241

该PE文档,其资源(.rsrc)部分占文档总大小的很大一部分(51%),这表明该恶意软件可能已经被封装。

1.png

资源部分消耗了二进制文档大小的一半以上

查看资源部分会发现两个异常资源,调用EXCEPT和CALIBRATE。 EXCEPT的文件量表明这可能是加密的有效载荷。转储资源将确认它包含加密的数据。在一些示例中,研究人员发现.data部分中的解密PE文档被删除了。

2.png

异常资源调用EXCEPT和CALIBRATE

3.png

EXCEPT中的加密数据

Emotet是模块化的,并使用诸如沙箱检测之类的反分析技术来逃避分析。一个解压后的Emotet二进制文档包含数百个函数,当在诸如Ghidra的反汇编器中打开可疑的封装样品时,只有少数函数可以识别。以下是二进制文档已封装的另一个迹象。

4.png

Ghidra在封装的Emotet示例中识别的函数列表

注册表检查

在分析封装程序代码期间,研究人员注意到一个函数,该函数生成一个char数组并具有条件while(true)无限循环。这一发现使研究人员感到好奇,是否可以触发无限循环来停止执行解压缩代码,从而阻止主要Emotet有效载荷运行。该函数通过调用RegOpenKeyA读取Windows注册表项来工作。如果找不到密钥,则恶意软件会进入无限循环(图5)。

5.png

检查注册表中是否存在 “interface\{aa5b6a80-b834-11d0-932f-00a0c90dcaa9}”函数

函数FUN_00401a90解码值为 “interface\{aa5b6a80-b834-11d0-932f-00a0c90dcaa9}”的字符串,该字符串作为参数传递给RegOpenKeyA。要使Windows脚本引擎接口IActiveScriptParseProcedure32起作用,此注册表项是必需的。具体地说,接口解析给定的代码进程并将该进程添加到名称空间。

6.png

RegOpenKeyA参数

研究人员回顾了Emotet的其他示例以获取类似函数,有趣的是,在执行时,所有示例要么退出主线程,要么在缺少注册表项的情况下进入无限循环。

文档名:891.exe

MD5:BD3B9E60EA96C2A0F7838E1362BBF266

SHA1:62C1BEFA98D925C7D65F8DC89504B7FBB82A6FE3

SHA256:28E3736F37222E7FBC4CDE3E0CC31F88E3BFC16CC5C889B326A2F74F46E415AC

7.png

在没有注册表项的情况下主线程陷入无限循环

文档名:448.exe

MD5:193643AB7C0B289F5DE3963E4ADC1563

SHA1:B14290BFAE015D37EBA7EDD8F5067AD5E238CC68

SHA256:FD9E5C47F9AEB47F5E720D42DD4B8AD231EE3BA5270E3FBD126FC8C6F399D243

8.png

在没有注册表项的情况下退出主线程

使用调试器进行二进制分析

为了证实研究人员对15.exe是一个压缩的Emotet二进制文件的怀疑,他们在x64dbg中将其打开并检查其映射的内存区域。在15.exe的可选标头中,禁用了地址空间布局随机化(ASLR),这意味着,如果可能,该模块将以其首选的基地址0x00400000加载到内存中。

第一阶段

在15.exe中导入的函数之一是VirtualAllocEx,此函数用于在远程进程中分配内存,恶意软件通常将其用于进程注入。研究人员将从在VirtualAllocEx的返回地址上放置一个断点。

9.png

15.exe的内存映射部分

如果运行到断点,研究人员将会看到Emotet在0x00220000处创建了内存分配。然后,它将代码存根从映射映像的.data部分中的0x00422200(文档偏移为0x0001FE00)复制到新分配的存储空间,并对其进行控制。

10.png

0x00220000处的内存分配

然后,Emotet对复制到0x00220000的代码中的API和DLL名称进行混淆处理。

11.png

对LoadLibraryExA和kernel32.dll进行混淆处理

12.png

对VirtualAlloc进行混淆处理

然后,它从kernel32.dll调用GetProcAddress以获取已解码API名称的地址。

13.png

从代码存根0x00220000调用GetProcAddress API,从内核32.dll的映射映像中检索导出API的地址

首先,以这种方式检索LoadLibraryExA的地址。然后,它使用该地址将kernel32.dll加载到地址空间为0x766D0000的位置。然后,它使用加载的模块kernel32.dll的句柄在下面的函数列表中调用GetProcAddress:

LoadLibraryExA
GetProcAddress
VirtualAlloc
SetFilePointer
LstrlenA
LstrcatA
VirtualProtect
UnmapViewOfFile
GetModuleHandleA
WriteFile
CloseHandle
VirtualFree
GetTempPathA
CreateFileA

14.png

调用GetProcAddress以获取LoadLibraryExA的地址

15.png

调用LoadLibraryExA将kernel32.dll加载到内存中

16.png

去混淆后的API名称

要注意的有趣事情之一是Emotet调用了名为 “mknjht34tfserdgfwGetProcAddress”的无效API的GetProcAddress。由于这是无效的,因此该函数返回一个空值,错误代码为0000007F(ERROR_PROC_NOT_FOUND)。在所有Emotet示例中,研究人员检查了对该无效函数名称的调用GetProcAddress。

17.png

为无效的API调用GetProcAddress

18.png

调用GetProcAddress以获取GetProcAddress的地址

19.png

堆栈中保存的API的函数地址

一旦代码存根检索到函数地址,就会调用VirtualAlloc分配另一个内存区域,在该区域中,它将从15.exe的.data部分(而不是.rsrc部分)写入解密的PE文档。

20.png

地址0x00240000处的内存分配

21.png

存根将PE文档写入地址0x00240000

从0x00240000转储的Emotet二进制文档:

文档名:emotet_dumped_240000.exe

MD5:D623BD93618B6BCA25AB259DE21E8E12

SHA1:BBE1BFC57E8279ADDF2183F8E29B90CFA6DD88B4

SHA256:01F86613FD39E5A3EDCF49B101154020A7A3382758F36D875B12A94294FBF0EA

转储可执行文档并进行检查后,发现它是另一个包含主要有效载荷的压缩Emotet二进制文档。研究人员已经在一些Emotet示例中看到,第一个映射的解密可执行文档从内存中转储后无法直接运行,但是该示例可以运行。

Pestudio识别了有关此文档的几个可疑特征,包括没有导入的文档,检测到封装程序签名“Stranik 1.3 Modula/C/Pascal”,并且该文件可能包含另一个文件。

22.png

由pestudio识别的有关emotet_dumped_240000.exe的可疑攻击标识

23.png

emotet_dumped_240000.exe的Bromium控制器进程交互图,它会自行启动并创建一个称为“ipropmini”的服务,该服务与15.exe所示的行为非常匹配

24.png

为emotet_dumped_240000.exe检测到的高严重性事件的Bromium控制器视图

第二阶段

在写入和解密0x00240000处的可执行文档后,代码存根使用VirtualAllocEx在地址0x00260000处分配另一个内存区域。分配内存后,它将从内存区域0x00240000读取有效载荷,并将其写入0x00260000。

25.png

调用VirtualAllocEx在0x00260000处分配内存

26.png

存根在0x00260000处写入主要Emotet有效载荷

在0x00260000处写入主要Emotet有效载荷后,代码存根将挂钩和JMP指令插入代码中(图28)。 Emotet这样做是为了使代码分析更加困难,并使反汇编程序代码更混乱。

挂钩放置完成后,有效载荷将取决于要运行的另一个内存区域,这意味着即使在修复了PE文件部分的对齐和原始偏移之后,也不允许将其转储到磁盘。

27.png

从虚拟地址0x00260000转储的PE文档的执行错误

28.png

代码存根对位于0x00260000的可执行文档的修改

第三阶段

在0x00260000处修改了有效载荷并准备就绪后,存根将调用UnmapViewOfFile以从0x00400000(这是第一个Emotet映像加载到的内存区域)中取消映射15.exe。然后,它在0x00400000处分配一个新的内存区域,该区域与有效载荷在0x00260000(15000字节)处的大小相同。分配新的内存区域后,它将修改后的有效内容复制到0x00400000。这是一种进程注入技术,其中恶意软件修改其内存中的二进制文档,然后进行自我覆盖。

29.png

30.png

映像15.exe未映射后的内存映射视图

31.png

新分配的内存为0x00400000

32.png

分配到0x00400000后的内存视图

33.png

将载荷从0x00260000复制到0x00400000

第四阶段

将有效载荷复制到0x00400000后,Emotet解析API名称,然后将执行流转移到有效载荷。在本例中,它会转移到0x0040C730,然后调用一个函数来解析与API名称相对应的哈希。

Emotet的主要有效载荷使分析人员难以遵循代码流,因为了解恶意软件函数的字符串可能被混淆了。

34.png

将API哈希表传递给反混淆函数以进行名称解析

35.png

解析来自ntdll.dll和kernel32.dll的API名称

在API名称解析之后,将调用GetCurrentProcessId以获取Emotet正在运行的进程的进程ID(PID)。然后,Emotet遍历所有正在运行的进程以查找其模块名称和父PID。一旦找到其父PID,它就创建两个PEM%X格式的互斥锁。其中一个互斥锁使用父进程ID(PEM [PPID])创建,另一个使用其自己的PID(PEM [PID])。

创建这些互斥锁后,它将调用CreateEventW以使用PEE%X格式创建事件,其中%X是其父PID。如果两个互斥锁都成功创建,它将从同一路径再次启动15.exe。启动子进程后,它将在PEE%X事件上调用WaitForSingleObject。

研究人员在一些Emotet示例中已经看到,它通过命令行开关启动子进程。此命令行开关表示Emotet进程已作为子进程启动,并且必须执行指定的任务。

启动的子进程执行所有相同的操作,直到它评估是否创建上述两个互斥锁为止。这次对互斥锁PEM [PPID]的CreateMutex调用失败,并显示错误“ERROR_ALREADY_EXISTS”。子进程中互斥锁创建失败后,它将信号PEE [PPID]发送给父进程15.exe。父进程从等待状态退出,然后终止。

36.png

控制流程图,显示了基于对CreateMutex和CreateEvent的调用启动子进程的决策

337.png

Emotet子进程15.exe(1352或0x548)和父PID(3520或0xDC0)的PID

38.png

对互斥锁名称PEMDC0的CreateMutex调用,其中0xDC0是父PID

39.png

对互斥锁名称PEM548的CreateMutex调用,其中0x548是Emotet进程15.exe的PID

40.png

对事件对象名称PEE548的CreateEventW调用,其中0x548是Emotet进程15.exe的PID

然后,启动的子进程将创建一个名为“ipropmini”的服务,并建立命令和控制(C2)通信。

总结

41.jpg

解析载荷步骤的过程

1.删除的Emotet二进制文档(15.exe)分配具有执行许可权的新内存区域,并在其中写入代码存根(图41,内存区域1)。

2.存根从映像的.data部分解密嵌入的PE文档,并将其写入新的内存区域(图41,内存区域2)。

3.写入内存区域2的文档是有效的PE文档,它是另一个Emotet二进制文档,可以转储并执行而无需修复其重定位。

4.内存区域1中的存根分配了一个具有执行许可的新区域(图31,内存区域3)。

5.存根从内存区域2读取嵌入的有效载荷,并将其写入内存区域3。将有效载荷写入存储区3后,然后通过插入新代码和蹦床对其进行修改。

6.一旦有效载荷在内存区域3中准备就绪,它将取消对15.exe映像的映射。在取消映像映射之后,它使用执行权限分配一个与内存区域3大小相同的新区域,并将7.有效载荷从内存区域3复制到新分配的区域(图41,内存区域4)。

8.然后,存根将执行传递到内存区域4,该区域将启动主Emotet载荷。

9.综上所述,Emotet使用分层方法来解压缩其主要有效载荷。在修改其映射映像后,它执行自注入技术以在内存中执行有效载荷。由于有效载荷永远不会写入文档系统,并且包含挂钩和JMP指令,因此可以轻松地基于动态代码分析来逃避防病毒和检测。

IOC

可以通过阻止对以下注册表项的读取访问来阻止Emotet的运行。由于下载程序将进入无限循环或结束进程,因此它不会损害系统的完整性,因为条件分支会发生在有效载荷执行之前。

32位系统:

HKEY_CLASSES_ROOT\Interface\{AA5B6A80-B834-11D0-932F-00A0C90DCAA9}

64位系统:

HKEY_CLASSES_ROOT\Wow6432Node\Interface\{AA5B6A80-B834-11D0-932F-00A0C90DCAA9}可以使用以下方式检测到Emotet下载程序:

1.从全局可写目录(例如%USERPROFILE%和%TEMP%)启动的进程监控对注册表项“Interface\{aa5b6a80 – b894 – 11d0 – 932f – 00a0c90dca9}”的读取访问。

2.监控对GetProcAddress的函数名称为“mknjht34tfserdgfwGetProcAddress”的API调用;

3.监控对GetProcAddress的API调用的顺序可以用作启发式算法;

本文翻译自:https://www.bromium.com/emotet-analysis-part-3/如若转载,请注明原文地址: https://www.4hou.com/malware/21612.html
点赞 0
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论