DNS Server远程代码执行漏洞分析-SIGRed(CVE-2020-1350)( 五 )

  • 有一个对Name_PacketNameToCountNameEx的调用 , 它的结果用于计算传递给RR_AllocateEx的大小 。
  • 传递给RR_AllocateEx的值是使用16位或16位以上的值计算的 。
  • dns.exe中满足这三个条件的唯一函数是NsecWireRead 。 让我们检查一下从函数反编译中得到的代码片段:
    RESOURCE_RECORD* NsecWireRead(PARSED_WIRE_RECORD *pParsedWireRecord, DNS_PACKET *pPacket, BYTE *pRecordData, WORD wRecordDataLength) { DNS_RESOURCE_RECORD *pResourceRecord; unsigned BYTE *pCurrentPos; unsigned int dwRemainingDataLength; unsigned int dwBytesRead; unsigned int dwAllocationSize; DNS_COUNT_NAME countName; pResourceRecord = NULL; pCurrentPos = Name_PacketNameToCountNameEx(&countName, pPacket, pRecordData, pRecordData + wRecordDataLength, 0); if (pCurrentPos) { if (pCurrentPos >= pRecordData// = (unsigned int)(pCurrentPos - pRecordData)) // = dwBytesRead// data, &countName); memcpy(&pResourceRecord->data + pResourceRecord->data->bOffset + 2, pCurrentPos, dwRemainingDataLength); } } } } return pResourceRecord; }如您所见 , 这个函数包含许多安全检查 。 其中一项是16位溢出检查 , 它可以防止我们在该函数中的漏洞变体 。 我们还想指出 , 这个函数比dns中的函数有更多的安全检查 。 这让我们怀疑这个漏洞是否已经被注意到和修复 , 但只是在那个特定的函数 。
    如上所述 , Microsoft在两个不同的模块中实现了DNS客户端和DNS服务器 。 虽然我们的漏洞确实存在于DNS服务器中 , 但我们想看看它是否也存在于DNS客户端中 。
    DNS Server远程代码执行漏洞分析-SIGRed(CVE-2020-1350)
    本文插图
    看来 , 不像dns.exe!SigWireRead, dnsapi.dll!Sig_RecordRead 它确实验证了Sig_RecordRead+D0传递给dnsapi.dll!Dns_AllocateRecordEx的值小于0xFFFF字节 , 从而防止了溢出 。
    这个漏洞在dnsapi.dll中不存在 。 并且两个模块之间的命名约定不同 , 这使我们相信Microsoft管理DNS服务器和DNS客户端的两个完全不同的代码库 , 并且不会同步它们之间的bug补丁 。
    Exploitation Plan
    根据Microsoft的要求 , 我们决定保留有关漏洞利用原语的信息 , 以便给用户足够的时间来修补他们的DNS服务器 。 相反 , 我们将讨论应用于Windows Server 2012R2的利用方案 。 然而 , 我们相信这个方案也应该适用于其他版本的Windows Server 。
    该dns.exe二进制文件是用控制流保护(CFG)编译的 , 这意味着在内存中重写函数指针的传统方法不足以利用这个漏洞 。 如果这个二进制文件不是用CFG编译的 , 利用这个漏洞将是非常简单的 , 因为很早就我们遇到了以下崩溃:
    DNS Server远程代码执行漏洞分析-SIGRed(CVE-2020-1350)
    本文插图
    如您所见 , 我们在ntdll!LdrpValidateUserCallTarget上崩溃了 。 这个函数负责作为CFG的一部分验证目标函数指针 。 我们可以看到被验证的指针(rcx)是完全可控的 , 这意味着我们成功地覆盖了某个函数指针 。 我们看到崩溃的原因是函数指针被用作全局位图表的索引 , 每个地址有“allowed” / “disallowed”位 , 而我们的任意地址导致从表本身的未映射page读取 。
    要利用这个漏洞在绕过CFG的同时实现远程代码执行 , 我们需要找到提供以下功能的原因:write- where(精确地覆盖栈上的返回地址)和infoleak(泄漏内存地址) 。
    信息泄漏
    为了实现Infoleak(信息泄露)原语 , 我们利用溢出破坏了DNS资源记录的元数据 , 而它仍在缓存中 。 然后 , 当再次从缓存中查询时 , 我们可以泄漏相邻的堆内存 。


    推荐阅读