作者:Numen Cyber Labs
原文链接:https://mp.weixin.qq.com/s/5oBAw-oLtHA52-0eBcPSpg
背景和准备
微软上月发布的补丁包含一个可能执行代码的TCP/IP协议漏洞。为了验证该漏洞的影响范围和可能后果,Numen 高级安全研究团队对此漏洞做了深入的分析,并通过补丁对比,还原出了PoC。本文将详细介绍我们如何通过补丁对比还原出PoC,以及漏洞的细节。Numen 将会持续输出高质量的安全研究文章,以及成果,为Web3安全以及网络安全其他领域提供权威高质量的研究成果,助力网络安全健康发展。
首先熟悉这些与IPv6和IPSec ESP协议相关的数据包结构有助于理解本文。技术参考如下:
IPv6:https://www.rfc-editor.org/rfc/rfc2460#page-6
IPSec ESP: https://www.rfc-editor.org/rfc/rfc2406
补丁分析
有了之前的知识,我们可以更容易地理解脆弱性原理。现在让我们分析补丁。
比较tcpip.sys的八月和九月补丁,我们发现有两个函数需要修补。如下所示:
这里的补丁修补方式表明在重组ipv6分片数据的时候,似乎存在某一个内存偏移大于预期的可能错误。除此之外,还有另一个修补过的函数:
这处函数修补作用并不太直观,我们只能知道接收IPsec_ESP封包的处理过程中,如果其某一标志位不满足补丁修补条件,这个包将会被丢弃。
PoC 构造
A. 初步尝试
尽管Microsoft在漏洞描述中明确表示,它将向启用IPSec的节点发送ipv6数据包。然而,从上面对漏洞补丁的分析来看,我们认为如果我们能够构建一个符合Ipv6pReassembleDatagram函数中修补条件的IPv6碎片数据,我们可以触发内存中错误(大于预期)偏移量的损坏。
一开始,我们忽略了IPSec的条件,专注于IPV6碎片化和包重组。tcpip DOS漏洞分析CVE-2021-24086 2021年初(https://blog.quarkslab.com/analysis-of-a-windows-ipv6-fragmentation-vulnerability-cve-2021-24086.html)提供了一个良好的切入点
参考本文中的PoC,我们可以在Ipv6pReassembleDatagram函数中重新组织一组IPV6分区包。但我们发现,只有普通ipv6标头携带碎片数据包,这不会影响补丁中限制的参数。
查看漏洞的官方描述,并注意第二个补丁。我们认为可能有必要使用IPSec中的ESP包来控制第一个补丁的关键参数。
我们开始构建启用IPSec协议的测试环境。
B. 构造IPSec流量包
如前介绍,IPSec是一种可以对指定类型ip流量筛选过滤,并加密验证的一种协议。通常在VPN或者增强其他协议密码安全性的部分条件下使用。在windows中
我们搭建了一个域条件下,两台对对方所有流量都加密验证的环境。因为很多时候它并不是一种默认开启的协议。
为了构造这种数据包,我们必须知道协议中指定的加密算法和秘钥。其中IPSec_ESP中加密方式我们可以自行选择。在本次分析中我们仅仅启用了IPSec中的完整性HASH验证,使用SHA1计算hash。但这已经能满足漏洞触发的基本要求。
这里需要额外说明IPSec数据包构造过程中数据加密的秘钥获取(如果选择linux作为攻击机的话,密钥获取将比较容易可以忽略这里)。
Windows中的IPSec的加密过程都是在tcpip协议驱动中实现的,虽然在应用层中,微软提供了一套WFP流量筛选平台框架API可以控制IPSec中SA的一些参数(如SPI),但是对于秘钥管理这一部分,我们并不能直接获取到IPSec_ESP包中加密所需的秘钥和其他的一些具体的加密规则(不同于流量加密秘钥每次的变化,流量完整性验证秘钥虽然也会不停变化,但在一段时间内,该秘钥是固定的)。
通过分析tcpip驱动,我们可以从tcpip的MICROSOFT_TCPIP_PROVIDER_Context中获取密钥(在测试环境,我们直接从内核中BCryptCreateHash的参数中获取)。并且如前所述,我们的IPSec流量中,只启用了流量完整性验证,并没有使用流量加密。这样对后续的分析,协议理解都比较直观。
构造好IPSec流量包后,通过调整IPSec数据包中的参数,我们的数据包满足了第二个修补函数对IPSec_ESP包在第二处补丁函数对指定标志的限制条件。对照协议结构,我们可以知道,第二处补丁函数限制的标志位对应的是IPSec_ESP包中加密数据的类型标志。当ESP中加密包ipv6扩展头类型为小于等于0时丢弃该数据包。具体包括一下几项0(IPv6 Hop-by-Hop Option)或者0x2b(Routing Header for IPv6)0x2c(Fragment Header for IPv6)
我们的测试中,使用了Fragment Header for IPv6的包(0x2c)。
C. 最终的 PoC
有了我们需要的IPSec流量包结构后,我们现在需要做的就是将第一步尝试构造的IPv6分片数据当做ESP协议中需要被加密以及验证的数据(其中关键是ESP尾部结构中最后一位标志ESP携带的扩展头类型标志设为0x2c),并组装好后开始发送。
由于该漏洞需要发送原始ip数据包,并且在初期调试的过程中,最好能够比较方便的控制修改每一个发送的ip包字节。我们需要一个比较底层且灵活的发包接口。喜欢python的朋友可以使用scapy。
我们这里使用的是一个现成的NDIS协议驱动。(该驱动代码和编译及安装。参考《windows网络通讯程序设计第二版》代码示例)。使用底层驱动协议的好处是除了个人习惯,另外有很多的各类型协议数据包组装代码示例方便理解。
总结
从根本上讲,该漏洞修复主要限制ESP携带IPv6 IPSec协议中提到的几个IPv6扩展头。
当前PoC代码可能导致NetIo协议头对象内容中的字节(大于0x38的任意偏移地址)被重写为任意值(此处为0x2C)。这里提到的0x2c不是ESP中携带的第一个ipv6扩展标头类型标志。它是ESP中携带的分区标头旁边的ipv6扩展标头标志。这可以任意设置。
在解析下一个扩展头时,我们可以通过构造一些准确的扩展头来崩溃esp包。如果随后的esp解密内容无效且与扩展头类型不一致,TCPIP通常不会立即触发异常(通过控制内存损坏的位置,我们可以构建一些不同类型的系统崩溃场景):
对于该漏洞的利用,目前有两种思路。
A、 重点分析目前我们可能损坏的NetIo协议头对象的详细结构,看看数据大小或其他关键数据位置是否可以通过我们写入的数据转换为其他缓冲区复制错误,从而构建任意读/写函数。
B、 因为我们可以破坏的位置不限于NetIo协议头对象,我们可以通过构造更大的偏移量(通过修改ESP包的长度)来覆盖其他对象。因此,可以使用其他对象来构造读/写源语言或代码执行。
但无论上述哪一种想法,都可能需要进一步分析其确切的可行性
实际影响
从漏洞本身的范围来看,该漏洞依赖于IPv6协议和IPSec协议的组合。IPv6目前被广泛使用。有许多场景支持IPv6,但一些常用的VPN或一些需要流量加密的环境通常不使用IPSec作为默认选项,需要由管理员设置。
此外,我们的PoC在域中设置了两个IP安全策略的环境中实现。首先,我们通过域验证身份,然后我们可以向其他受信任的域成员目标发送易受攻击的数据包。未经身份验证发送的加密流量将不会被处理。否则,您需要获得预共享的密钥才能完成身份验证。
漏洞PoC详情, 请访问:https://github.com/numencyber/VulnerabilityPoC。
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1996/