APT41 DodgeBox 出来混呢,求的就是一个”稳“字

admin 2024年9月6日12:07:13评论21 views字数 3430阅读11分26秒阅读模式

省流

DodgeBox使用傀儡模块加载payload,具体步骤如下(忽略了很多细节):

  • • 检查payload的有效性

  • • 在system32下查找代码段足够大的模块作为目标

  • • 复制目标模块到指定路径并重命名(下称傀儡模块),优先复制到%systemroot%,如果没有权限,就复制到%programdata%下

  • • 将展开后的payload头和代码段写入傀儡模块

  • • 查找傀儡模块镜像大小的空闲内存

  • • 对傀儡模块进行映射,使用NtCreateSection和ZwMapViewOfSection对模块进行映射

  • • 将展开后的payload剩余部分写入映射区域

  • • 调用payload入口函数


校验payload

  • • 标志位检查

  • • 架构检查

  • • 重要字段判定

    • • 通过属性判断是否是一个可执行的(0x2)dll模块(0x2000)

    • • 文件头中的可选头大小是否等于实际的可选头大小,这个字段在后面会经常用到

  • • 文件大小参数检查

  • APT41 DodgeBox 出来混呢,求的就是一个”稳“字

下面是在校验所有节的文件偏移确实小于文件大小,否则给定的文件大小有误。

APT41 DodgeBox 出来混呢,求的就是一个”稳“字

注意红框内代码,在获取原始文件中节偏移和节大小时并没有使用IMAGE_SECTION_HEADER结构体,而是通过入口点偏移地址加上可选头大小来获得指向原始文件节大小位置,再来获取节偏移,初看会有点懵,需要自己去查表,实际上像这样的小手段还有很多。


查找目标

查找目录

在完成payload校验之后,就开始在system32目录下查找符合一定条件的白文件,用于查找的函数声明如下所示:
参数一和参数二分别用来输出符合条件的文件名和全路径
参数三:payload起始位置到代码段末尾虚拟内存大小
参数四:payload代码段末尾到镜像末尾虚拟内存大小
返回值:找到返回true,未找到返回false
bool find_satisfied_sysdll(wchar_t* _out_name, 
                           wchar_t* _out_path, 
                           DWORD payload_text_end_rva, 
                           DWORD size_except_virtual_text)

查找条件
下面我们来看具体逻辑
在system32目录下有些dll是被列为了白名单,也就是天选之子,不会被感染
      block_dll[0] = (__int64)L"advapi32.dll";
      block_dll[2] = (__int64)L"bcrypt.dll";
      block_dll[3] = (__int64)L"bcryptprimitives.dll";
      block_dll[4] = (__int64)L"cfgmgr32.dll";
      block_dll[5] = (__int64)L"combase.dll";
      block_dll[6] = (__int64)L"cryptbase.dll";
      block_dll[7] = (__int64)L"cryptsp.dll";
      block_dll[8] = (__int64)L"dhcpcsvc.dll";
      ...
除了上述dll以外,其他的dll都要被审视一番,审视的伪代码如下所示
(text_end_virtual - entry_rva - 0x10) > payload_text_end_rva &&
(nt_header->OptionalHeader.SizeOfImage - text_end_virtual) >= size_except_virtual_text
判断一下目标dll从入口点的RVA到代码段末尾的RVA是否大于payload代码段末尾RVA,目标dll从代码段末尾到镜像末尾大小是否大于payload的代码段末尾到镜像末尾大小。
如果觉得绕看不懂,我画张图你可能就理解了。

APT41 DodgeBox 出来混呢,求的就是一个”稳“字

至于为什么要从目标dll的入口RVA开始算起,是因为后面对其进行patch,稍后再说。
如果找到满足条件的dll,就保存到一个数组中,最后从数组中随机挑选一个作为最终目标,选取调用的API同上一节堆栈欺骗的rand64有所不同,使用的是SystemFunction036获取数组下标

感染目标副本

