我们研究了 Windows TCP/IP 网络堆栈漏洞,该漏洞可能授予攻击者以最大权限进行远程访问。利用 CVE-2024-38063 并不意味着用户采取任何行动。在本文中,我们通过比较两个版本的驱动程序并开发攻击链来解释如何遏制该漏洞
每月的第二个星期二,微软都会发布“补丁星期二”更新,这是一个修复严重漏洞的 Windows 更新。8 月 13 日,该供应商修复了网络堆栈中的一个严重漏洞,如果启用 IPv6,该漏洞允许通过 TCP/IP 进行特权远程访问。
该漏洞标识符为 CVE-2024-38063。本质上,这是一个零点击 Windows TCP/IP RCE。根据 Microsoft 安全公告,该漏洞是由于tcpip.sys
负责处理 IPv6 数据包的驱动程序的一个函数中存在整数下溢漏洞造成的。由于此漏洞的严重性,我们的漏洞研究团队开始玩“补丁星期二 — 漏洞星期三”(他们花了两周时间才获胜)。
初步分析
为了了解漏洞的原因,我们检查了补丁前后的驱动程序文件。本质上,有两种方法可以做到这一点:
-
保存旧文件,然后更新 Windows,并获取新文件。这是最可靠的方法,但最好旧文件不要太旧。否则,您无法在 BinDiff 中进行良好而方便的比较,而是可能会得到大量的更改,其中大多数与安全无关。
-
使用Winbindex,这是一个进行各种 Windows 相关研究的优秀网站。更新的文件会在那里稍微延迟出现,但并不严重。
为了明确起见,我们决定使用第二种方法:我们访问 Winbindex 网站并下载了两个最新版本的 Windows 10 22H2 驱动程序。
此处,10.0.19041.4780 是唯一已解决漏洞的文件版本。您可以通过单击“额外”列中的“显示”或将鼠标悬停在更新编号上来查看这些文件的发布日期。
上传文件后,您可以使用开源实用程序BinDiff进行比较;例如作为 IDA Pro 的插件。在比较这两个文件时,我们在补丁星期二分析中遇到了一个相当不寻常的情况:受此更改影响的唯一函数是Ipv6pProcessOptions
,它用于处理 Jumbo Payload 或 Hop-By-Hop 等 IPv6 数据包选项。
如果你查看反编译代码中的函数内部,你会发现最后只有修改过的部分。
在 7 月 23 日的版本中:
在8月13日的版本中:
现在,该IppSendError
函数用于IppSendErrorList
在处理 IPv6 选项时发生错误。它们之间的区别在于,它将IppSendErrorList
错误发送给链中的每个数据包,而只IppSendError
发送给一个数据包。如果您查看该函数的反编译代码,就会看到这一点IppSendErrorList
:
对修改部分周围的代码进行进一步分析,可以发现以下逻辑:Ipv6pProcessOptions
被设计为仅处理一个数据包(或片段),而 则IppSendErrorList
处理链中的所有数据包。这是一个明显的逻辑错误,可能会导致一些不好的事情。但是,我们不知道到底是什么样的坏事,所以我们继续研究。但首先我们决定开发一个检查器,看看我们是否正确理解了逻辑。
事实上,对三个存在 IPv6 选项错误的数据包,一共有六个响应:
-
每个数据包 1 有三个错误(数据包 1 → 数据包 2 → 数据包 3)
-
每个数据包 2 有两个错误(数据包 2 → 数据包 3)
-
每个数据包 3 有一个错误 (数据包 3)
-
需要注意的是,只有当系统上的 Windows 防火墙被禁用时,带有错误的数据包才会到达发送方。
但是,启用防火墙并不会影响发送的数据包是否到达易受攻击的系统,因为它们在被防火墙处理之前就已经在操作系统内核级别进行了处理。
从零到英雄
查看该函数的反编译代码时IppSendError,您可能会注意到以下片段:
此行为是正确的,因为调用此函数会终止当前数据包的处理并发送错误消息。但是,由于在易受攻击的驱动程序版本中,此代码片段针对链中的每个数据包(包括未处理的数据包)执行,因此该IPv6_HeaderSize
字段可能会用于处理后续数据包。实际上,此字段等于 IPv6 标头大小,包括所有嵌套选项标头。
我们意识到情况不应该如此,并且工作数据包的大小字段不应等于零。我们很长时间都无法确定该原语的具体应用位置,因此我们转向了之前对 Windows TCP/IP 网络堆栈漏洞的研究(1、2)。
我们偶然发现了一篇文章,探讨了源于 IPv6 碎片化的类似漏洞 CVE-2022-34718。该漏洞存在于Ipv6pReassembleDatagram
使用未记录的Packet_t
和Reassembly_t
对象的函数中。
Reassembly_t
虽然此函数没有使用我们更改其值的字段,但它会影响父级对象的字段Ipv6pReceiveFragment
:
在这里我们可以看到,它0x30
从之前错误更改的 IPv6_HeaderSize 字段的值中减去,因为该函数假定此阶段的大小不能小于 48 个字节。此代码片段是应用先前获得的原语的绝佳机会,但我们仍需要到达这里。为此,我们需要通过两项检查,其中第一项可以在上图中看到:它检查该片段是否是链中的第一个。虽然这种情况减少了利用可能性,但还不够:一个数据包就足够了。
第二个条件是检查片段偏移量不为零,但是由于中的处理IppSendError
(其中NetioRetreatNetBufferList
调用以将插入符号返回到数据包的开头),该条件实际上检查FlowLabel
IPv6 标头结构中的偏移量不为零。
Windows 如何查看tcpip.sys 代码
现实中会发生什么
现实中会发生什么上述Ipv6pReassembleDatagram
函数在从缓冲区复制数据时会用到此结构字段。
此时,我们已经确信我们已经完全了解了这个漏洞,但是由于函数开头的下一个检查,内核并没有执行必要的代码:
这里,TotalLength变量等于新初始化的大小Reassembly_t与下溢的大小之和(利用后始终相等0xffd8):
你可能会认为这里发生了溢出UnfragmentableLength,但加法是在类型内执行的UInt32,因此不会发生溢出,并且TotalLength会大于0xfff。
完成度和影响力
在研究使用该Reassembly 结构的其他函数时,我们发现了一个处理程序,该处理程序在下一个片段的超时到期时调用Ipv6pReassemblyTimeout。此函数还使用负(大正)大小从我们的缓冲区复制数据。
为了得到这个代码片段,我们需要将FlowLimit其设置为 1(因为IppSendError,驱动程序将此字段解释为FragOffset)并等待一分钟。此后,Windows 内核中的池缓冲区会溢出,溢出值几乎完全由我们控制,因为很容易在附近喷射必要的块。看起来一切都很好,对吧?
远程利用此类漏洞始终存在一个大问题:我们不知道 KASLR 之后的内核地址。如果我们在受攻击的主机上执行(这是完全有可能的,允许将权限提升到 SYSTEM),我们可以充分利用 Windows 中的各种 KASLR 泄漏(1、2 ) 。然而,我们的情况并非如此,尽管上述使用原语的方法为本地权限提升和远程 BSOD 提供了绝佳的机会。对分配的内存部分大小的严格限制不允许我们在不让内核崩溃的情况下了解内核地址。
这里有两点需要注意:
-
任何可能危害 KASLR 地址的额外内核漏洞(与所呈现的漏洞一起)都会导致 RCE。
-
Windows 网络堆栈是操作系统中最复杂的部分之一。本文介绍的利用原语可能会影响后续数据包处理中的代码部分,因此很可能存在其他(高度复杂的)攻击方法,可导致 KASLR 泄漏和内核入侵。
尽管利用此零点击 TCP/IP RCE 漏洞的复杂性很高,但我们建议您在未使用 IPv6 的主机上禁用 IPv6。作为安全建议的一部分,BI.ZONE EDR会检测主机网络接口中启用的 IPv6 组件,并将发现的错误配置通知监控团队。此规则在本地 BI.ZONE EDR 版本和BI.ZONE TDR中均可用。
https://bi-zone.medium.com/breaking-down-cve-2024-38063-remote-exploitation-of-the-windows-kernel-bdae36f5f61d
原文始发于微信公众号(Ots安全):深入分析 CVE-2024–38063:远程利用 Windows 内核漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论