微软最新 WiFi 远程代码执行漏洞(CVE-2024-30078)探究
一、背景介绍
微软在六月的安全更新中,修复了一个 WiFi 驱动的漏洞。漏洞描述的攻击场景吸引了很多人的注意:在近源的场景下,攻击者通过发送 WiFi 数据包来发动攻击。
这篇漏洞公告一石激起千层浪:
有人声称以 5000 美元出售漏洞的 PoC
国内技术论坛上对漏洞的讨论和猜测
以及 github 上流传的各种假 PoC。
它一开始声称漏洞原因是 SSID 溢出,直到广大网友在 issues 中质疑无法复现,才承认自己只是在尝试复现漏洞,并删光了之前所谓对漏洞原理的描述。
从直觉上讲,这个漏洞危害不会很大。漏洞作者是赛博昆仑 @XiaoWei___,如果特别好用在这个时间点可能就不会提交了。至于出售 PoC 可信度也不高,5000 美元也过于便宜了。
漏洞作者的推特上有一些公开的信息,表明这是一个越界写漏洞。漏洞的发现受到了古河 2023 年 blackhat 议题的启发。这个议题是关于 windows 对各种底层网络协议处理上的漏洞。
考虑要过去出现过类似场景下的漏洞,例如 p0 团队从苹果的 AWDL 协议(基于 WiFi)中发现的漏洞,可以做到 0click 通过 WiFi 黑掉 iphone。因此我们希望弄清楚,漏洞的影响具体如何,是否有大家猜测的那样危害巨大。
二、静态分析
2.1 定位补丁
首先需要定位到 patch 的位置。在打上 win10 的 6 月更新补丁后,我们发现 win10 的 C:\Windows\System32\Drivers\目录下有以下文件的更新:
从文件名上看,nwifi.sys 和 wdiwifi.sys 比较可疑
经过 bindiff 的处理,wdiwifi.sys 新旧版本没有明显的差异,而 nwifi.sys 有个疑似打过补丁的函数,Dot11Translate80211ToEthernetNdisPacket
后续漏洞作者也在回复中确认,漏洞位于 nwifi.sys
2.2 什么是系统
从函数的名称 Dt11Translate80211ToEthernetNdisPacket 可以推测,这个函数用于处理 802.11 数据包。802.11 是一种无线局域网的标准,而 WiFi 是 802.11 标准的一种产品实现。用 IDA 反编译函数,发现补丁是增加了一处对数值的比较,如果不满足条件则返回 NDIS_STATUS_INVALID_PACKET(0xc001000),即数据包非法。
调用层次图表明,函数会在接收到 802.11 数据包时被调用。上层函数的名称中,STA 即 Station,AP 即 Access Point。Station 是无线网络中的客户端设备,而 Access Point 则是提供无线接入服务的网络基础设施设备。用户通过 Station 设备连接到 Access Point,进而访问网络资源。
我们预期受害者应该作为 Station,进而关注上层的 ExtSTAReceivePacket 函数。在其中可以看到对多种类型 802.11 数据包的处理,包括 DataPacket、ProbeRequest 和 Beacon
Beacon 和 ProbeRequest 都属于 802.11 的管理帧:
Beacon 是 WiFi 服务端 AP 定期广播的数据包,其主要目的是宣告该 AP 的存在以及网络的基本信息。WiFi 客户端会监听这些 Beacon 帧,来发现可用的网络;
ProbeRequest 是由 WiFi 客户端 Station 发起的,用于主动搜索特定的无线网络。如果存在匹配的网络,则该 AP 将回应一个 Probe Response 帧,包含网络的具体配置信息。
在 802.11 的 header 中,管理帧的 Type 值为 00,控制帧的 Type 值为 01,数据帧的 Type 值为 10。观察函数 ExtSTAReceiveDataPacket 所在分支的进入条件 v19 & 0xC == 0x8,即 v19 & 0b1100 == 0b1000。所以想要进入 Patch 的代码,需要数据包为数据帧,而非管理帧。因此一些假 PoC 所谓 Beacon 中 SSID 溢出,明显是错误的。
接下来的部分难以通过静态分析看出来,需要进行动态调试。
三、动态调试
3.1 环境搭建
准备一个 usb 无线网卡,以及两台 windows 虚拟机
在 vmware 中设置 debugee 连接 usb 网卡
为两台虚拟机设置同一个 host-only 网络,以进行双机内核调试,具体可参考教程。
3.2 主要问题
根据 patch 的代码,现在有两个需要解决的问题:
满足 v14==0x81,才能进入补丁的逻辑,那么 v14 是数据包中的哪个字段,0x81 又是什么含义;
patch 处比较的两个值又是什么,注意这是一个纯数值上的比较,v27 - *(a+12) >= 12。
3.3 问题 1. 0x81 是什么
对于加密的 WiFi,其数据帧中的数据是被加密的。
不过在进入 ExtSTAReceiveDataPacket 函数时,Data 的内容已经被解密。动态调试发现,v8 指向解密后的数据包,v7 为 802.11 数据头的长度,因此 &v8[v7]即跳过了 header,指向解密后 Data 数据。
将数据从内存中导出,放到 wireshark 中进行分析。可以看出 v14 就是取出 Logical-Link Control 层中,Type 的值。
LLC 是数据链路层的网络协议,通常占用 8 个字节。
实际调试时,v14 的值不是 0x8 就是 0xdd86
0x8 -> 0x0800 表示 ipv4
0xdd86 -> 0x86dd 表示 ipv6
而关键的判断条件,0x81 -> 0x8100 则表示 VLAN-tagged frame
如果类型标识是 VLAN 的话,LLC 的 8 字节后还要带上一个 4 字节的 802.1Q Header,用来标识 VLAN ID。
而 v14 == 0x81 后的代码,就是在处理这 4 个字节 (&v8[v7+8], &v8[v7+10])。
其实这里已经可以看出问题了,先卖个关子,不知道读者能不能反应过来。提示:不要被“越界写漏洞”这个概念先入为主。
3.4 问题 2. 比较双方的值是什么
回顾 patch 的内容,大概逻辑是判断 x < y+12
其实在 patch 之前,还有一处比较,比较逻辑是 x < y+8
经过测试,我们发现 y 是 802.11 header 的长度,而 x 是整个数据包的大小 (包括 802.11 header)。现在答案已经呼之欲出了:正常来说 LLC 的大小为 8 字节,但是如果 LLC 中 Type 为 VLAN,后面还要跟上 4 字节的内容。然而如果整个数据包只有 802.11 header 和 LLC,且 LLC 长度为 8、Type 为 VLAN,则在未打补丁的版本上会越界读 4 个字节!
这么看来两处比较就非常合理:
total_len >= 80211_header_len+8
接下来要处理 8 字节的 LLC 数据,所以这 8 个字节必须存在
(patch) total_len >= 80211_header_len+12
接下来要处理 12 字节的 LLC 数据,所以这 12 个字节必须存在
3.5 越界写
漏洞的直接原因清楚后,既然漏洞作者表明这是一个越界写,则在后续的代码中自然有越界写的位置。这个 Patch 函数的名字是 Dot11Translate80211ToEthernetNdisPacket,说明它是一个转换函数。但问题是这个转换的过程没有发生在新的 buffer,而是直接修改原 buffer。注意变量 v15,最终指向了 802.11header 的倒数第 6 个字节。在 line113/114/115 发生了对原 buffer 的修改。
直接说结论,在最理想的情况下,会在原 buffer 中写 12 字节的数据,分别是 802.11header 中,帧接收端 mac 地址以及帧发送端的 mac 地址。为了节约空间,正常就是覆盖 802.11header 结尾的 6 个字节,以及 LLC 的 6 个字节。至于为什么保留了 2 个字节,因为要保留 EtherType 的信息。
但是如果 LLC 类型为 VLAN,程序就会默认 LLC 有 12 字节,给 v15 加上 4。于是便覆盖 LLC 开始的 6+4=10 个字节。但是在上述恶意的数据包中,LLC 长度只有 8,这样就会在堆上越界写 2 个字节,且这两个字节为发送端 mac 地址的后两位,是可控的。
例如图中数据包原长度为 0x22,经过函数处理后,越界写了 2 个字节。22:0b 即发送者 mac 地址的后两位(c2:38:97:bd:22:0b)。
但是有一个限制,越界写的触发并不稳定,需要缓冲区后的 4 个字节满足一定的条件:
*(WORD)(buf_end+0) 的数值有一定的要求,否则会直接退出;因为这个位置期望的数值包含 VLAN ID,VLAN ID 有 0 – 4095 的范围限制;
*(WORD)(buf_end+2) 的数值必须大于等于 6;因为这个位置期望的数值是 EtherType。
这些使得整个漏洞更难以利用。
3.6 动态复现
购买一个 kali linux 上可以开启 monitor mode 的无线网卡,有一个简单进行动态复现的方式:
1)开启 1 个未加密的 WIFI,比如手机热点;
2)debugee 连上这个 WIFI;
3)kali 进入 monitor mode 抓包,抓取一个从 WIFI AP 到 debugee 的数据包(data frame);
4)将这个数据包修改成期望的数据,在 kali 上使用 scapy(python 三方库)重放修改后的数据。
注意:保证开启 monitor mode 的无线网卡,和 WIFI AP 位于同一 channel 下
四、总体回顾
4.1 漏洞效果
攻击者发送恶意的 WiFi 数据包;受害者在处理这个数据包时,在一定的条件下会触发一个堆上的越界写,写入的两个字节攻击者可控。
4.2 利用场景
1)攻击者作为 AP,诱导受害者连上该 WiFi,之后发动攻击
注意:不存在用户仅仅是开启 WiFi 就被攻击的情况
2)攻击者和受害者连上同一 WiFi,之后发动攻击
4.3 局限性
1)内存洞利用在场景 2 下可能较为困难:
一些路由器可能会直接丢弃这种畸形数据包,或是丢弃未知源 mac 的数据包;
连上 WiFi 的设备普遍会有大量的数据包交互,可能会影响漏洞利用时对堆的控制;场景 1 下实战 RCE 的可能更大,因为作为 AP 可以严格控制发送给受害者的数据包,一定程度上降低对内存布局的干扰。
2)受害者必须要主动连上 WiFi,不存在什么都不操作,只要开启 wifi 功能就会中招的情况:
WiFi 客户端会自动忽略来自不同 WiFi 的数据帧,即使该 WiFi 没有加密。这是由网卡厂商开发的驱动决定的。如果能自动监听数据帧等于有窃听的功能,有法律上的风险。
除非网卡进入“监视模式”,才会接收和处理周围所有 WiFi 网络中的数据帧,而不是直接过滤掉。但是 Windows 上网卡驱动一般不支持开启监视模式,即使支持也需要用户主动去切换(netsh wlan show wirelesscapabilities 查看)。
所以正常用户必须要连上 WiFi 才可能中招;
3)采用较新的 WDI 驱动模型编写的网卡驱动,不会受到漏洞影响:
无线网卡的驱动都会与 ndis.sys 交互。ndis.sys 是 Windows 操作系统中的网络驱动接口规范(Network Driver Interface Specification, NDIS)的核心驱动程序。
如果驱动使用的是传统 NDIS 模型,则 ndis.sys 最终会调用 nwifi.sys 驱动模块的功能;而如果驱动是采用 WDI 驱动模型,则 ndis.sys 最终会调用 wdiwifi.sys 驱动模块的功能。nwifi.sys 和 wdiwifi.sys 都有对 802.11 相关协议的解析,后者只支持 win10 及以上的版本。
例如我测试的 tx-ac88 网卡的驱动,采用了 WDI 驱动模型,不会受到漏洞的影响。
而 UGREEN-AX300 为了兼容 win7 使用了传统 NDIS 模型,才能触发到漏洞位置。
所以这个漏洞确实有一定的危害性,但是没有大家猜测的那么危险。直观的缓解措施就是不要随便连 WiFi。同时这个漏洞也能给我们带来很多启发,近源场景下的漏洞值得被关注。
最后,由于笔者水平有限,文章难免有错误或疏漏的地方,欢迎各位大佬交流指正。
参考链接
[01] 漏洞描述
https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-30078
[02] 古河 2023 年 blackhat 议题
[03] 设置 host-only 网络进行内核调试教程
[04] P0 团队从苹果 AWDL 协议(基于 WiFi)中发现的漏洞
https://googleprojectzero.blogspot.com/2020/12/an-ios-zero-click-radio-proximity.html
版权声明: 本文为 InfoQ 作者【阿里技术】的原创文章。
原文链接:【http://xie.infoq.cn/article/363cd3251e83708ef701e3e4b】。文章转载请联系作者。
评论