Android蓝牙子系统"BlueFrag"漏洞分析(CVE-2020-0022)

发布者:ADLab
发布于:2020-02-14 11:32
1、漏洞背景
2020年2月,Android安全公告中披露并修复了一个严重漏洞,漏洞编号为CVE-2020-0022,又称BlueFrag,可影响Android蓝牙子系统。该漏洞是一个远程代码执行漏洞,出现在Bluedroid蓝牙协议栈的HCI层,当无线模块处于活动状态时,攻击者可以利用蓝牙守护程序提升权限进而在设备上执行代码。该漏洞影响Android Oreo(8.0和8.1)、Pie(9),但无法在Android 10上进行利用,仅能触发DoS攻击。

2、协议简介
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 数据用于主机和控制器之间的非同步数据交换,如播放音乐数据的数据包,格式如下图:
 

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


   其中,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数据包的格式如下图所示。
 
数据包中每个字段的说明如下所示:


3、漏洞原理分析
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的操作。

4、影响版本
Android Oreo(8.0和8.1)
Android Pie(9)
Android 10

5、安全建议
尽快更新最新的Android安全补丁。
仅在绝对必要时启用蓝牙。
保持蓝牙设备不可发现。

6、参考信息
(1)481K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6A6L8Y4y4A6L8Y4g2S2N6r3!0J5i4K6u0W2L8X3g2@1i4K6u0r3x3U0l9J5x3q4)9J5c8U0l9J5i4K6u0r3j5%4u0A6N6r3W2U0j5h3I4Q4x3X3c8T1L8s2g2W2N6r3!0G2N6r3S2Q4x3X3c8$3N6h3I4F1k6i4u0S2j5X3W2D9K9i4c8&6i4K6u0V1K9h3&6Q4x3X3c8S2L8X3c8J5L8$3W2V1i4K6u0V1j5%4k6W2i4K6u0V1x3U0l9J5x3q4)9J5k6o6l9H3x3U0u0Q4x3V1j5`.
(2)bb1K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6S2K9$3S2G2P5X3!0Q4x3X3g2T1L8r3!0Y4M7%4m8G2N6q4)9J5k6h3y4G2L8g2)9J5c8U0t1H3x3U0m8Q4x3V1j5H3x3W2)9J5c8X3y4J5K9i4c8A6j5$3q4D9i4K6u0V1j5h3&6V1M7X3!0A6k6q4)9J5k6r3u0D9N6h3g2@1L8$3!0@1K9q4)9J5k6r3k6D9j5i4N6Q4x3X3c8U0N6X3g2Q4x3X3g2Z5N6r3#2D9i4K6y4r3M7%4m8J5k6h3k6Q4x3@1c8@1N6H3`.`.
(3)70dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6S2L8X3c8J5L8$3W2V1i4K6u0W2k6$3!0G2k6$3I4W2M7$3!0#2M7X3y4W2i4K6u0W2j5$3!0E0i4K6u0r3M7r3I4S2N6r3k6G2M7X3#2Q4x3V1k6K6P5i4y4@1k6h3#2Q4x3V1k6T1N6q4)9J5c8W2)9J5b7W2)9J5c8U0y4U0j5U0M7I4y4o6W2V1z5r3k6W2k6o6u0V1y4$3b7%4y4$3y4W2j5h3p5&6y4h3u0X3z5o6b7#2x3U0t1@1j5K6c8V1j5U0y4T1j5h3k6Q4x3U0f1#2c8g2)9J5y4e0t1I4i4K6u0r3i4K6t1K6c8U0l9`.
(4)839K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6L8%4g2J5j5$3g2Q4x3X3g2S2L8X3c8J5L8$3W2V1i4K6u0W2j5$3!0E0i4K6u0r3M7$3g2U0N6i4u0A6N6s2W2Q4x3V1k6T1N6h3I4D9k6i4c8A6L8W2)9J5c8U0t1H3x3U0m8Q4x3X3b7H3x3W2)9J5k6o6l9I4i4K6u0W2K9s2c8E0L8l9`.`.
(5)b05K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4F1k6s2u0G2K9h3c8^5M7X3g2X3i4K6u0W2j5$3!0E0i4K6u0r3z5q4)9J5k6e0q4Q4x3X3f1H3i4K6g2X3M7U0x3K6i4K6u0r3P5s2u0W2k6W2)9J5c8Y4y4&6M7%4c8W2L8g2)9J5c8X3u0@1i4K6u0r3K9r3y4A6i4K6u0r3M7%4u0U0i4K6u0r3M7r3q4U0K9$3g2@1i4K6g2X3k6Y4u0S2k6$3#2W2L8Y4c8W2M7W2)9J5k6h3y4U0
(6)Bluetooth_Core_v4.2蓝牙官方文档




声明:该文观点仅代表作者本人,转载请注明来自看雪