拷贝副本
找到合适的目标以后,会拷贝一份副本到某个特定的目录,优先对 %systemroot%MicrosoftNETassemblyGAC_MSILSystemDataTrace 进行写入,会首先尝试写一个<时间戳>.log进去,如果发现写入失败则写入目录 %programdata%MicrosoftNETSystem.Data.Trace,新的文件名由UuidCreate生成的guid和v4.0_4.0.0.0__拼接而成
既然目标已定,接下来就到了核心部分,目标副本下称傀儡模块

寻找空闲内存
  • • 以(kernel32基址 - 傀儡模块映射内存大小)(需要按照0x100000对齐)为起点,每次递减0x100000查找可用内存区域映射傀儡模块,找到后就能确定payload的新基址,计算公式如下:payload_new_base = new_image_base + src_data_rva - payload_data_rva;

APT41 DodgeBox 出来混呢,求的就是一个”稳“字

payload手动展开
  • • 在内存中展开payload
  • • payload_image_handle_iat:手动加载导入模块,修复导入表,同时抹掉导入表中的模块名和函数名
  • • payload_image_handle_reloc:手动对payload进行重定位
  • • payload_image_remove_table
    • • 抹掉payload的导入表和导入目录
    • • 抹掉pyaload的重定位表和重定位目录
    • • 抹掉调试表和调试目录
  • • paylod_image_remove_other:抹掉文件头中的Machine,TimeDateStamp,Characteristics,可选头中Subsystem,除了.lrsrc之外的节名

感染傀儡模块磁盘文件:
  • • 修改可选头中DllCharacteristics,去掉随机基址
  • • 重新设置加载基址
  • • 抹掉重定位和TLS
  • • 找到入口点的FOA并进行PATCH,写入0xC300000001C0C748也就是
mov eax,1
return
  • • 将payload的【头和代码段】写入文件

阶段性总

为了防止搞混,我通过一张图来总结一下:

目前payload已经在内存中展开并进行导入表,重定位表等修复,以及抹除了某些信息,同时目标文件也已经被抹掉了一些字段,并被写入了payload的代码部分,截止到目前目标文件还没有被映射到内存中

APT41 DodgeBox 出来混呢,求的就是一个”稳“字

映射傀儡模块
那接下来就是要目标文件进行映射,通过NtCreateSectionZwMapViewOfSection实现,成功映射之后,将模块插入到InMemoryOrderModuleList

感染傀儡模块内存
还记得刚才只是将代码段覆盖到目标文件中,随着目标文件的加载,payload代码自然就被映射了,那剩下的内容当然是不能被忽略的,所以插链成功后就需要将payload剩余部分复制到内存中,也就是分两步完成,此时图示如下:

APT41 DodgeBox 出来混呢,求的就是一个”稳“字

至此,整个过程其实很明了了。
剩下的就是再次使用原始文件覆盖被感染的目标文件来隐藏自己
然后就是调用payload的入口点

如果上述流程出问题了怎么办?
那就会调用正常的反射加载函数加载模块,也就是展开->修复IAT->修复重定位那一套,当然还是会抹掉一些表之类的,和上面的一样,最后修改节的内存属性,这里值得一提的是修改属性的方法是一页一页扫过去的,如果发现这个页在某个节内,再去找这个节的属性,然后设置这个页的属性,而不是直接找到节的位置和大小直接修改。

总结:

我暂且用傀儡模块来称呼这一技术实现,从原理上来说我想大多数人听我这么一说感觉并不是十分的复杂,与我们平时所见或者是对比傀儡进程来说无非是将操作步骤进行了拆分,一部分在文件,一部分在内存。但是从代码实现角度来说,APT41的实现是完整且细腻的,文章实际上只是进行了大纲的总结提炼,还有很多的代码细节保证了稳定和健壮性,以及保证了自身的隐蔽性。


原文始发于微信公众号(无名之):APT41 DodgeBox 出来混呢,求的就是一个”稳“字

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月6日12:07:13
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   APT41 DodgeBox 出来混呢,求的就是一个”稳“字https://cn-sec.com/archives/3137844.html

发表评论

匿名网友 填写信息