BROADCOM WI-FI芯片漏洞分析一


文章目录
  1. 1. 前言
  2. 2. 为什么研究Wi-Fi?
  3. 3. 读懂Poc需要哪些前提知识
    1. 3.1. JNI
    2. 3.2. Netlink Protocol Library Suite (libnl)
    3. 3.3. Netlink
    4. 3.4. cfg80211/nl80211
    5. 3.5. TDLS 协议
  4. 4. POC 的分析过程
    1. 4.1. NDK环境搭建
    2. 4.2. 编译POC
    3. 4.3. 测试POC
  5. 5. Wifi芯片代码分析
    1. 5.1. 怎么找到Wi-Fi固件?
    2. 5.2. 什么时候产生漏洞?
    3. 5.3. 定位固件中漏洞位置
  6. 6. 漏洞触发场景复现
  7. 7. 总结
  8. 8. 参考链接

前言

Android Wi-Fi驱动一直是众多安全研究员关注的重点,Android Wi-Fi驱动中曾经被发现大量root提权漏洞。 但这些漏洞都是存在于WEXT(Wireless-Extensions)接口中的,WEXT是一种即将被淘汰的Wi-Fi配置接口。取而代之的是基于nl80211协议的cfg80211接口。本篇文章主要介绍了基于nl80211协议的cfg80211怎么触发Wi-Fi芯片中的漏洞。以及相关漏洞的定位和分析。

为什么研究Wi-Fi?

Wi-Fi的使用已经与生活密不可分,Wi-Fi安全问题也应该受到更大的重视。在最近的一次移动安全峰会上,有一个议题是关于Wi-Fi芯片漏洞远程代码执行的。借此,重点研究了一下Wi-Fi芯片漏洞。

读懂Poc需要哪些前提知识

JNI

poc 是jni语言编写的,那什么是jni呢?

JNI全称为java native interface,Java本地开发接口,JNI是一个协议,这个协议可以用来沟通Java代码和本地的c/c++代码 让两者可以相互的调用
在poc的文件结构中我们可以看到一个jni文件都包含下面三个文件:
• Android.mk文件是在使用NDK编译C代码时必须的文件。Android.mk文件中描述了哪些C文件将被编译且指明了如何编译。
• Application.mk目的是描述在你的应用程序中所需要的模块(即静态库或动态库)。
• Pwn.c是触发漏洞的测试代码。

主要用来套接字的处理、发送和接收数据、消息的构造和解析。

Netlink协议是基于套接字的进程间通信(IPC)机制,它可用于用户空间进程和内核之间或者用户空间进程之间的通信。Netlink 协议基于 BSD 套接字并使用 AF_NETLINK 地址簇。每一个 Netlink协议都有自己的协议号(比如:NETLINK_ROUTE,NETLINK_NETFILTER,等等)。
它的寻址方案是基于 32 位的端口号(之前被称为 PID),这个端口号用来唯一的标识每一个对等通信节点。
-nl_send_auto_complete()消息和数据的发送函数
poc中主要使用nl_send_auto_complete函数来发送数据,这个是libnl封装好的。最终还是调用nl_send_auto() 函数来发送数据。参考一个翻译的官方文档:http://blog.guorongfei.com/2015/01/27/libnl-translation-part4/
-nla_put()数据封装函数
nla_put() 函数以 nla_reserve()函数为基础,只不过它还接收一个指向包含属性载荷的缓冲区的指针。这个函数会自动把数据从缓冲区拷贝到消息中去。会利用嵌套属性进行数据包封装,属性的嵌套是通过在代码前后分别调用nla_nest_start() 和 nla_nest_end() 来完成的。
nla_nest_start() 函数会在消息中添加一个没有实际载荷的属性头部,在此之后添加的数据都会成为容器属性的载荷部分直到调用 nla_nest_end()为止,它的调用“关闭”了容器属性并校正它的载荷长度以包含所有的数据长度。

cfg80211/nl80211

nl80211是供用户空间进程使用,操作利用cfg80211 API 开发无线网卡驱动。cfg80211是开发驱动的接口。

TDLS 协议

