DodgeBox | ThreatLabz
这是个24年的样本。逆向这个加载器意在学习在实际攻击场景中,恶意样本会如何选择内存免杀中的各种技术,以及我们从其技术选择中如何一窥其战略。
样本大致为白加黑的DLL侧载。本次涉及到的IOC:在bootstrap_runtime_and_scan_modules函数中,首先使用MD5算法对其config blob和payload 进行校验。MD5应该是加载器生成器在二进制中预置的。接下来,以config头16Bytes为IV,以一个内置key对整个payload blob进行流式解密。然后进行了一次参数验证。其首先通过AES-CFB算法对内置数据进行解密,获取一个模块名称msvcrt.dll和函数名称__getmainargs,并分别用loadlibraryw和getprocessaddr进行加载,然后运行获得当前进程的命令行参数,然后检查命令行参数中是否包含--type字符串,并对其之后的字符串进行MD5检查。根据分析,其所验证的参数为--type driver。
接下来样本通过一个自定义的函数位置解析器init_hashed_api_dispatch_table从给定的系统dll中查找特定的导出函数地址。特别之处在于这里使用了FNV-1a哈希算法。FNV-1a(Fowler-Noll-Vo)是一种非加密型哈希算法,以其极高的计算速度和极低的碰撞率而闻名。它非常适合处理字符串、文件名、IP 地址等短数据的哈希化。这个也是常规流程了,能够以不错的效果规避一些静态扫描。不过现在一些安全方案里面不仅会存储这些常见的api名称,还可能存储一些常见的api的哈希。这里使用的相对小众的算法可能能够规避。
样本需要首先获取需要脱钩的模块。遍历PEBInMemoryOrderModuleList,对于每个模块,读取LDR_DATA_TABLE_ENTRY中的Flags字段。检查 Flags & 0x60000000 是否为 0x20000000。如果是,表示该模块已被此 Loader 处理过,直接跳过。读取模块 PE 头的 Characteristics。必须包含 IMAGE_FILE_DLL (0x2000),即只处理 DLL,不处理 EXE。接下来解析 FullDllName,提取其父目录名。只有父目录名为 system32 的模块(如 ntdll.dll, kernel32.dll, user32.dll)才进入后续模块脱钩。样本通过pNtReadFile函数从System32目录下读取 被标记为需要处理的DLL,然后进行内存映射和重定位。在这里,加载器按照预定义的规则进行脱钩:PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY (winnt.h) - Win32 apps | Microsoft Learn
接下来是CFG Bypass环节。我们首先有一个问题:为什么这个样本要进行CFG?实际上是在为之后的DLL Hollowing进程注入进行准备,其会读写非CFG位图中所允许的位置。样本先调用RtlGetVersion函数检查系统版本是否大于0x602(NT 6.2 = Windows 8,即须为 Windows 8.1 及以上),然后检查CFG是否开启:调用 probe_cfg_policy通过GetProcessMitigationPolicy(-1,ProcessControlFlowGuardPolicy,0,4)调用检查PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY结构体中EnableControlFlowGuard值是否为1。如果通过门控检查,则开始进行CFG Bypass。在locate_hook_target_pattern函数中,程序会尝试通过特定的二进制序列匹配到LdrpHandleInvalidUserCallTarget函数的入口点。这个函数是当CFG检查失败的时候会调用的函数。这里还会对windows版本进行检查,以windows 10 14393版本为分界线,使用两套不同的特征码进行定位。定位到的函数头内存指针会返回到主函数中。主函数会先将该函数头size为5的部分设置为PAGE_EXECUTE_READWRITE,然后在这个函数头改写为JMP RAX; INT 3; NOP。
CFG Bypass相关资料
接下来仍然是CFG BYPASS环节。程序首先尝试解析msvcrt的longjmp函数,然后向前寻找一个特定模式MOV EBX,EDX; MOV RDI,RCX; CALL rel32并且尝试解析rel32的绝对目标longjmp_dispatch_fn,然后校验 longjmp_dispatch_fn 以 48 8B 05(MOV RAX,[RIP+disp32]) + magic DWORD 0x48D18B48 开头。这一系列的魔法操作是怎么回事呢?查看msvcrt发现:跳转到__except_validate_jump_buffer:别的不说我们先来看一下这段函数逻辑。这里是 __except_validate_jump_buffer 函数,它是 longjmp 调用的一部分。由于 longjmp 会通过非正常的堆栈回溯改变控制流,它是黑客最喜欢利用的攻击点之一,因此 Windows 必须在这里检查跳转的目标是否合法可以发现这里是意图获取CFG机制的开关指针__guard_check_icall_fptr。首先获取*(__guard_check_icall_fptr),然后和下面的_CrtSetDbgBlockType逻辑对比。这个_CrtSetDbgBlockType在这里则是一个空容器,内容为retn 0。如果指针所指向的代码的位置和空容器相同,则说明CFG被短路,否则执行之后的CFG检查。所以样本首先获取了__guard_check_icall_fptr,然后获取了_CrtSetDbgBlockType,最后使前者指向后者。如此则短路检查必定ok。最后样本再将刚才修改过的权限复原。我个人用一些类似APC或VEH劫持的办法去劫持线程RIP寄存器也是一种不错的CFG Bypass办法。接下来样本会验证机器的mac地址、机器名称以及当前登录的账户。对于当前登录的账户,OpenProcessToken打开进程令牌,GetTokenInformation(TokenUser)获取当前用户SID,LookupAccountSidW将SID解析为account_name+domain_name,然后AES-CFB8解密PE中嵌入的期望账户名/域名密文,使用_wcsicmp比较;匹配返回1,否则返回0。在我拿到的样本中,不存在内嵌的mac、机器名称配置。接下来开始执行载荷读取和加载功能。函数会通过一系列函数的调用生成载荷路径,并且对载荷进行校验和解密。在这里有一个很有意思的环境控制功能:最初始版本的载荷是通用的,但是一旦在某一台机器上执行了函数,则会被这台机器通过其注册表中的MachineGUID得到的MD5进行加密,尽可能延缓被安全厂商通过
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-290150.htm
[原创]APT41-DodgeBox载荷加载器逆向——堆栈欺骗技术探究
158 浏览
13 回复
厉害,学习
学习
感谢分享,学习了!
学习
感谢分享
看看!!!!!!
学习了
牛逼
回复康康
感谢分享
感谢分享,学习一下。
感谢分享,学习一下。
xiexie