GhostEmperor:通过利用内核以及ProxyLogon系列漏洞发起攻击(下)
2021-10-15 12:50:00 Author: www.4hou.com(查看原文) 阅读量:38 收藏

abstract_digital_emperor-1200x600.jpg

IOCTL代码说明

0x220180: 通过验证其大小为 272 字节来处理用户模式恶意软件组件提供的缓冲区,然后通过否定其字节对其进行解码,这个 IOCTL 实际上不是由用户模式代码调用的。

0x220184: 在内核空间分配缓冲区,锁定其页面,创建 MDL 并使用 MmMapLockedPagesSpecifyCache API 将缓冲区映射到用户模式地址。这本质上等同于来自原始调度程序的 IOCTL_CE_ALLOCATEMEM_NONPAGED 和 IOCTL_CE_MAP_MEMORY 中的功能链接。

在这个调用之后,用户模式代码可以访问内核模式缓冲区,并且可以在用户模式下使用指针对其进行写入,就像编写 shellcode 的情况一样。然而,这一次,恶意软件手动将 rootkit 的 PE 映像加载到分配的缓冲区中。

0x2201B4:由于恶意软件的用户模式代码负责在IOCTL 0x220184中手动加载rootkit的映像,它必须解析内核空间中的一些函数地址,这些地址在映像的导入地址表中作为依赖项出现。这个IOCTL允许函数名作为字符串从用户空间接收,使用MmGetSystemRoutineAddress API检索它们的地址,并将其提供给用户模式代码。后者将解析的地址放在加载图像的相应IAT项中。

0x220188:从用户空间取消内核模式缓冲区的地址映射,以便只能通过内核模式指针访问它。

0x2201B8 :使用 IoCreateDriver 函数创建一个新的驱动程序对象,将驱动程序初始化函数指针分配给一个与位置无关的存根,该存根与 shellcode 一起提供,一旦被调用,就会调用加载的 rootkit 的 DriverEntry 函数。

值得一提的是,该恶意软件的服务在加载 dbk64.sys 驱动程序期间使用了名为 kernelmoduleuloader.exe(MD5:96F5312281777E9CC912D5B2D09E6132)的Cheat Engine实用程序。该驱动程序与实用程序和 .sig 文件一起被删除,后者被用作通过传送与其二进制相关联的数字签名来对调用 dbk64.sys 的组件进行身份验证的一种手段。

由于恶意软件不是 Cheat Engine 的组件,它将 kernelmoduleunloader.exe 作为一个新进程运行,并向其注入一个小的 shellcode,该 shellcode 仅使用 CreateFileW API 打开 dbk64.sys 设备的句柄。句柄的值被写入注入缓冲区中的第二个QWORD,由恶意软件的进程读取,并使用DuplicateHandle API进行复制。这样,恶意软件的服务就可以调用驱动程序,就好像它是一个签名的Cheat Engine组件。

6.png

Rootkit 加载阶段

Demodex rootkit功能

加载的 rootkit,我们称之为 Demodex,用于隐藏恶意软件服务构件。这是通过一组由 rootkit 驱动程序公开的 IOCTL 来实现的,这些 IOCTL 依次由服务的用户模式代码调用,每个 IOCTL 都伪装了一个特定的恶意构件。为了访问 rootkit 的功能,恶意软件应该获得相应设备对象的句柄,在此之后,以下IOCTL 可用于进一步使用:

0x220204:接收带有运行恶意服务代码的 svchost.exe 进程的 PID 参数,并将其存储在全局变量中。此变量稍后由其他 IOCTL 使用。

0x220224:初始化稍后用于保存数据的全局变量,例如前面提到的 svchost.exe PID、恶意软件服务的名称、恶意软件 DLL 的路径和网络端口。

0x220300:在services.exe进程地址空间的列表中隐藏恶意软件的服务。服务的名称作为参数传递给IOCTL,然后在系统维护的链表中查找。相应的条目被解除链接,从而使服务无法被轻易检测到。

0x220304:这个IOCTL用于通过IoRegisterFSRegistrationChange API注册一个文件系统过滤驱动的通知例程。注册新文件系统时调用的通知例程验证它是否是基于ntfs的文件系统,如果是,则为附加到主题文件系统的设备堆栈的rootkit创建一个设备对象。此外,文件系统的设备对象和关联的rootkit设备对象都被注册在由rootkit驱动程序维护的全局列表中。随后从该文件中检索信息、访问或修改该文件的尝试将会失败,并生成错误代码,如STATUS_NO_MORE_FILES或STATUS_NO_SUCH_FILE。

