和很多安全行业从业者一样,这些天我们忙于Shadow Brokers泄漏的实现研究和检测。其中有大量有趣的内容,尤其是DOUBLEPULSAR payload引起了我们的注意。因为它是一个非常稳定的内核模式的payload,是很多漏洞利用的默认的payload。另外,它能注入任意的DLL到用户层的进程中。我们也确认了一个潜在的有用的特征,以检测这种技术是否用于主机(需要未重启)上。
我们尤其感兴趣我们的EDR软件是否能检测出这种代码注入,以便更好的保护我们的用户。很多安全行业的人都在测试注入Meterpreter DLL和其他公开的框架,我们也这么做了,并且确认了我们能检测到这种注入的DLL和线程。然而,类似Meterpreter移植体在内存中的噪点非常多,我们不确定我们是否只能检测特定的公开的反射加载的技术,或者我们是否能检测到通用的DOUBLEPULSAR注入技术。
0x01 细节
首先,我们研究了通过它使用的机制能否注入任意的DLL,而不是像很多公开的利用框架使用的反射加载技术需要特殊构造的DLL。我们尝试注入一个标准的Windows DLL(wininet.dll)到calc.exe进程中,同时使用Sysinternals的进程监控工具监控,并使用Windbg分析注入前后的目标进程的地址空间,同时还是用了我们EDR软件。
正如我们所见,wininet.dll成功加载了,因为它加载了依赖的DLL,如normaliz.dll、urlmon.dll等。然而,在那之前没有什么可观察的行为,没有wininet自身的加载,意味着它必须使用内存技术加载。另外,我们也看到了EDR软件报告的两种反射dll加载技术,以确认了内存DLL注入技术。通过在windbg中比较注入前后的地址空间,我们很快能发现一些有趣的区域,与我们报告的可疑的内存区域对应。
第一个区域是有趣的,因为它看起来很像一个加载的DLL,但是所有的节是独立加载的,不像标准DLL的文件映射,并且明显使用自定义的loader加载的,而不是标准的Windows loader。分析这些节能与wininet.dll内容对应。
第二个感兴趣的是对应原始wininet整个内容的单独区域。奇怪的是,有个区域在这之前也分配了PAGE_EXECUTE_READWRITE,并且是个更大的内存但内容几乎都是0,除了内存中23字节的小内存块。
尽管在我们的EDR软件中也能看到这些,这很明显是一种不同于各种利用框架中和已知的恶意软件家族中的标准公开技术的高级技术,我们很想知道它是如何工作的,因此我们进一步深入分析。
另外,我们还解密了C2通信流量,它使用简单的4字节XOR算法,我们最近公布了一个python脚本来完成解密。我们使用这个转储了使用DOUBLEPULSAR注入DLL时发给服务器的整个payload。进一步分析,我们发现了接近4885字节的内核代码,接着是逐字节的wininet的副本。我们假设这是一些必要的机制,目的是为了将内核空间中任意的DLL隐蔽的加载到用户模式的进程中,因此我们逆向了这个payload。下面是payload的每部分的细节。
在一些标准函数序言后,payload调用了下面的函数,很明显是遍历内存,直到找到MZ头为止(0x5a4d)。这用于定位内核内存中的ntoskrnl.exe。然后使用作为指针,开始动态定位需要的内核函数,它使用了下面的函数:
这个函数使用4字节的哈希来定位感兴趣的函数。这和其他的shellcode技术很类似,而不是硬编码函数名字字符串来定位函数。哈希处理如下:
我们使用python实现哈希算法,基于所有的内核函数生成一个哈希查询表,并使用这个来记录解析后期需要的函数。这个查询过程和注释如下:
继续解析另外一些函数,但是在这里我们做了一个假设,它将枚举进程以找到要注入的目标进程,然后使用ZwAllocateVirtualMemory和KeInsertQueueApc组合注入用户层的DLL到目标进程中,然后通过APC执行代码。
在这里我们跳过了一些无趣的细节但是是必要的,在内核中定位函数的过程如下:
枚举运行的进程
检查进程名得到想要的目标
附加到进程中,提取命令行参数,检查得到想要的目标
在目标进程中使用PAGE_EXECUTE_READWRITE属性分配内存
在内存中写入0x12458a字节,来自后面的内核payload(起始于“SUWVATUAA”)
|