TDLS旨在提供一种不依赖AP的Wi-Fi网络上的对等通信方式。 TDLS是基于IEEE 802.11z标准。TDLS自动链接配置主要通过几个过程来完成
• TDLS Discovery过程
TDLS Discovery过程不是必要选项。一个TDLS STA设备可以选择直接发起TDLS建立过程。 TDLS Discovery 过程由一部STA设备通过AP或Go(群组拥有者) 向另外一部STA设备发送一个TDLS Discovery请求帧开始。如果目标设备也兼容TDLS,它将直接向该发送设备回复TDLS Discovery 响应帧,并提供有关设备本身的能力信息。包括所有支持速率及信道。TDLS Discovery 过程除了提供目标STA确实支援TDLS的相关能力信息外,TDLS Discovery过程的帧交互亦可以用作AP与目标TDLS STA设备的相对信号强度的测量。发起的STA能够判断与目标STA间的直接连接是否有利于提供有效信息。通过对比分別由目标STA设备与AP设备收到的信号强度,发起的STA设备可以评估建立直接连接是否会比通过AP发送数据包更为有效。
• TDLS Setup过程
TDLS Setup过程需要进行一系列的帧交换。发起设备首先发送一个TDLS传输请求,通过AP信道传输至目标设备。封装帧包括发送设备的性能信息。目标设备之后会回复TDLS Setup 建立响应,同样通过AP信道传送其性能信息,另外附加一个状态代码,表示接受或者拒绝该建立请求。如果接受Setup请求,发送设备将会通过AP传送一个TDLS 确认帧(Confirm Frame)。至此,两部设备之间开始进行直接通讯。
• TDLS Teardown过程
发送方或接受方设备均可向另外一方直接发送TDLS拆解帧(Teardown Frame),而如果未处于讯号范围內,该帧则可以通过AP信道传输。

POC 的分析过程

NDK环境搭建

网上有很多参考,这里就不再赘述。

编译POC

在这次研究过程中,POC的编译过程花费了大量时间。主要是不了解libnl库的正确使用方法,然后找了很多资料都是安卓开发ndk的,没有怎么使用libnl库的。最后经过大量资料参考,终于找到了一种暂时可行的方法,在那篇博文中作者称目前android自身没有携带libnl库,所以如果用到libnl库就需要把libnl一起移植到安卓上。我们就根据他所描述的方法,从github上down下来了一份已经移植好的安卓平台的libnl库。
具体命令如下:

1
2
3
4
5
git clone https://github.com/dschuermann/libnl-3-android
cd libnl-3-android/lib
cd ../android_toolchain
gvim jni/Android.mk(前文有该文件的相关介绍)
gvim jni/Applcation.mk (前文有该文件的相关介绍)

在Android.mk的最后部分加上这一段:

1
include $(CLEAR_VARS) LOCAL_MODULE := pwn LOCAL_SRC_FILES := $(call list-all,$(LOCAL_PATH),pwn.c) LOCAL_SHARED_LIBRARIES := nl-3 nl-genl-3 include $(BUILD_EXECUTABLE)

修改好之后将pwn.c文件拷贝到lib文件就可以编译了。

1
2
cd ../../android_toolchain
ndk-build

编译好后将libs文件夹下的libnl-3.so,libnl-genl-3.so,pwn 利用adb命令将libs文件下的lib-3.so、lib-genl-3.so、pwn拷贝到已经root后的支持nl80211协议的测试机中,执行编译好的pwn就可以测试poc是否可用了。

测试POC

分别在华为、小米、nexus 6p手机上进行了poc的测试。poc具体执行情况在小米手机上执行所有poc都是no such file or directory 。在华为手机上发送数据后的返回值为success。nexus手机测试结果也是success。 因为没有过wifi漏洞分析经验,不知道触发漏洞后正确的返回值应该是什么?所以接下来要透彻了解漏洞是否触发还需要大量的固件分析,漏洞定位,利用漏洞复现工作。

Wifi芯片代码分析

经过一系列的各种折腾,终于开始慢慢切入正题。分析的芯片是nexus 6p 6.0 版本。

怎么找到Wi-Fi固件?

要分析漏洞的成因,需要找到Wi-Fi芯片中与应用层通信部分代码。那接下来要怎么做才能将手机中的固件代码导出来呢?
第一步:我们需要了解Wi-Fi芯片在安卓系统内存中的加载位置。关于这个问题我们参考了Project Zero 的博客中分析的有关Wi-Fi芯片架构的知识,在Broadcom Wi-Fi芯片组相关的数据手册中,ARM内核具有用于保存固件代码的640KB ROM,以及用于数据处理(例如堆)和存储固件代码补丁的768KB RAM。

RAM的位置可以通过读取主机驱动程序中的初始化代码,找到包含RAM内容的文件是上图所示fw_bcmdhd.bin文件(实际上,通过驱动程序的代码,我们找到了BCMDHD_FW_PATH配置,其用于表示驱动程序将内容上传到RAM的文件的位置。)

ROM转存则可以通过Broadcom提供的一个非常强大的命令行实用程序dhdutil,可用于通过bcmdhd驱动程序与芯片进行交互。命令: ./dhdutil -i wlan0 membytes r 0x0 0xA0000 > /sdcard/rom.bin

什么时候产生漏洞?

利用上述方法转存出来bin文件之后,我们首先要了解bug产生的原因,才能下一步更好的定位漏洞位置。根据Project Zero提供的技巧,Wi-Fi管理帧以小的“标记”数据块(称为信息元素(IE))对大多数信息进行编码,传输的大部分信息也是利用IE进行编码的,所以这应该是我们逆向分析的一个好的出发点,有数据交互才可能出现漏洞。分析了漏洞利用可行性之后确定了在进行TDLS连接过程中会触发漏洞。有关TDLS前文中有描述。

