第三种:SMB_COM_NT_TRANS数据包的内存布局如下所述,InParameters和OutParameters之间、InData和OutData之间的缓冲区都是重叠的。
+---------------+-----------------------------------------------------------+ | TRANSACTION | transaction data buffer | +---------------+-----------------------------------------------------------+ | InSetup | InParameters | InData | | +---------+----------------------+--------------------------+ | | OutParameters | | OutData | +-----------------------------------------------------------+06 当ParameterCount等于TotalParamterCount且DataCount等于TotalDataCount时,本次Transaction请求就会被服务端处理。
07 当处理transaction请求时,InParameters和InData指针有可能会被修改。
08 处理完Transaction请求后,ParameterCount和DataCount字段(通常在被调用的transaction处理函数中设置)被分别用于决定响应数据包中OutParameters和OutData缓冲区的大小。
09 SMB_COM_*_SECONDARY请求可以被用来覆盖之前SMB数据包发送的trans_parameters和trans_data的内容。无论覆盖的偏移是多少,ParameterCount和DataCount字段都会相应增加。
假设TotalParameterCount为0,TotalDataCount为16。第一个transaction请求中包含8字节的trans_data。如果第二个transaction请求中包含偏移为0的8字节数据(正常情况下偏移应该为8),就会导致第一个transaction请求的8字节数据全部被覆盖,并且接下来的8字节trans_data没有被覆盖。
[译者注] 在SMB_COM_*_SECONDARY请求中包含ParameterDisplacement和DataDisplacement两个偏移字段,用来定义当前SMB数据包中的trans_parameters和trans_data在InParameters缓冲区和InData缓冲区的偏移。正如上述举例所示,将第二个请求的DataDisplacement偏移设置为0,则第二个请求的Data内容就会覆盖第一个请求的Data内容。
10 对于复杂的transaction请求(指的是那些采用secondary才能完成传输过程的Transaction)而言,服务端根据最后一个SMB_COM_*_SECONDARY命令来确定transaction的类型。
如果最后一个命令是SMB_COM_TRANSACTION_SECONDARY,服务端后续会按照TRANS_*处理子命令;如果最后一个命令是SMB_COM_TRANSACTION2_SECONDARY,服务端后续会按照TRANS2_*处理子命令;如果最后一个命令是SMB_COM_NT_TRANSACT_SECONDARY,服务端后续会按照NT_TRANSACT_*处理子命令。
11 WriteMode字段被设置为RAW_MODE的SMB_COM_WRITE_ANDX命令,也采用Transaction方式在客户端与服务端之间进行数据交互。期间Transaction采用SMB_Parameters.FID代替SMB_Header.MID来实现前后transaction数据包的匹配。
上述关于SMB Transaction的知识已经足够了,下面赶紧开始漏洞细节吧。这些都是通过MS17-010补丁对比发现的。
Bug1:Transaction InParameters和InData缓冲区未初始化漏洞[译者注] 通常来说,申请一块内存后的第一件事,就是将这块内存的所有字节初始化为0×00或其它内容。遗憾的是,Transaction data buffer这一点做的并不完善。
微软在SMB协议的实现上,申请Transaction data buffer后并没有将其初始化。如果我们发送多个ParameterDisplacement和DataDisplacement偏移为0的transaction请求,由于ParameterCount和DataCount字段无论偏移是多少都会相应增加(参见实现细节09),因此服务端会将未初始化的trans_parameter和trans_data缓冲区的内容作为后续处理函数的输入数据。
一般情况下,服务端进程会将输入的trans_parameter和trans_data作为不可信数据进行处理(使用前会进行验证),因此未初始化的输入通常并没有什么用处。但是,如果我们能够找到一个可以将输入数据作为输出返回给客户端的transaction命令,就可以利用这个bug来泄露输入中未初始化的数据内容。