导语:在Shadow Brokers公开的NSA黑客武器库中,Eternalromance (永恒浪漫) 是影响Windows全平台的SMBv1漏洞攻击工具,现已被微软补丁MS17-010修复。

在Shadow Brokers公开的NSA黑客武器库中,Eternalromance (永恒浪漫) 是影响Windows全平台的SMBv1漏洞攻击工具,现已被微软补丁MS17-010修复,Windows XP和2003等不在微软支持期的系统版本没有补丁,相关用户可以下载使用360“NSA武器库免疫工具”对漏洞进行免疫,能够预防Eternalromance的攻击。

360Vulcan Team的@pgboy1988和@zhong_sf对Eternalromance使用的漏洞进行了技术分析。

1 环境

EXPLOIT:
Eternalromance-1.3.0

TARGET:
windows xp sp3

FILE:
srv.sys 5.1.2600.5512

2 Exploit使用

我们可以发现工具包中有两个Eternalromance, 一个1.4.0, 另外一个是1.3.0。经过我一翻折腾也没有把1.4.0跑起来。无奈试了下1.3.0发现竟然能成功运行。因此便有了这篇分析。大家可能都会用fuzzbunch这个命令行了。但是我们调试的时候总不能调一次都要重新输入TargetIp等那些配置吧。告诉大家一个省劲的方法。首先fuzzbunch按正常流程走一遍。在最后跑起exp的时候,在Log目录下会生成一个xml的配置文件。然后大家就可以用Eternalromance.1.3.0 –inconfig “xml路径” 来调用了。还有就是你也可以改exploit下的那个配置文件不过你得填非常多的参数。

3 基础知识

3.1 SMB Message structure

SMB Messages are divisible into three parts:
* fixed-length header
* variable length parameter block
* variable length data block

header的结构如下:

1492819386380732.png

