标签:构造 uri 结构 image 请求 函数参数 nts cap 实现
Eternalblue为方程式组织漏洞利用框架中一个针对smb协议进行攻击的模块,由于其稳定性及易用性,被勒索蠕虫Wanacry用于传播,网络上就Eternalblue使用的主要漏洞进行了分析,但是就整体利用并没有一个详细的说明,本文希望尽可能对Eternalblue的整个利用过程进行一个梳理。
攻击机 win7 32
被攻击机 win7 32
Srv.sys:6.1.7601.17514
Srvnet.sys: 6.1.7601.17514
首先是Eternalblue工具中使用到的主体漏洞,该漏洞也是Eternalblue的核心部分,可以通过smb协议的SMB_COM_TRANSACTION2命令触发,该命令说明如下所示:
当该数据包中包含对应的FEA LIST时,smb服务中会将FEA LIST转换为对应的NTFEA LIST
其对应的数据结构并不公开,如下所示为趋势团队分析出的对应的FEALIST结构。
入口处理函数为SrvSmbOpen2,其中漏洞出现在函数SrvOs2FeaListToNt中。
如下所示为对应的漏洞函数SrvOs2FeaListToNt,用于实现FEA LIST转换为对应的NTFEA LIST,函数调用SrvOs2FeaListSizeToNt计算FEALIST的长度,但是该函数存在漏洞导致在特定的情况下,攻击者可以伪造超长的size,从而导致在之后的SrvOs2FeaToNt转换中导致pool溢出。
进入导致漏洞的SrvOs2FeaListSizeToNt函数,该函数会计算对应的FEA LIST的长度并随后对长度进行更新,该长度一开始为DWORD类型的,之后的长度更新代码中计算出的size拷贝回去的时候是按WORD进行的拷贝,此时只要原变量a中的初始值大于FFFF,即为10000+,该函数的计算结果就会增大。
该赋值中如下所示esi变成了si,此时如果eax高位中的数据不为零,则将返回的超长的size。
如下图所示为对应发送的该数据包,可以看到该请求数据包的长度为103d0,其中对应的FEALIST的长度为10000。
如下图所示,eax为链表的开头,其指向了FEA LIST的总长度,即10000,esi为遍历之后的链表尾部,eax-esi=ff5d,为实际对应的长度,但是更新长度的mov指令中esi变成了si,由于eax中的值为10000,原本应该被赋值为ff5d的eax,变成了1ff5d。
之后在紧接着的函数SrvOsFeaToNt中,由于使用了错误的长度进行memmove从而导致溢出。
下图为其中的复制导致越界写,长度为a8,可以看到正常请求应该是在86535000这个srv.sys对象smb buffer中,由于长度过长导致对srvnet.sys分配的smb buffer越界写。
Enternalblue中通过内存布局,将srvnet对象buffer稳定的分配到srv拷贝对象buffer之后,如下图所示为越界写时的内存情况。
Srvnet 对象buffer中包含两个重要的域
因此覆盖并控制MDL将导致之后的tcp 栈实现任意写入伪造对象的操作
覆盖并控制该指针可用于将其指向一个攻击者控制的伪造对象,此时断开smb(srvnet)连接即可导致代码执行。
如下图所示,MDL复写为ffdfef80后,紧接着Eternalblue发送的shellcode就会被写入到ffdfef80+0x80的位置,即ffdff000。
可以看到此时的调用栈。
写入的地址ffdff000是系统预留的用于保存系统信息的地址,并且可执行。
被写入到ffdff000地址的是一个srvnet_recv的结构(该结构不公开)和紧随其后的shellcode,该结构用于smb(srnet)结束或断开连接的时候通过SrvNetWskReceiveComplete调用SrvNetCommonReceiveHandler ,SrvNetCommonReceiveHandler 根据srv_recv中的指针此处为下图中的poi(ffdff190(ffdff020(被覆盖的对应指针)+0x16c)+4)获取到对应的函数并调用,地址即我们伪造的shellcode的地址(ffdff1f1)。
如上述漏洞所示可以导致一次越界写,但是前提是FEA LIST的长度必须大于10000,通过分析可以发现FEA LIST只存在于SMB_COM_TRANSACTION2命令的子命令中,而该命令的数据结构如下,其TotalDataCount(数据包总长度)是USHOER类型的,即最大值只能为FFFF,那这个地方Eternalblue是如何发送的长度大于FFFF的SMB_COM_TRANSACTION2子命令请求的呢?
通过抓包可以发现此处发送的并不是SMB_COM_TRANSACTION2子命令的请求包,而是SMB_COM_NT_TRANSACT子命令的请求包
如下图所示SMB_COM_NT_TRANSACT子命令中TotalDataCount的类型为ULONG,支持发送大于FFFF长度的数据包。
但是SMB_COM_NT_TRANSACT本身是不支持FEA LIST的,这里就涉及到Eternalblue中使用到的第二个漏洞。
Smb的子命令中存在一个名为transcation系列的命令
SMB_COM_TRANSACTION: 用于和邮槽、命名管道进行通信
SMB_COM_TRANSACTION2: 用于打开或创建一个共享文件或文件夹,设置它们的扩展属性
SMB_COM_NT_TRANSACT: 用于打开或创建一个文件或文件夹,并应用扩展属性EA或安全描述符SD
其中产生漏洞的即为对应的SMB_COM_TRANSACTION2命令。
对于transcation系列的命令如果发送的长度过大,smb会将该请求包拆分成**Second的形式进行发送,如下所示为其相应的**Second系列的命令。
SMB_COM_TRANSACTION
SMB_COM_TRANSACTION_SECONDARY
SMB_COM_TRANSACTION2
SMB_COM_TRANSACTION2_SECONDARY
SMB_COM_NT_TRANSACT
SMB_COM_NT_TRANSACT_SECONDARY
服务端根据smb请求头部的TIP,PID,UID,MID确定哪一个**Second属于对应的transtion,而服务端根据最后一个**Second确定对应的transtion类型,即如果最后一个**Second为SMB_COM_TRANSACTION2_SECONDARY,就按SMB_COM_TRANSACTION2来处理。
如下图为处理对应**Second的逻辑,对于一个transaction,如果没有发送完,后续会跟上对应的**Second数据包,服务端不会检查对应的**Second类型,只要保证其TIP,PID,UID,MID匹配,服务端就会将这些数据重新组装还原成一个transaction,而类型由最后一个**Second决定。
因此为了发送一个长度为0x10000的SMB_COM_TRANSACTION2,首先发送一个长度为103d0(FEA LIST:1000)SMB_COM_NT_TRANSACT,之后发送一系列SMB_COM_TRANSACTION2_SECONDARY数据包,只要保证TIP,PID,UID,MID一致,服务端最后就会将其当做一个SMB_COM_TRANSACTION2来处理,而此时其长度103d0。
由于smb会等待最后一个**SECOND数据包到来才生成最后的transaction,因此Eternalblue可以在此期间发包对目标设备的内存进行部署,之后再发送最后一个数据包从而触发漏洞越界写。
如上文所述,漏洞会导致溢出越界写,而Eternalblue中对于该漏洞的利用思路和大多数的pool越界写是一致的。
但是这里和一般的内核漏洞的利用存在一个很大的区别,就是我们的环境是远程的。
通常的本地内核漏洞利用的时候我们可以从容的选择进行spray的内核对象,但是对于远程的环境而言,内核对象的选择及对应的控制就要小很多。
Eternalblue中用于被覆盖的对象为srvnet buffer
srvnet buffer中的对象包含两个重要的结构
微软提供了smb 2直接支持tcp的通信方式,可以通过该方式来创建srvnet缓冲区。
如下图所示srvnet对象的spray过程,生成的大小依赖于前四字节。
srv对象是通过释放后重申请的方式获取的地址空间,但是smb中如何通过远程方式稳定的申请并释放一段pool内存了?这就涉及到Eternalblue中使用的第三个漏洞。
该漏洞出现在SMB_COM_SESSION_SETUP_ANDX命令中
该命令的请求依赖于WordCount的值来确定具体的请求格式,当为12时格式如下图所示,当为13时红框中的变量会有所区别。
直接借用网上逆向简化后的一段代码,如下所示:如果发送的代码中WordConut为12,包含CAP_EXTENDED_SECURITY字段,但却没有FLAGS2_EXTENDED_SECURITY字段,将会导致服务器将以处理13类型请求的方式去处理类型12的请求包,从而进入错误的函数GetNtSecurityParameters流程中。
GetNtSecurityParameters会检查对应的请求中的参数,函数参数中的v70为通过wordcount和Bytecount计算出来的一个size。
GetNtSecurityParameters函数中的计算如下所示:
该参数返回后作为SrvAllocateNonPagedPool的参数分配一段pool。
因此利用该漏洞将12类型的请求包通过13类型进行处理,由于两种类型的请求包格式不一致,通过控制请求包指定偏移的数据,即可以控制SrvAllocateNonPagedPool创建的pool的大小,可以使用以下的断点监控该过程:
bp GetNtSecurityParameters+0x1AC ".printf\"GetNtSecurityParameters1\\n\";r;.echo;?cx-si+bx+1d;g;"
bp SrvAllocateNonPagedPool+0x10 ".printf\"SrvAllocateNonPagedPool NonPageSize:%p\\n\",ecx;g;"
bp SrvAllocateNonPagedPool+0x15C ".printf\"SrvAllocateNonPagedPool alloc Nopage:%p\\n\",eax;g;"
bp BlockingSessionSetupAndX+0x7C0 ".printf\"BlockingSessionSetupAndX double\\n\";g;"
如下图所示即为通过断点监控到的非法size生成的过程,通过构造畸形数据包,包含数据87f8,漏洞触发后识别出该错误的偏移,计算最后会分配一段大小为10fec大小的pool。
通过断开对应的该命令请求,可以导致之前分配的10fec大小的pool被释放,从而在地址空间中生成一个hole,该hole之后会被srv对象buffer来填充。
现在知道如何在内存中稳定的spray一段连续的srvnet的对象buffer,以及如何开辟并释放一段指定大小的空间,内存布局的基本条件已经具备,可以看到具体的布局流程如下所示:
1.通过SMB_COM_NT_TRANSACT发送一段FEA LIST长度满足0x10000的数据包 |
2.发送后续的SMB_COM_TRANSACTION2_SECONDARY,这将导致smb服务将SMB_COM_NT_TRANSACT当做SMB_COM_TRANSACTION2处理,但是最后一个SMB_COM_TRANSACTION2_SECONDARY留置最后。 |
3.通过smb 2协议进行srvnet对象的spray |
4.通过SMB_COM_SESSION_SETUP_ANDX漏洞在srvnet对象之后分配一段大小和srv对象大小几乎一致的pool内存 |
5.通过smb 2协议继续进行srvnet对象的spray,以确保srvnet位于srv对象之后 |
6.断开连接导致第四步开辟的pool内存释放,生成一个hole |
7.发送最后一个SMB_COM_TRANSACTION2_SECONDARY,由于大小一致,该数据包会填补生成的hole,并触发漏洞导致之后的srvnet对象buffer中的MDL和指针被修改,此时后续发送的数据将拷贝到ffdff000的位置。 |
8.断开所有连接,触发srvnet_recv指向的shellcode执行 |
可以通过以下断点监控利用时内存的释放和分配(主要是srv,srvnet对象)
bp SrvAllocateNonPagedPool+0x10 ".printf\"SrvAllocateNonPagedPool NonPageSize:%p\\n\",ecx;g;"
bp SrvAllocateNonPagedPool+0x15C ".printf\"SrvAllocateNonPagedPool alloc Nopage:%p\\n\",eax;g;"
bp SrvFreeNonPagedPool+0x3 ".printf\"SrvFreeNonPagedPool free Nopage:%p\\n\",eax;g;"
bp BlockingSessionSetupAndX ".printf\"BlockingSessionSetupAndX\\n\";g;"
bp SrvNetAllocateNonPagedBufferInternal ".printf\"AllocateNonPaged NonPagedBufferSize:%p\\n\",poi(esp+8);g;"
bp SrvNetAllocateNonPagedBufferInternal+0x179 ".printf\"AllocateNonPaged NonPagedBufferAddress:%p\\n\",eax;g;"
bp SrvNetFreeNonPagedBufferInternal ".printf\"SrvNetFreeNonPagedBufferInternal free NonPageBufferAddress:%p\\n\",poi(esp+4);g;"
ba e1 srvnet!SrvNetWskReceiveComplete+0x13 ".if(poi(esi+0x24) == ffdff020) {} .else {gc}"
如下图所示即为整体监控到的数据包于内存中的布局情况,其中867bb000处为对应的srv buffer对象,之后867cc000上的srvnet buffer对象将会被覆盖如下所示:
以上为Eternalblue利用过程中内存布局及对应发送数据包的一个概述,但是其内部其实还有一些细节可供深入挖掘。
由于作者水平有限,有什么错误欢迎大家指正。
http://bobao.360.cn/learning/detail/3738.html
https://github.com/worawit/MS17-010
http://blog.trendmicro.com/trendlabs-security-intelligence/ms17-010-eternalblue/
标签:构造 uri 结构 image 请求 函数参数 nts cap 实现
原文地址:http://www.cnblogs.com/goabout2/p/7701871.html