Android蓝牙子系统“BlueFrag”误差剖析(CVE-2020-0022)

宣布时间 2020-02-13

一、误差配景


2020年2月 ,Android清静通告中披露并修复了一个严重误差 ,误差编号为CVE-2020-0022 ,又称BlueFrag ,可影响Android蓝牙子系统 。该误差是一个远程代码执行误差 ,泛起在Bluedroid蓝牙协议栈的HCI层 ,当无线�?榇τ谠硕刺� ,攻击者可以使用蓝牙守护程序提升权限进而在装备上执行代码 。该误差影响Android Oreo(8.0和8.1)、Pie(9) ,但无法在Android 10上举行使用 ,仅能触发DoS攻击 。


二、协议简介


2.1 HCI


HCI 层位于蓝牙协议栈高层协媾和低层协议之间 ,提供了对基带控制器和链路管理器的下令以及会见蓝牙硬件的统一接口要领 ,其接口适用于BR/EDR控制器、BR/EDR/LE控制器、LE控制器、AMP控制器 ,与底层的结构关系如下图:


尊龙凯时 - 人生就是搏!


主机系统上的HCI驱动程序和控制器中的HCI层之间会保存中心层 , 这些中心层即是主机控制器传输层 ,这些传输层是透明的 ,只需完成传输数据的使命 ,不必清晰数据的详细名堂 。两个蓝牙装备点对点HCI层的交互历程如下图所示:


尊龙凯时 - 人生就是搏!


2.1.1 HCI包名堂


HCI通过包的方法来传送数据、下令和事务的 ,所有在主机和主机控制器之间的通讯都以包的形式举行 。包括每个下令的返回参数都通过特定的事务包来传输 。HCI有数据、下令和事务三种类型的包 。下令包COMMAND(0x01)只能从主机发往主机控制器 ,其中数据包是双向的 ,分为两类:ACL(0x02)、SCO(0x03) ,而事务包EVENT(0x04)始终是主机控制器发向主机的 。主机发出的大大都下令包都会触发主机控制器爆发响应的事务包作为响应 ,在传输历程中会有一个句柄 ,用于识别主机之间的逻辑通道和控制器 ,共有三种类型的句柄:毗连句柄、逻辑链路句柄和物理链路句柄 。


凭证需要 ,这里只先容ACL数据包名堂 ,ACL 数据用于主机和控制器之间的非同步数据交流 ,如播放音乐数据的数据包 ,名堂如下图:


尊龙凯时 - 人生就是搏!


每个字段的说明如下所示:


字段

说明

Handle

Connection_Handle用于在主控制器上传输数据包或段 。

PB  Flag

包界线和顺应规模 。

BC  Flag

广播标记 。

Data  Total Length

以八位位组为单位的数据长度 ,包括高层协议data 。

其中 ,PB Flag的形貌如下:


尊龙凯时 - 人生就是搏!


设置为 00'b 的时间 ,代表 Host -> Contoller 的 L2CAP 的首包 。设置为 01’b 的时间 ,代表 Host -> Contoller 或者 Contoller -> Host 的 L2CAP 的续包(中心的) 。设置为 10'b 的时间 ,代表 Contoller -> Host 的 L2CAP 的首包 。


2.1.2 分段(Fragmentation)和重组(Reassembly )


分段是将PDU剖析成较小的部分 ,以便从L2CAP转达到较低层 。重组是凭证从下层转达来的片断重组PDU的历程 。分段和重组可以应用于任何L2CAP PDU 。


尊龙凯时 - 人生就是搏!


2.2 L2CAP数据包名堂


L2CAP是基于分组的 ,但也遵照信道传输的通讯模子 。L2CAP支持的信道有两种:面向毗连的信道和面向无毗连的信道 。在面向毗连的信道中 ,L2CAP数据包的名堂如下图所示 。


尊龙凯时 - 人生就是搏!


数据包中每个字段的说明如下所示:


字段

说明

Length

2字节 ,体现信息有用负载的巨细 ,不包括长度L2CAP头 。

Channel  ID(CID)

2字节 ,用于标识目的信道的终端 。通道ID的规模与正在发送数据包的装备相关 。

Information(Payload)

信息负载 。长度为0到65535字节 。


三、误差原理剖析


CVE-2020-0022误差位于HCI层 ,误差补丁代码位于hci/src/packet_fragmenter.cc(以8.1.0_r33为例)中的reassemble_and_dispatch()函数中 ,该函数是用于数据包分片的重组 。关于过长的ACL数据包需要举行包的重组 ,主要是凭证ACL包中的PB Flag标记位举行重组 ,若是目今是起始部分并且是不完整的 ,则天生一个部分包(partial_packet)放到map里 ,等下次收到它的后续部分举行拼装 ,拼装完毕后就分发出去 。详细剖析reassemble_and_dispatch()函数如下:


尊龙凯时 - 人生就是搏!


