ToB企服应用市场:ToB评测及商务社交产业平台
标题:
针对一个红队病毒样本逆向分析
[打印本页]
作者:
王國慶
时间:
2024-10-4 17:58
标题:
针对一个红队病毒样本逆向分析
近日翻到一个比较新奇的样本,在最终后门载荷释放前运用了不少免杀手段,包括堆栈诱骗,实现反射性调用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”。
2. 编写一个加载器执行恶意导出函数“SbieDll_Hook”,同步假造内存地址,动态、静态联合调试主要功能。
设置信息解密
3. 使用MD5校验加密设置信息,确保设置的完备性。计算密文段的MD5 hash来于硬编码在样本中的正确hash值做比对。
两段密文hash值:
FE93E8E1C5C3032A26D783A78A820587
1E8EE70F02D60E389D8F721E8CE6DF1F
复制代码
4. AES-CFB算法进行解密,获取解密设置信息入下,其中包括明笔墨符“sbiedll.dat”、“--type”以及其他设置信息或密钥供后续使用。
杀软规避
5. 执行历程是否使用正确的参数启动,验证期望参数包含“--type driver”,如果此验证查抄失败,则终止历程。计参数的MD5哈希值,多次计算效果与硬编码的哈希值进行比较。
硬编码hash值:
E2D45D57C7E2941B65C6CCD64AF4223E
复制代码
6. 使用加盐的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函数调用相干功能,此样本中暂时没用到。
7. 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文件有用载荷解密及加载
8. 在当前历程堆中开辟内存,读取“sbiedll.dat”文件流。
在完成路径等相干字符初始化后,使用NtReadFile读取文件流至当前历程的堆空间中。
9. 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 文件无法在任何其他机器上解密,从而加强了有用载荷的安全性。
10. 关于堆栈调用诱骗可以再做一些更具体的说明。这里鉴戒原始报告的描述。
首先设置堆栈,经心构造堆栈的内容,包括插入返回地址和所需的参数,确保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)
}
复制代码
团体样本尤其是一些免杀首发相对复杂,因为中途快照出现了些问题,注释等信息缺失,以是纪录思绪显得有点混乱,不过照旧根本描述扫除了相干手法。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4