这个能够完美利用该bug的transaction子命令就是NT_TRANSACT_RENAME。微软相关文档中将该命令标记为“未实现”,但实际上srv.sys中处理该命令的SrvSmbNtRename函数是有具体实现代码的,其大致伪代码如下所示:
SrvSmbNtRename() { // ParameterCount must be >= 4 // first 2 bytes of InParameters is fid // verify fid // if verification failed, return error without data // if verification success, return success without modifying OutParameters, ParameterCount, OutData, DataCount }[译者注] 该函数首先要求ParameterCount字段必须大于等于4,并且InParameters的前两个字节定义为FID字段。然后判断FID是否合法。如果FID不合法,会返回一个错误;如果FID合法,则会返回不作任何修改的OutParameters、ParameterCount、OutData、DataCount等内存数据。
前面实现细节05中已经提到,transaction的InData和OutData的内存区域是重叠的。因此,如果transaction的*Parameter*和*Data*区域不作任何改动,SrvSmbNtRename函数验证FID成功后,实际上返回的是InData缓冲区的内容(类似于echo回显命令)。
要想成功实现回显,对NT_TRANSACT_RENAME命令唯一的要求就是提供一个合法的FID。因此我们首先需要通过打开一个命名管道或共享来从服务端得到一个合法的FID。
由于泄露的信息来自于已经释放了的缓冲区,因此这个bug对漏洞利用并没有太大帮助。并且transaction的长度始终不小于0×5000字节,也很难从中筛选出有效信息。
该bug可能的用途:
(1)通过泄露的指针检测目标系统架构是32位还是64位;
(2)泄露的内存中可能包含某些重要的数据。
备注:
(1)该漏洞并没有被用在NSA武器库中;
(2)微软起初推出的补丁只是将InParameters和InData缓冲区初始化为0,由于In*和Out*不完全重叠,因此还是有可能从OutParameters和 OutData缓冲区造成信息泄露。后续5月17号的安全补丁就修复了srv.sys中多个有可能造成OutParameters和OutData缓冲区信息泄露的函数漏洞。未修复前,这些函数都没有将OutParameters和OutData缓冲区初始化为0;
(3)5月17号的安全补丁修改了SrvSmbNtRename函数,使其只返回一个错误信息。
Bug2: TRANS_PEEK_NMPIPE子命令始终期望MaxParameterCount为16SrvPeekNamedPipe函数用于处理TRANS_PEEK_NMPIPE子命令。它会将命名管道数据trans_data存在OutParameters缓冲区中,具体位于OutParameters+16的内存位置。
[译者注] OutParameters和OutData的内存位置是相邻的,正常情况下命名管道数据存储的内存位置就是OutData缓冲区的起始位置,也就是说OutData Pointer = OutParameters+16。
+------------------------------------------------------+ | OutParameters | OutData | +------------------------------------------------------+如果MaxParameterCount字段等于16,那么OutData正好会指向正确的命名管道数据的内存位置。但如果故意设置MaxParameterCount的值大于16,OutData Pointer = OutParameters + MaxParameterCount,就有可能泄露未初始化的OutData缓冲区。结合后面的bug3,会达到更好的利用效果。
值得注意的是,网上很多扫描器利用这个bug来判断MS17-010是否已经被修补。
SrvAllocationTransaction函数用于申请transaction结构和transaction data缓冲区。如果申请的transaction data缓冲区长度大于0×10400字节,该函数就将指向transaction的指针设置为NULL,然后向客户端返回一个STATUS_INSUFF_SERVER_RESOURCES/0xC0000205错误码。
按照上述逻辑,如果客户端发送一个MaxParameterCount、MaxDataCount二者之和大于0×10400的请求时,理论上应该得到一个0xC0000205的错误码。
不过为了修复上面这个内存泄露漏洞,在调用SrvAllocationTransaction函数之前,MS17-010补丁会将TRANS_PEEK_NMPIPE命令的MaxParameterCount字段强制修改为16。这样就会导致,即使原本MaxParameterCount、MaxDataCount二者之和大于0×10400,如果MaxParameterCount被修改后,二者之和很没有超过0×10400,SrvAllocationTransaction函数就不会返回错误码,反而继续调用SrvPeekNamedPipe函数。而SrvPeekNamedPipe响应给客户端的内容由InSetup的内容决定。