首先 ,处置惩罚第一个packet ,代码127行到129行 ,划分读取handle、acl_length和l2cap_length 。handle为本次链路的Connection_Handle 。凭证前文数据包名堂的先容 ,acl_length为Data Total Length ,该data数据域中存放着L2CAP数据包分片(也可能是一个完整的L2CAP数据包) 。然后 ,直接读取data中L2CAP Length ,该l2cap_length是一个完整的L2CAP数据包中payload的长度 。行131 ,校验packet包长度是否正常 。行133 ,通过handle获取boundary_flag ,即是PB Flag 。


尊龙凯时 - 人生就是搏!


行136 ,判断boundary_flag是否为2 ,二进制体现为10’b ,即判断目今packet是否为 Contoller -> Host 的 L2CAP 的首包 ,若是是 ,进入if语句 。行137到行147 ,判断目今packet是否已经被处置惩罚 ,包管本次处置惩罚的packet都是最新的 。行149到行154 ,判断L2CAP数据包长度是否正常 ,不正常直接报错返回 。


尊龙凯时 - 人生就是搏!


接下来 ,行156到行157 ,盘算full_length ,其中包括一个完整的L2CAP数据包中的payload的长度 ,一个L2CAP头部长度和一个HCI头部长度 。行161到行168 ,判断full_length是否凌驾BT_DEFAULT_BUFFER_SIZE ,若是凌驾直接报错返回 。行170到行178 ,判断目今头包packet是否尚有续包 ,若是没有续包直接挪用callbacks->reassembled处置惩罚目今packet并返回 。


尊龙凯时 - 人生就是搏!


若是目今头包packet后面尚有续包 ,那就最先重新分派一块新的内存用于packet中数据包重组 。行180到184 ,分派并设置partial_packet ,将partial_packet->len设置为full_length ,将partial_packet->offset设置为packet->len即目今头包packet->data的长度 。行186 ,挪用memcpy ,将头包packet中HCI数据包整体拷贝到partial_packet中 。行189到行191 ,先找到HCI数据包头部 ,并跳过handle ,更新acl_length为一个完整的L2CAP数据包长度 。行193 ,将partial_packet存放到容器中 。行196 ,释放目今头包packet ,体现已经处置惩罚完第一个packet ,不再需要它了 。行197 ,else语句最先处置惩罚后续packet ,即boundary_flag不即是2的packet 。


尊龙凯时 - 人生就是搏!


行198到行205 ,首先通过handle判断目今后续packet是否属于本次链路的 ,若是不属于 ,直接返回 。行206 ,获取前一轮天生的partial_packet 。行208 ,将目今后续packet->offset赋值为HCI_ACL_PREAMBLE_SIZE即4字节 ,此时packet->offset指向HCI包中的data域 ,内里存放着L2CAP数据包分片 。行209和行210 ,盘算projected_offset ,projected_offset为partial_packet->offset与本次L2CAP数据包分片的长度之和 。


尊龙凯时 - 人生就是搏!


行211和行219 ,判断projected_offset是否大于partial_packet->len,即判断projected_offset是否大于full_length 。若是大于 ,则修改packet->len为partial_packet->len减去partial_packet->offset ,即packet->len为partial_packet剩余空间的长度 。然后 ,将projected_offset设置为partial_packet->len 。详细数据包重组如下图所示:


尊龙凯时 - 人生就是搏!


修正好现实要拷贝的长度后 ,行221 ,挪用memcpy举行拷贝 ,误差点到了,第一个参数为partial_packet->data + partial_packet->offset,目的地址是准确的 ,第二个参数为packet->data + packet->offset ,源地址也是准确的 ,第三个参数是要拷贝的长度len为packet->len - packet->offset ,这个值是有问题的 ,分两种情形 。第一种情形是projected_offset小于partial_packet->len ,packet->len - packet->offset为L2CAP数据包片断总长度 ,并且是个正数 。第二种是行211的情形 ,packet->len已经被修正过 ,不需要再一次packet->len - packet->offset的操作 ,若是partial_packet剩余空间长度小于4字节 ,那packet->len - packet->offset 是小于零的 ,是一个负数 。由于memcpy()函数第三个参数类型是一个无符号整型类型 ,因此整数溢出导致堆溢出 。误差补丁如下:


尊龙凯时 - 人生就是搏!

可以看到 ,补丁代码中将packet->len加上了一个packet->offset ,用于后面抵消减packet->offset的操作 。


四、影响版本


Android Oreo(8.0和8.1)

Android Pie(9)

Android 10


五、清静建议


尽快更新最新的Android清静补丁

仅在绝对须要时启用蓝牙

坚持蓝牙装备不可发明


参考信息:

1.https://insinuator.net/2020/02/critical-bluetooth-vulnerability-in-android-cve-2020-0022/

2.https://akhozo.blogspot.com/2020/02/critical-android-bluetooth-flaw-cve.html?spref=tw

3.https://android.googlesource.com/platform/system/bt/+/3cb7149d8fed2d7d77ceaa95bf845224c4db3baf%5E%21/#F0

4.https://source.android.com/security/bulletin/2020-02-01.html

5.http://androidxref.com/8.1.0_r33/xref/system/bt/hci/src/packet_fragmenter.cc

6.Bluetooth_Core_v4.2蓝牙官方文档