针对一个红队病毒样本逆向分析
近日翻到一个比较新颖的样本,在最终后门载荷释放前运用了不少免杀手段,包括堆栈欺骗,实现反射性调用 API,以及 DLL 侧加载、DLL 挖空、HOOK 规避等手法,对其执行流程和部分手法做详细分析记录。
样本概述
初始载荷
初始载荷如下,其中 loader.exe 是作者编译的一个 shellcode 加载器。
执行流程
有合法签名可执行文件(taskhost.exe)侧载恶意 DLL (sbiedll.dll)。这个恶意 DLL 作为中间载荷,解密密文载荷 DAT 文件(sbiedll.dat)获取二阶段有效负载。解密的有效负载 MoonWalk 充当后门,滥用 Google Drive 进行命令和控制(C2) 通信。流程图如下:
精心构造的配置信息
样本主要使用“AES-CFB”算法解密载荷,多次使用 MD5 hash 算法做载荷校验或将 hash 值作为 AES 算法的 KEY、IV 值。主要配置信息如下:
较为丰富的杀软规避
DLL 侧加载执行恶意载荷
指定特定的执行参数,期望参数“--type driver”。
FNV1a hash 动态解析 API。
堆栈欺骗调用 win API,规避 win API 调用过程检测(构造伪造堆栈、修改堆栈指针、调用 API、恢复原始堆栈)
使用了大量的 windows 底层函数(如内核函数 NtCreateFile 等)。
使用当前植入主机的 GUID md5 值作为 AES IV 值,生成唯一、绑定载荷。
hook 规避、断点检查。
MD5 hash 算法和 AES-CFB 算法进行解密,MD5hash 算法进行完整性检查
样本分析
侧加载
“白加黑”侧加载执行初始恶意载荷。由 Sandboxie 签名的合法可执行文件 (taskhost.exe) 侧载恶意 DLL (sbiedll.dll)。
“sbiedll.dll”中初始化了三个导出函数,但实际上都指向相同的地址,函数名称“SbieDll_Hook”
编写一个加载器执行恶意导出函数“SbieDll_Hook”,同步虚拟内存地址,动态、静态结合调试主要功能。
配置信息解密
利用 MD5 校验加密配置信息,确保配置的完整性。计算密文段的 MD5 hash 来于硬编码在样本中的正确 hash 值做比对。
两段密文 hash 值:
AES-CFB 算法进行解密,获取解密配置信息入下,其中包括明文字符“sbiedll.dat”、“--type”以及其他配置信息或密钥供后续使用。
杀软规避
执行进程是否使用正确的参数启动,验证期望参数包含“--type driver”,如果此验证检查失败,则终止进程。计参数的 MD5 哈希值,多次计算结果与硬编码的哈希值进行比较。
硬编码 hash 值:
利用加盐的 FNV-1a 哈希算法来逃避针对 WINAPI 的静态检测。在哈希计算过程中添加盐值不同样本的哈希值不同,提高了逃避静态检测的能力。
上图中伪代码通过指针运算和类型转换访问内存,还原 C 代码,所需 API 地址通过结构体成员访问。
函数体内部关键步骤入下:
初始化哈希值。
对输入字符串(DLL 名或函数名)进行哈希计算。
对盐值进行哈希计算。
首先,它对输入字符串(代表 DLL 或函数名称)进行哈希处理。然后,它单独对盐值进行哈希处理。此两步哈希处理过程相当于对输入字符串和盐的连接进行哈希处理。
盐值,CB 24 B4 BA
最终将 API 函数地址放置与连续的内存地址中,即结构体成员变量中。下图以 API 函数“LookupAccountSidw”为例。
此外 ResolveImport 中还有内置了一些 API 函数调用相关功能,此样本中暂时没用到。
DodgeBox 在完成 API 初始化后,它就会扫描并解除从 System32 目录加载的 DLL。此过程包括遍历 .pdata 每个 DLL 的部分,检索每个函数的起始和结束地址,并计算每个函数字节的 FNV1a 哈希值。计算存储在磁盘上的相同函数字节的相应哈希值。如果两个哈希值不同,则可以检测到潜在的篡改,将用磁盘中的原始版本替换内存中的函数。
此过程包括遍历 .pdata 每个 DLL ,步骤方法入下:
当前线程的线程环境块(TEB)->进程环境块(PEB)->已加载模块的信息链表 InMemoryOrderModuleList。
获取链表信息后再循环遍历加载模块列表的每个节点。
DAT 文件有效载荷解密及加载
在当前进程堆中开辟内存,读取“sbiedll.dat”文件流。
在完成路径等相关字符初始化后,利用 NtReadFile 读取文件流至当前进程的堆空间中。
DAT 文件有效载荷解密
读取计算机唯一标识符 GUID,计算 MD5。检索注册表“SOFTWARE\Microsoft\Cryptography MachineGuid”值获取系统 GUID,计算其 MD5 值,作为后续解密 AES 算法的 IV 值。
检查文件的前四个字节。如果这些字节非零,则表示 DAT 载荷投递在了特定目标上,如果 DAT 文件不是特定于机器的,DodgeBox 将继续使用 AES-CFB 加密解密文件,利用存储在配置文件中的密钥参数。从“sbiedll.dat”载荷的第五个字节开始解密,解密获取一个隐藏 PE 头的 PE 文件。
使用前机器 GUID 的 MD5 哈希作为 AES IV 重新加密载荷。
在完成有效载荷解密后,样本将载荷与当前植入机器绑定,使用原有的 AES 密钥 key,但是将 GUID 的 MD5 哈希作为 AES IV。这种方法保证了解密的 DAT 文件无法在任何其他机器上解密,从而增强了有效载荷的安全性。
关于堆栈调用欺骗可以再做一些更详细的说明。这里借鉴原始报告的描述。
首先设置堆栈,精心构造堆栈的内容,包括插入返回地址和所需的参数,确保 API 调用能按预期进行。
真实 API 调用,在准备好堆栈和寄存器后,执行 jmp 指令,将控制流重定向到目标 API。这种方式使得 API 调用看起来像是从正常位置发起的。
这种方法结合了堆栈伪造和 API 调用技术,通过控制堆栈和寄存器的状态来确保 API 调用能够成功并达到隐蔽的效果。这种复杂的技术可以有效地绕过静态分析和动态监控,增加了恶意代码的隐蔽性。
样本的加盐 FNV1a 哈希的 Python 实现如下所示:
整体样本尤其是一些免杀首发相对复杂,因为中途快照出现了些问题,注释等信息缺失,所以记录思路显得有点混乱,不过还是基本描述清除了相关手法。
评论