大约在一个月前,Shadow Brokers(影子经纪人)泄露了方程式组织(为NSA效力的黑客组织)的软件FuzzBunch,一个类似于Metasploit的利用框架。这个框架中有几个针对Windows的未认证的远程利用(如EternalBlue, EternalRomance和 EternalSynergy)。很多利用的漏洞在MS17-010中被修复了,它可能是近十年来最关键的Windows补丁。
提示:你能使用我的MS17-010 Metasploit auxiliary module来扫描你网络中没有打补丁的系统。如果没有安装补丁,还会检查是否感染DoublePulsar。
0x01 介绍
DoublePulsar是FuzzBunch中SMB和RDP利用中使用的主要的payload。下面分析是在Windows Server 2008 R2 SP1 x64上使用EternalBlue SMBv1/SMBv2 exploit完成的。
Shellcode基本上执行了下面几个步骤:
步骤0:判断x86还是x64。
步骤1: 从KPCR中定位IDT,从第一个中断处理反向遍历找到ntoskrnl.exe的基址(DOS MZ头)。
步骤2:读取ntoskrnl.exe的导出目录,并使用哈希(和应用层shellcode类似)来找到ExAllocatePool/ExFreePool/ZwQuerySystemInformation函数地址。
步骤3:使用枚举值SystemQueryModuleInformation调用ZwQuerySystemInformation,得到一个加载驱动的列表。通过这个定位到Srv.sys,一个SMB驱动。
步骤4:将位于SrvTransaction2DispatchTable[14]的SrvTransactionNotImplemented()函数指针指向自己的hook函数。
步骤5:使用辅助的DoublePulsar payload(如注入dll),hook函数检查是否正确运行并分配一个可执行的缓冲区来运行原始的shellcode。所有的其他请求直接转发给原始的SrvTransactionNotImplemented()函数。"Burning" DoublePulsar不会完全擦除内存中hook函数,只是休眠它。
在利用后,你能看到缺少SrvTransaction2DispatchTable符号。在这里应该有两个处理程序与SrvTransactionNotImplemented符号。这是DoublePulsar后门(数组索引14):
我真的很好奇这个payload,在Countercept的DLL注入代码分析之外没有看到很多它的细节。但是我很好奇初始SMB后门是如何安装的,这也是本文的内容。
使用IA32_LSTAR系统调用MSR(0xc000082)和包含FEFE的Srv.sys的区域的EternalBlue利用中有一些有趣的设置,但是我将专注于原始的DoublePulsar的方法…很像EXTRABACON shellcode,这个非常狡猾并不只是产生一个shell。
0x02 shellcode详细分析
在shadow brokers的转储中你能找到DoublePulsar.exe和EternalBlue.exe。当你使用FuzzBunch中的DoublePulsar,有个选项是将它的shellcode输出到一个文件中。我们还发现EternalBlue.exe包含了它自己的payload。
步骤0:判断CPU架构
主payload非常大,因为它包含x86和x64的shellcode。前面一些字节使用操作码技巧来决定正确的架构(参考我之前的文章汇编架构检测)。
下面是x86头几个字节。
你该注意到inc eax意思是je指令不执行。接着是一个call和pop,获取正确的指令指针。
下面是x64的:
其中inc eax由rex替换。因此zf标志寄存器由xor eax,eax操作设置。因为x64有RIP相对寻址,不需要获取RIP寄存器。
X86的payload和x64的基本一样,所以这里只关注x64。
由于NOP在x64中一个真正的NOP,我使用16进制编辑器用CC CC(int 3)覆写40 90。中断3是调试器的软件断点。
现在执行利用,我们附加的内核调试器将自动断点在shellcode开始执行处。
步骤1:找到ntoskrnl.exe的基址
一旦shellcode判断是在x64上面运行,它将开始搜索ntoskrnl.exe的基地址。代码片段如下:
相当简单的代码。在用户模式下,x64的GS段包含线程信息块(TIB),其保存了进程环境块(PEB),该结构包含了当前运行进程的各种信息。在内核模式中,这个段寄存器包含内核进程控制区(KPCR),其中偏移0处包含当前进程的PEB。
该代码获取KPCR的偏移0x38处,是IdtBase并包含一个KIDTENTRY64结构的指针。在x86中很熟悉,能知道这是中断描述符表。
在KIDTENTRY64的偏移4,你能得到中断处理的函数指针,其代码定义在ntoskrnl.exe中。从那里按页大小(0x1000)增长反向搜索内存中.exe的DOS MZ头(cmp bx,0x5a4d)。
步骤2:定位必要的函数指针
一旦你知道了PE文件的MZ头的位置,你能定位到导出目录,并得到你想要的函数的相对虚拟地址。用户层的shellcode一直能做到这个,通常是找到ntdll.dll和kernel32.dll的一些必要的函数。只需要和用户层shellcode一样,ring0 shellcode也使用哈希算法代替硬编码字符串,以便找到必要的函数。
下面是要找的函数:
ZwQuerySystemInformation
ExAllocatePool
ExFreePool
ExAllocatePool能用来创建可执行内存区域,并且ExFreePool能用来清理内存区域。这些是重要的,因此shellcode能为它的hook函数和其他函数分配空间。ZwQuerySystemInformation在下一步中是重要的。
步骤3:定位SMB驱动Srv.sys
使用SystemQueryModuleInformation(0xb)调用ZwQuerySystemInformation能实现。得到所有加载的驱动的列表。
Shellcode在这个列表中搜索两个不同的哈希,定位到Srv.sys,这是SMB运行的主要驱动。
过程和用户层一样,通过PEB->Ldr得到,遍历搜索加载的DLL。这里要查找的是SMB驱动。
步骤4:Patch SMB的trans2派遣表
现在DoublePulsar已经有了主要的SMB驱动,它遍历.sys的PE节,直到找到.data节。
.data节中通常是全局读写内存,在这里存储着SrvTransaction2DispatchTable,一个处理不同的SMB任务的函数指针数组。
Shellcode分配一些内存并实现函数hook。
接下来shellcode存储派遣函数SrvTransactionNotImplemented()的函数指针(以便能在hook函数中调用)。然后使用hook覆盖SrvTransaction2DispatchTable中的这个成员。
后门完成了。现在它返回到它自己的调用栈,并做一些小的清理操作。
步骤5:发送“Knock”和原始的shellcode
当DoublePulsar发送了指定的“knock”请求(被视为不可靠的SMB调用),派遣表调用hook的假的SrvTransactionNotImpletemented()函数。能观察到奇怪的行为:正常的SMB响应MultiplexID必须匹配SMB请求的MultiplexID,但是增加了delta作为一个状态码。
操作能够隐身,在Wireshark中没有合适的解析。
状态码(通过MultiplexID delta):
0x10 = 成功
0x20 = 不可靠的参数
0x30 = 分配失败
操作列表:
0x23 = ping
0xc8 = exec
0x77 = kill
你能使用下面的算法得到操作码:
反之,你能使用这个算法制作包,其中k是随机生成的:
在一个Trans2 SESSION_SETUP请求中发送一个ping操作将得到一个响应,其中包含需要为exec请求计算的XOR密钥的一部分。
“XOR密钥”的算法是:
更多的shellcode能使用Trans2 SESSION_SETUP请求和exec操作发送。使用XOR密钥作为基本流密码,一次性在数据包4096字节的“数据payload”部分中发送。后门将分配一块可执行内存区域,解密复制shellcode并运行。注入dll的payload能注入你想要的DLL。
我们能看见hook被安装在SrvTransaction2DispatchTable+0x70 (112/8 = index 14)处:
全部的汇编在这里。
0x03 总结
这是一个复杂的多平台的SMB后门。它是一个非常酷的payload,因为你能感染一个系统,驻留,并能在你想要的时候做更多的事。它在系统中找到了一个很好的隐藏位置,并且不会触PatchGuard。
通常我们只能在本地漏洞利用看到内核shellcode,因为它会变换进程令牌以便提权。然而,微软在内核中做了很多网络相关的事,例如Srv.sys和HTTP.sys。所描述的技术在很多方式完全符合远程利用中用户模式的shellcode的操作方式。
当它移植到Metasploit中,我可能不会逐字节复制,而是跳过后门的想法。它不是最安全的,因为它不再是秘密,任何人都可以使用你的后门。
下面是可以代替做的事:
1. 和DoublePulsar相同的方式获得ntoskrnl.exe地址,并读取导出目录获得必要的函数。
2. 启动一个隐藏的进程(如notepad.exe)
3. 使用Meterpreter payload插入APC
4. 恢复进程,退出内核
5. ???
6. 收获
|