switch (cmd_code) {
case L2CAP_CMD_REJECT:
+ if (p + 2 > p_next_cmd) {
+ android_errorWriteLog(0x534e4554, "74202041");
+ return;
+ }
/*******************************************************************************
*
* 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);
/*******************************************************************************
*
* 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);
}
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;
}
}
}