0x00 简介
在本月初,我们发布了三个内存破坏Bug(ZDI-20-494,ZDI-20-495和ZDI-20-496-统称为CVE-2020-0558),它影响了Intel双频无线适配器的两个Windows Wi-Fi驱动程序。根据厂商介绍,这些驱动程序适用于AC 7265 Rev D、AC 3168、AC 8265和AC8260无线适配器。ZDI-20-494和ZDI-20-496都是越界写漏洞,它们的根本原因是在nettw04.sys和nettw06.sys驱动程序中。ZDI-20-495是一个栈溢出漏洞,仅影响nettw06.sys驱动程序。这些漏洞是百度安全实验室的谢海阔和王颖发现的,最初是在2019年11月底向ZDI报告的。
可以在我们的GitHub上找到这三个漏洞的PoC,POC已经在基于Qualcomm Atheros AR9271的USB网络适配器对AC 3168和AC 8260进行了测试。
这三个漏洞都需要受害者启用“移动热点”功能。攻击者只需使用恶意的802.11 MAC帧连接到无线网络,无需热点密码,就可以利用这些漏洞。
0x01 ZDI-20-495
这个堆溢出漏洞存在于Netwtw06.sys驱动程序中。PoC向受害者移动热点发送四个802.11 MAC帧,触发受害者机器上的BSOD。漏洞的原因很简单。超长的SSID通过位于802.11关联请求帧主体内的TLV(Tag-length-value)编码元素标记的信息传递给一个易受攻击的函数。以下是由Scapy实用程序生成的恶意frame的剖析:
在上图中,我们可以看到ID为0x00的元素信息,它对应于SSID,长度为55个字节。接下来是SSID长字符串。
漏洞函数prvhPanClientSaveAssocResp()使用错误的DstSize参数通过memcpy_s()将SSID复制到固定长度的堆缓冲区中。它提供了攻击者提供的SSID长度,而不是目标缓冲区的大小。下面是来自驱动程序20.70.13.2版本的prvhPanClientSaveAssocResp()函数的反汇编代码片段。
在0x1403A7F5C处,r8指向SSID元素信息的头部。在0x1403A7F66处,攻击者提供的SSID长度(55)被传递给DstSize,该值随后也传递给MaxCount。以这种方式传递SSID长度会破坏memcpy_s()的安全性,这是这个漏洞的根本原因,如果进一步查看反汇编,我们可以看到var_4C只有36个字节长:
当memcpy_s()继续将攻击者控制的缓冲区复制到大小不足的栈缓冲区中的变量时,就会发生缓冲区溢出的情况。
0x02 ZDI-20-494和ZDI-20-496
由于这两个漏洞原因相同,所以我们只讨论ZDI-20-494。关联请求帧的处理中存在越界写漏洞。为了到达漏洞的代码处,攻击者必须先发送身份验证请求,然后再发送恶意关联请求。攻击者发送一个包含ID为59(0x3B)的元素信息的关联请求帧,该元素信息对应于支持的操作类。元素的值由221个空字节组成。请求的frame分析如下:
驱动程序调用两个函数来处理信息元素:prvPanCnctProcessAssocSupportedChannelList()和utilRegulatoryClassToChannelList()。prvPanCnctProcessAssocSupportedChannelList()尝试调用函数utilRegulatoryClassToChannelList()221次,与元素长度相对应。以下是来自nettw04.sys驱动程序19.51.23.1版本的prvPanCnctProcessAssocSupportedChannelList()函数的反汇编代码段:
在0x140388500处,ebx初始化为0。0x1403885AF处的循环退出条件将循环索引ebx与存储在eax寄存器中来自Prior[2]之前的4条指令的元素信息进行比较。在0x140388559处的循环中调用utilRegulatoryClassToChannelList()函数。函数的第三个参数是通过r8寄存器传递的内存缓冲区地址,该地址是受这个越界写漏洞影响的缓冲区地址。还要注意,在0x14088502处,缓冲区的第一个DWORD初始化为零。
utilRegulatoryClassToChannelList()函数的作用是从易受攻击的缓冲区中读取第一个DWORD作为索引,并将其用作偏移量,将0xFF字节的数据写入自身。这在每次调用函数时都会发生。由于缺少边界检查,所以当重复调用此函数时,索引可能指向超出缓冲区末端的内存区域。
在0x1400D06A8处,来自第三个参数的漏洞缓冲区被传输到rbx寄存器。在0x140D068F处,循环索引edi在进入循环之前初始化为0。这将重复0xFF次。在从0x140D0718开始的基础块中,缓冲区中的第一个DWORD被读取并存储在eax寄存器中。值立即被用作漏洞缓冲区的偏移量,并向其写入一个字节。在0x1004D0729处,漏洞缓冲区的第一个DWORD将递增。当调用utilRegulatoryClassToChannelList()函数两次以上时,会出现越界写。
0x03 总结
虽然触发这些漏洞的条件非常少,但是我们在程序中看到数据链路层中的漏洞仍然是非常有趣的。虽然有一些关于fuzzing的讨论,但是我们并没有看到很多关于基于wi-fi的漏洞分析。IEEE 802.11系列无线技术标准提供了一个巨大的攻击面,而漏洞研究者几乎还没有开始仔细研究这个协议。这个攻击向量中的一个非常好的驱动程序Bug可以直接访问内核。相比于基于Web浏览器的攻击,需要多个bug和沙箱逃逸,Wi-Fi攻击向量可能是攻击者考虑的一个有趣的替代向量。对于那些有兴趣学习更多IEEE 802.11系列标准的人来说,《802.11 Wireless Networks: The Definitive Guide written》by Matthew S. Gast 编写的权威指南是开始学习的一个很好的资源。