0x220308:从列出它们的实用程序(例如 netstat)中隐藏使用给定范围内端口的 TCP 连接。这是通过一种 known4 方法完成的,其中 NSI 代理驱动程序的 IOCTL 调度例程被挂钩,并且完成例程被设置为检查给定连接的端口。如果底层连接的端口在给定范围内,则其条目将从系统的 TCP 表中删除。构成范围的两个端口作为参数传递给 IOCTL。

0x22030C:通过 CmRegisterCallback API 挂钩多个注册表操作来隐藏与恶意软件相关的注册表项。注册的回调会检查操作的类型并按照以下逻辑进行操作:

对于 RegNtPostEnumerateKey 或 RegNtPostEnumerateValueKey(项或子项的枚举)类型的操作,它验证是否尝试枚举 HKLM\SYSTEM\ControlSet0**\Services\

对于 SOFTWARE\Microsoft\{EAAB20A7-9B68-4185-A447-7E4D21621943} 类型的 RegNtPreOpenKeyEx(尝试打开项)类型的操作,它会清除驱动程序的所有内部全局变量,这相当于重置其操作。这是因为该项是由恶意软件的卸载程序 PowerShell 脚本使用的,在前面的部分中提到过。

对于任何试图通过RegNtPreSaveKey或更低代码的操作在HKLM\MACHINE\SYSTEM下更改项的操作,它会将返回状态设置为应用程序错误 0xC0000043。

有趣的是,传递给 CmRegisterCallback 的指针不包含处理上述逻辑的函数的直接地址,而是包含 pci.sys 驱动程序映像的可执行部分末尾的地址,该地址最初用0填充对齐内存中的部分。在将回调指针传递给 CmRegisterCallback 之前,会在 pci.sys 驱动程序中寻找这样的部分,并修补其中的相应字节,以便调用对处理上述逻辑的实际回调的调用,如下所述。这允许所有拦截的注册表操作看起来好像它们是由源自合法 pci.sys 驱动程序的代码处理的。

7.png

用于修补内存中 pci.sys 映像中的部分的代码,以便使用跳转到注册表检查回调的短 shellcode 存根编写它

值得一提的是,Demodex rootkit 在设计上支持 Windows 10,根据我们对 Windows 10 版本的测试,它确实可以正常工作。这在多个地方的驱动程序代码中很明显,其中根据底层操作系统的版本采用不同的代码流。在此类检查中,可以观察到某些流对应于 Windows 10 的最新版本,如下面的代码片段所示。

8.png

混淆和反分析方法

GhostEmperor 活动集群中使用的恶意软件组件的开发者做出了一些对取证分析过程有影响的设计。为了介绍研究人员所面临的一些障碍,我们将讨论限于两种常见的分析工具——WinDbg和Volatility。在处理有问题的植入程序时,其他工具可能会遇到类似的障碍。

首先,由于 Demodex 的加载方式,它的驱动程序没有正确地与其他以文档方式加载的系统模块一起列入 WinDbg。也就是说,仍然可以通过引用其名称 (\driver\dump_audio_codec0) 来找到 rootkit 的驱动程序对象,从而也可以列出其关联的设备对象:

9.png

WinDBG 中列出的驱动程序对象名称

同样,当尝试使用 Volatility3 widows.driverscan 模块列出系统模块时,输出中不存在Demodex驱动程序。但是,该框架确实表明在扫描内核内存空间以搜索驱动程序的过程中检测到异常:

10.png

使用 windows.driverscan Volatility3 模块列出Demodex驱动程序时出现异常

此外,恶意软件开发者有意选择从恶意软件的第三阶段和 rootkit 驱动程序的内存加载图像中删除所有 PE 标头。这是通过引入带有清零标头的图像开始(如第三阶段的情况)并依靠自定义加载程序来准备执行或通过在加载后替换图像的标头来完成0x00 值,就像 rootkit 的驱动程序一样。从取证的角度来看,这会阻碍通过搜索标头文件来识别加载到内存中的 PE 图像的过程。

11.png