更详细见 (https://msdn.microsoft.com/en-us/library/ee441702.aspx)

3.2 SMB_COM_TRANSACTION (0x25)

为事务处理协议的传输子协议服务。这些命令可以用于CIFS文件系统内部通信的邮箱和命名管道。如果出书的数据超过了会话建立时规定的MaxBufferSize,必须使用SMB_COM_TRANSACTION_SECONDARY命令来传输超出的部分:SMB_Data.Trans_Data和SMB_Data.Trans_Parameter。这两部分在初始化消息中没有固定。
如果客户端没有发送完所有的SMB_Data.Trans_Data,会将DataCount设置为小于TotalDataCount的一个值。同样的,如果SMB_Data.Trans_Parameters没有发送完,会设置ParameterCount为一个小于TotalParameterCount的值。参数部分优先级高于数据部分,客户端在每个消息中应该尽量多的发送数据。服务器应该可以接收无序到达的SMB_Data.Trans_Parameters 和 SMB_Data.Trans_Data,不论是大量还是少量的数据。
在请求和响应消息中,SMB_Data.Trans_Parameters和SMB_Data.Trans_Data的位置和长度都是由SMB_Parameters.ParameterOffset、SMB_Parameters.ParameterCount,SMB_Parameters.DataOffset和SMB_Parameters.DataCount决定。另外需要说明的是,SMB_Parameters.ParameterDisplacement和SMB_Parameters.DataDisplacement可以用来改变发送数据段的序号。服务器应该优先发送SMB_Data.Trans_Parameters。客户端应该准备好组装收到的SMB_Data.Trans_Parameters和SMB_Data.Trans_Data,即使它们是乱序到达的。

The PID, MID, TID, and UID MUST be the same for all requests and responses that are part of the same transaction.

更详细看 (https://msdn.microsoft.com/en-us/library/ee441489.aspx)

3.3 SMB_COM_TRANSACTION_SECONDARY(0x26)

此命令用来完成SMB_COM_TRANSACTION中未传输完毕数据的传输。在请求和响应消息中,SMB_Data.Trans_Parameters和SMB_Data.Trans_Data的位置和长度都是由SMB_Parameters.ParameterOffset、SMB_Parameters.ParameterCount,SMB_Parameters.DataOffset和SMB_Parameters.DataCount决定。另外需要说明的是,SMB_Parameters.ParameterDisplacement和SMB_Parameters.DataDisplacement可以用来改变发送数据段的序号。服务器应该优先发送SMB_Data.Trans_Parameters。客户端应该准备好组装收到的SMB_Data.Trans_Parameters和SMB_Data.Trans_Data,即使它们是乱序到达的。

更详细看 (https://msdn.microsoft.com/en-us/library/ee441949.aspx)

3.4 SMB_COM_WRITE_ANDX (0x2F)

结构如图:

042103.jpg

更详细看 (https://msdn.microsoft.com/en-us/library/ee441848.aspx)

3.5 总结下漏洞相关的重点

1.    客户端处理SMB_COM_TRANSACTION命令的时候如果数据大小超过MaxBufferSize,则需要使用SMB_COM_TRANSACTION_SECONDARY传输剩下的数据。

2.    对于作为同一Transcation部分的所有请求和响应,PID,MID,TID和UID必须相同。

4 漏洞分析

4.1 SrvSmbTransactionSecondary

结合上一节的重点中提到的。
如果我们先发送了一个数据大小大于MaxBufferSize的SMB_COM_TRANSACTION数据包。那么接下来肯定是要发送SMB_COM_TRANSACTION_SECONDARY来传输剩余的数据,那么服务器在收到处理SMB_COM_TRANSACTION_SECONDARY这个命令的时候肯定会找他对应的那个Transcation。同时服务器也可能同时收到很多个包含SMB_COM_TRANSACTION_SECONDARY命令的数据包。怎么定位某个MB_COM_TRANSACTION_SECONDARY数据包对应的SMB_COM_TRANSACTION数据包呢?重点里也提到了。如果MB_COM_TRANSACTION_SECONDARY数据包中的PID,MID,TID和UID和SMB_COM_TRANSACTION数据包中的相同,那么就认为是同一部分的请求。
代码是这么实现的。

1492819573727939.png

这个SMB_COM_TRANSACTION_SECONDARY命令的处理函数的大体流程就是首先查找SMB_COM_TRANSACTION_SECONDARY对应的SMB_COM_TRANSACTION如果找到就将SMB_COM_TRANSACTION_SECONDARY中的Parameter和Data都复制到SMB_COM_TRANSACTION中去。

4.2 SrvFindTransaction

下面来看下查找的逻辑SrvFindTransaction:

1492819614343249.png

大家可以看下逻辑。重点在这里如果命令是SMB_CMD_WRITE_ANDX(0x2f)话那么MID的对比就不一样了,这里是用Transaction->MID和SMB_CMD_WRITE_ANDX->Fid比较。如果一样返回pTransaction。那么问题来了。如果服务器端正好有一个其他的Transaction->MID恰好和SMB_CMD_WRITE_ANDX->Fid的相等那么将会返回一个错误的pTransaction。很经典的类型混淆。

处理SUM_CMD_WRITE_ANDX的函数SrvSmbWriteAndX代码如下:

1492819648803743.png

看到pTrans_->DataBuffer += Size这句相信大家就能明白了。这里将DataBuffer的指针增大了。再处理此Transcation的SMB_COM_TRANSACTION_SECONDARY命令的时候也就是SrvSmbTransactionSecondary中复制Data的memcpy可就越界了!!!!!
所以此漏洞可以总结成类型混淆造成的越界写。

4.3 Exploit数据包

通过对Exploit抓包我们可以看到其漏洞触发过程。

首先发送SMB_COM_TRANSACTION命令创建一个TID=2049 PID=0 UID=2049 MID=16385(0x4001)的Transcation:

1492819698618556.jpg

然后发送SMB_CMD_WRITE_ANDX命令还增加pTrans_->DataBuffer这个指针:

1492819729394192.png

5 漏洞利用

从上面描述可以看出,该漏洞为类型混淆导致的越界写漏洞。前期通过spray 使多个TRANSACTION相邻,然后让其中一个TRANSACTION触发该漏洞,再通过该TRANSACTION越界写其相邻的TRANSACTION

spary 最终 memory view:

1492819751217377.png

spray的目的是构造出下出三个相邻的transaction, 其中write_transaction 主要用于写操作,leak_transaction 主要用于信息泄露,而control_transaction为触发漏洞的transaction,触发之后其他InData字段会增加0x200, 以致于可写的范围可以向后延伸0x200.利用于这点可以写与其相依的write_transaction的InData字段。从面达到任意地址写的效果。

注: 本次调试中 control_transaction地址为:0x58b90, write_transaction地址为: 0x59c38, leak_transaction地址为:0x5ace0

其中TRANSACTION 结构部份如下:

1492820385736105.png

写操作:

1492820405610011.png

读操作:

1492820438948848.png

从exp运行的log可以看出该漏洞利用分为两部份:信息泄露 与 代码执行

4444.png

5.1 信息泄露

需要泄露的信息包括 Transaction2Dispatch Table基址 与 一块NonePagedPool Buffer地址. 通过修改Transaction2Dispatch Table中的函数指针来执行 shellcode, 其中NonePagedPool Buffer就是用于存放shellcode.

5.1.1 泄露Transaction2Dispatch Table基址

从exp运行的log可以看出首先泄露CONNECTION结构基址:CONNECTION地址存放于TRANSACTION->Connection字段。看到这,你可能已经想到该怎么做了:直接利用constrol transaction的0x200字节的越界能力修改write_transaction的DataCount字段让其可以越界读leak_transaction上的内容,从而读出TRANSACTION->Connection。 但exp作者却并没有这么做,这里并不打算深究其原因,或许是有其他限制,或许不是。
作者这里利用了另一种复杂不少方法,通过另一种方法修改 write_transaction的DataCount字段。

5.1.1.1 write_transaction初始状态如下:

042102.png

可以看出CONNECTION地址为:89a29c18,OutData为0x5acd4 (== 59c38+0x109c)已经是write_transaction的末尾,所以其DataCount为0,表示不可读。

5.1.1.2 修改write_transaction->DataCount

首先 修改write_transaction的InSetup为0x23,这点通过control_transaction很容易完成。之后发包触发写write_transaction操作,会走到:

1492820623906751.png

由于之前已经将write_transaction的InSetup修改为0x23, 所以会call SrvPeekNamedPipe。

1492820658542691.png

SrvPeekNamedPipe() 调用IoCallDriver最终调到 RestartPeekNamedPipe()函数 

1492820737222019.png

该函数最终会修改Transaction->DataCount 为 0x23c。

1492820817310804.png

至此,已经成功修改了write_transaction的DataCount值,之后便可以越界读出 leak_transacion->Connection值: 89a29c18。

5.1.1.3 SRV global data pointer

1492820855728637.png

其中srv!SrvGlobalSpinLocks+0x3c 就是所谓的 SRV global data pointer :b76d8bec

5.1.1.4 Locating function tables

1492820912352694.png

5.1.2 泄露Npp Buffer (shellcode buffer)

这里又得回到ExecuteTransaction函数:

1492820960972629.png

这在这个函数里有这么一个逻辑,当transaction->OutParameters==NULL里,会将PWORK_CONTEXT->ResponseHeader加上一定的offset赋于它,PWORK_CONTEXT->ResponseHeader就是个NonePagedPool.

1492820989912437.png

5.1.2.1 transaction->OutParameters=NULL

Transaction 初始状态下OutParameters并不为NULL:

1492821021751714.png

这里通过write_transaction越界 写 leak_transaction->OutParameters为NULL, 然后发包触发写leak_transaction操作,之后leak_transaction->OutParameters便为一 Npp Buffer值了。

5.1.2.2 leak_transaction->OutData = &leak_transaction->OutParameters

这里要事先泄露leak_transaction的基址,其实也不难,通过读leak_transaction的OutData 或 OutParameters 或 InData 字段的值再减去一定的偏移便得到了基址,使leak_transaction->OutData = &leak_transaction->OutParameters之后,发包触发leak_transaction读操作便将该Npp buffer地址泄露出来了。

5.1.2.3 写shellcode到Npp Buffer

将control_transaction->OutData设为Npp Buffer+0x100地址,然后发包发送shellcode,便将shellcode写到了Npp Buffer+0x100内。

5.2 代码执行

至此,直接将Npp buffer+0x100写到之前泄露出来的函数表里

1492821063168602.png

之后发包就能触发该函数调用:

1492821127273718.png

6 关于补丁

了解了漏洞原理之后修补都很简单了。只要在srv!SrvFindTransaction里面判断一下SMB COMMAND的类型是否一致就好了。
修补前

1492821169944445.png

补丁点就是if ( Command_Trans != Command_header )看注释的地方。

7 总结

总之,这个漏洞还是非常好的,远程任意地址写,还可以信息泄露。威力很大。

8 联系作者

pgboy 微博
zhong_sf 微博

源链接

Hacking more

...