2017-05-22 11:46:13
阅读:743次
点赞(0)
收藏
来源: countercept.com
作者:myswsun
翻译:myswsun
预估稿费:200RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
0x00 前言
和很多安全行业从业者一样,这些天我们忙于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”)
跟踪定位payload揭露了shellcode的另一部分,真实的原始DLL内容。现在,考虑到在windbg中的用户空间的地址看不到这个,我们只能看见原始DLL的内容和加载的DLL的节内容。我们稍后回到这里。
下面,我们看到APC调用被调用来执行注入。这非常接近于内核payload的末尾了。注入的内存似乎以代码开头,接着DLL内容,在windbg的内存空间中看不到这些,这意味有一个二级stage用户模式的payload和DLL一起被注入,并且通过APC转移控制,以便能真正的加载DLL到目标进程中。
有趣的是,然后我们看见了内核payload确实清除了它的内容:
在这里内核payload清除了所有的代码,包括DLL内容等。然而它没有清除一小段代码,并且在一大段0中间留着这段小代码段。结果是这段代码非常类似于早前我们在空白区域中看到的保留的代码。所以,我们认为很明显用户层的payload负责内存加载DLL和清空自身。
为了进一步调查,我们在DOUBLEPULSAR发送DLL注入payload之前将windbg附加到calc.exe上,暂停进程的执行,然后分析地址空间。在内核payload执行后我们能看到进程内存,但是在用户层stage执行前,加载DLL并清除了内存,这更加证明了我们假设。
在这里,我们能看到我们希望的。调试器暂停了calc.exe的执行,结果我们看不到加载的DLL,也看不到包含wininet的0x124000字节大小的内存。然而,之前大部分空白的0x125000字节大小的内存区域现在包含了通过内核payload注入的起始代码,之后还有wininet的整个内容。恢复执行,我们能看到分配了一块新的内存区域包含了wininet,然后在内存中有节被加载,接着是用户层payload擦除自身,只留下一小部分代码片段。
这导致了一个明显的内存特征,因为它是一段特定的字节序列,被0包围,还总是出现在不同大小的DLL的相同偏移中。这提供了有效的内存分析证据,能帮助发现DOUBLEPULSAR之前的受害者,当然需要系统还没被重启过。
真实的dll加载是通过用户层的payload加载的,我们稍后会详细介绍。
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://www.countercept.com/our-thinking/analyzing-the-doublepulsar-kernel-dll-injection-technique/