定位固件中漏洞位置

根据Project Zero的提示在brcmsmac驱动程序可以找到Broadcom是使用一个函数从bcm_parse_tlvs帧提取IE。那接下来我们需要做的就是定位这个函数。搜索附近字符串提示我们首先定位到了bcm_parse_tlvs函数位置。
img
然后通过交叉参考分析最终找到漏洞函数,要探索该漏洞的成因,首先要知道TDLS建立确认帧的函数处理流程。这个函数首先会执行一些验证,以确保请求是合法的。其查询内部数据结构,以确保确实正在与请求对等体建立TDLS连接。然后,其验证Link-ID IE(通过检查其编码的BSSID与当前网络的匹配),并且还验证32字节的发起者随机数(“Snonce”)值(通过将其与存储的初始随机数进行比较)。 建立对请求可能确实是合法的一定程度的置信度后,该函数开始调用一个内部帮助函数,任务是计算MIC并确保其与编码在帧中的一致。固件还包括该函数的名称(“wlc_tdls_cal_mic_chk”)。我们先看MIC通过编码在握手帧中的计算。
img

我们对固件逆向后也定位到了这个函数,如下图所示,反编译后伪代码中提示信息可以看到在标记的第二段代码进行了IE长度的校验,后续的IE就没有校验了。因此,将RSN-IE的长度设置为较大的值将导致Timeout Interval和Fast Transition IE越界复制,从而溢出缓冲区。
img

接下来看断开连接时mic计算。
img

伪代码中的在断开帧时没有对FT-IE进行溢出校验,如果构造好FT-IE数据就能触发溢出。
img

漏洞触发场景复现

知道了漏洞是怎么产生的,接下来就是测试漏洞是否按照我们预期的那样能够触发。
首先,准备了同时支持TDLS 协议的TL-WN722N无线网卡,跟nexus 6p。
第二步,为了测试漏洞,需要修改wpa_supplicant,以使我们能发送包含过大FTIE的TDLS断开帧。查看wpa_supplicant的代码可快速识别负责生成和发送断开帧的函数wpa_tdls_send_teardown。通过对该函数添加一些小的更改(绿色),我们应该能够在收到断开帧时触发溢出,导致超写25个字节的0xAB,修改后make编译一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
{
...
ftie = (struct wpa_tdls_ftie *) pos;
ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
ftie->ie_len = 255;
os_memset(pos + 2, 0x00, ftie->ie_len);
os_memset(pos + ftie->ie_len + 2 - 0x19, 0xAB, 0x19); //Overflowing with 0xAB
os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
pos += ftie->ie_len + 2;
...
}

第三步,执行TDLS命令去触发。这种情况是可以触发漏洞的,但是呢并没有明显现象的原因是Broadcom堆实现背后的逻辑。深入分析分配算符的逻辑,我们发现其非常简单,其是一个简单的“最适合”分配算符,其执行向前和向后合并,并保持一个空闲块单链表。当分配块时,从最适合空闲块(足够大的最小块)的末端(最高地址)对其进行切取。在断开连接后,空闲列表中的其中一个块的大小突然异常大。回想一下,由于分配算符使用“最适合”,这意味着只要存在其他足够大的空闲块,后续分配将不会被放置在此块中。这也意味着固件不会崩溃,实际上会继续正常运行。如果我们不可视化堆的状态,我们就根本无法确定发生了什么事。

1
2
3
TDLS_DISCOVER – 发送“TDLS发现请求”帧并列出响应
TDLS_SETUP - 建立与具有给定MAC地址的对等体的TDLS连接
TDLS_TEARDOWN - 断开与具有给定MAC地址的对等体的TDLS连接

总结

最近两周的分析过程中,确实遇到了很多问题,比如最开始编译poc的过程中对libnl库不会用,花了很长时间,最终的可行方法也不知道是不是最适合的。poc代码中使用了内核通信netlink协议,之前并没有接触过,这次也只是花了两天时间粗略看了poc中用到的函数。有关netlink相关知识后续还要深入学习。接下来编译测试poc之后,poc测试返回success但是并没有明显的触发漏洞现象。
基于此,参考了Project Zero的博客复现了CVE-2017-0561漏洞,整个过程从Wi-Fi芯片固件的知识开始了解,到怎么定位漏洞位置,怎么找到漏洞,怎么利用漏洞触发漏洞。这个过程也学到了很多知识。比如,Wi-Fi帧格式、TDLS协议、Broadcom堆实现、支持TDLS协议的wpa_supplicant开源工具等这些的理解。接下来要想利用漏洞就需要深入的去理解Wi-Fi的通信过程,netlink协议,以及动态的分析过程。

参考链接

1、 http://bobao.360.cn/learning/detail/3742.html
2、 https://googleprojectzero.blogspot.jp/2017/04/over-air-exploiting-broadcoms-wi-fi_4.html