如上所述,开发人员在 pci.sys 合法驱动程序中实现了一个蹦床( trampoline),以屏蔽为注册表相关操作调用的回调源。因此,试图追踪此类回调的分析师可能会忽略一些回调,因为它们看上去是良性的回调。如下面 Cm* 回调的 WinDbg 列表所示,其中一个与符号 pci!ArbLibraryDeinitialize+0xa4 相关联;然而,如果我们查看同一地址的代码,我们会发现它实际上是 rootkit 发出的一小段 shellcode,目的是跳转到隐藏恶意软件注册表项的实际恶意回调。

12.png

在从 pci.sys 驱动程序调用的看似良性代码中发现的 Cm* 回调和 shellcode 列表

除了上述方法之外,开发人员还引入了更多标准的混淆方法,这些方法通常会减慢代码的静态分析速度,并且在多个恶意软件组件中都很明显。这方面的一个例子是字符串混淆模式,即使用一组预定义的算术和逻辑操作对每个字符串进行解码,以便为每个字符串选择不同的操作数(例如移位偏移量)。这表明在编译期间每个字符串都是模糊的,并且开发者建立了一种 SDK 形式,有助于在构建期间对每个样本进行独特的混淆。

13.png

用于通过一组算术和逻辑运算从硬编码 blob 中获取明文字符串的字符串解码逻辑

同样,可以在代码中观察到 API 调用混淆的多个实例。这是通过用其他存根函数替换对 API 函数的内联调用来完成的,这些存根函数将请求的 API 名称构建为堆栈字符串,使用 GetProcAddress 解析它并在将特殊结构中提供的参数传递给存根函数的同时调用它。结构体的大小比传递参数数据所需的要大,并且其中大部分都充满了垃圾,因此只有特定的字段才有有意义的数据,这些数据在传递给存根之前会被编码。这些字段在存根函数中被解码,然后传递给 API 函数。

14.png

用于 API 调用混淆的存根示例

值得注意的是,在字符串混淆的情况下,每个存根都是唯一构建的,并使用一个不同大小的参数结构,其中被实际参数数据占用的字段是随机选择的。堆栈字符串的初始化顺序也是随机的,并且每个存根函数只用于替换单个内联API函数调用一次。换句话说,在代码中不同地方使用的相同 API 函数将在每个地方有不同的存根,具有不同的参数结构。这强化了开发者使用指定混淆 SDK 的观察结果,其中 API 调用混淆是另一个功能。

最后,可以看到一些变体以混淆和非混淆形式出现。例如,我们设法以两种形式查看第二阶段加载程序的 C++ 版本,有一种形式根本没有混淆,另一种形式严重混淆(MD5:18BE25AB5592329858965BEDFCC105AF)。在下图中,我们可以在两个变体中看到相同的函数:一个是编译器生成的原始代码流,没有经过混淆,而另一种是将控制流简化到无法跟踪操作顺序的程度。

15.png

在第二阶段加载程序的两个变体中使用的相同函数的示例;一个是非混淆的,另一个的控制流被简化

Post-exploitation工具集

一旦攻击者通过前面提到的感染链获得了对被攻击系统的访问权限,他们就会使用合法和开源的攻击工具集来获取用户凭证,并转向网络中的其他系统。这包括Sysinternals套件中用于控制进程的常用工具(例如,PsExec, PsList和ProcDump),以及其他工具,如WinRAR, CertUtil和BITSAdmin。至于开源工具,攻击者使用如mimkat_ssp、Get-PassHashes.ps1、Token.exe、Ladon等工具。内部网络侦察和通信通常由 NBTscan 和 Powercat 进行。

网络基础设施

对于 C2 通信,攻击者注册的域名似乎是随机生成的,可能不会引起对恶意流量的任何关注。 GhostEmperor 主要使用基于香港和韩国的托管服务,例如 Daou Technology 或 Anchent Asia Limited。

16.png

我们还观察到用于下载一些恶意样本或内存植入程序的 C2 通信的其他 IP 地址:

17.png

攻击目标

GhostEmperor 的大多数受害者是东南亚的政府和电信公司,其中不乏马来西亚、泰国、越南和印度尼西亚。我们还观察到来自埃及、埃塞俄比亚和阿富汗等国家受害者。尽管后一组受害者与我们看到 GhostEmperor 高度活跃的地区属于不同的地区,但我们注意到其中的一些组织与东南亚国家有着密切的联系。

本文翻译自:https://securelist.com/ghostemperor-from-proxylogon-to-kernel-mode/104407/如若转载,请注明原文地址


文章来源: https://www.4hou.com/posts/Yqv2
如有侵权请联系:admin#unsafe.sh