写点什么

针对一个红队病毒样本逆向分析

  • 2024-08-02
    湖南
  • 本文字数:3172 字

    阅读完需:约 10 分钟

近日翻到一个比较新颖的样本,在最终后门载荷释放前运用了不少免杀手段,包括堆栈欺骗,实现反射性调用 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 算法进行完整性检查

样本分析

侧加载

  1. “白加黑”侧加载执行初始恶意载荷。由 Sandboxie 签名的合法可执行文件 (taskhost.exe) 侧载恶意 DLL (sbiedll.dll)。

“sbiedll.dll”中初始化了三个导出函数,但实际上都指向相同的地址,函数名称“SbieDll_Hook”


  1. 编写一个加载器执行恶意导出函数“SbieDll_Hook”,同步虚拟内存地址,动态、静态结合调试主要功能。

配置信息解密

  1. 利用 MD5 校验加密配置信息,确保配置的完整性。计算密文段的 MD5 hash 来于硬编码在样本中的正确 hash 值做比对。

两段密文 hash 值:

FE93E8E1C5C3032A26D783A78A8205871E8EE70F02D60E389D8F721E8CE6DF1F
复制代码


  1. AES-CFB 算法进行解密,获取解密配置信息入下,其中包括明文字符“sbiedll.dat”、“--type”以及其他配置信息或密钥供后续使用。

杀软规避

  1. 执行进程是否使用正确的参数启动,验证期望参数包含“--type driver”,如果此验证检查失败,则终止进程。计参数的 MD5 哈希值,多次计算结果与硬编码的哈希值进行比较。

硬编码 hash 值:

E2D45D57C7E2941B65C6CCD64AF4223E
复制代码


  1. 利用加盐的 FNV-1a 哈希算法来逃避针对 WINAPI 的静态检测。在哈希计算过程中添加盐值不同样本的哈希值不同,提高了逃避静态检测的能力。

上图中伪代码通过指针运算和类型转换访问内存,还原 C 代码,所需 API 地址通过结构体成员访问。

sImportTable->ntdll_LdrLoadDll = ResolveImport(L"ntdll", 0xFE0B07B0, 0xCA7BB6AC);
复制代码

函数体内部关键步骤入下:

  • 初始化哈希值。

  • 对输入字符串(DLL 名或函数名)进行哈希计算。

  • 对盐值进行哈希计算。

首先,它对输入字符串(代表 DLL 或函数名称)进行哈希处理。然后,它单独对盐值进行哈希处理。此两步哈希处理过程相当于对输入字符串和盐的连接进行哈希处理。

while ( 1 )  {    Fun_FNV1a_Hash = 0x811C9DC5;                // FNV-1a 初始哈希值    InputString = (unsigned __int8 *)(a1 + *v12);    v18 = -1i64;    do      ++v18;    while ( InputString[v18] );                 // 输入字符长度计算    for ( i = &InputString[(unsigned int)v18]; InputString < i; Fun_FNV1a_Hash = 0x1000193 * (Fun_FNV1a_Hash ^ v20) )// 计算输入字符串FNV-1a哈希      v20 = *InputString++;    v21 = (unsigned __int8 *)&unk_7FFFBEAD7D78; // Salt value: CB 24 B4 BA    do    {      v22 = *v21++;      Fun_FNV1a_Hash = 0x1000193 * (Fun_FNV1a_Hash ^ v22);// 对盐值进行哈希计算    }    while ( v21 < byte_7FFFBEAD7D7C );
复制代码

盐值,CB 24 B4 BA

最终将 API 函数地址放置与连续的内存地址中,即结构体成员变量中。下图以 API 函数“LookupAccountSidw”为例。

此外 ResolveImport 中还有内置了一些 API 函数调用相关功能,此样本中暂时没用到。

  1. DodgeBox 在完成 API 初始化后,它就会扫描并解除从 System32 目录加载的 DLL。此过程包括遍历 .pdata 每个 DLL 的部分,检索每个函数的起始和结束地址,并计算每个函数字节的 FNV1a 哈希值。计算存储在磁盘上的相同函数字节的相应哈希值。如果两个哈希值不同,则可以检测到潜在的篡改,将用磁盘中的原始版本替换内存中的函数。

此过程包括遍历 .pdata 每个 DLL ,步骤方法入下:

p_InMemoryOrderModuleList = &NtCurrentTeb()->ProcessEnvironmentBlock->Ldr->InMemoryOrderModuleList;
复制代码

当前线程的线程环境块(TEB)->进程环境块(PEB)->已加载模块的信息链表 InMemoryOrderModuleList。


获取链表信息后再循环遍历加载模块列表的每个节点。

p_InMemoryOrderModuleList = &NtCurrentTeb()->ProcessEnvironmentBlock->Ldr->InMemoryOrderModuleList;  for ( i = p_InMemoryOrderModuleList->Flink; i != p_InMemoryOrderModuleList; i = i->Flink )  {    Flink = (__int64)i[2].Flink;    v3 = (const wchar_t *)i[4].Flink;    if ( ((__int64)i[5].Blink & 0x60000000) != 0x20000000      && (*(_WORD *)(*(int *)(Flink + 60) + Flink + 22) & 0x2000) != 0 )
复制代码

DAT 文件有效载荷解密及加载

  1. 在当前进程堆中开辟内存,读取“sbiedll.dat”文件流。

在完成路径等相关字符初始化后,利用 NtReadFile 读取文件流至当前进程的堆空间中。


  1. 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 实现如下所示:

func fnv1aSalted(data, salt []byte, seedValue uint32) uint32 {
combinedData := append(data, salt...)
hash := seedValue prime := uint32(0x01000193)
for _, byteValue := range combinedData { hash ^= uint32(byteValue) hash *= prime hash &= 0xFFFFFFFF } return hash}
func main() { ntdll := []byte("n\x00t\x00d\x00l\x00l\x00") salt := []byte{0xba, 0xb4, 0x24, 0xcb}
hash1 := fnv1aSalted(ntdll, salt, 0x811c9dc5) fmt.Printf("Hash for ntdll: 0x%08x\n", hash1)
ldrLoadDll := []byte("LdrLoadDll") hash2 := fnv1aSalted(ldrLoadDll, salt, 0x811c9dc5) fmt.Printf("Hash for LdrLoadDll: 0x%08x\n", hash2)}
复制代码

整体样本尤其是一些免杀首发相对复杂,因为中途快照出现了些问题,注释等信息缺失,所以记录思路显得有点混乱,不过还是基本描述清除了相关手法。

用户头像

欢迎关注,一起学习,一起交流,一起进步 2020-06-14 加入

公众号:做梦都在改BUG

评论

发布
暂无评论
针对一个红队病毒样本逆向分析_黑客_我再BUG界嘎嘎乱杀_InfoQ写作社区