【蓝牙】CVE-2018-9381 gatts_process_read_by_type_req未初始化栈变量导致信息泄露

补丁

  • https://android.googlesource.com/platform/system/bt/+/0519f6aa5345be0917ad52188479230148adf8bd

补丁初始化了两个变量,并且判断了传入的参数len,所以我们判断这里可能是len长度可以由攻击者控制,且后续直接使用了未初始化的栈变量

DO NOT MERGE Initialize local variable in gatts_process_read_by_type_req

Bug: 73125709
Test: manual
Change-Id: I8b3346f605e0820385ea5ed7401bbee664fd15aa
(cherry picked from commit 0e34139d7fa338df6c99aaba13eb839a3dbc2548)
diff --git a/stack/gatt/gatt_sr.cc b/stack/gatt/gatt_sr.cc
index 06c9c52..f9e8f53 100644
--- a/stack/gatt/gatt_sr.cc
+++ b/stack/gatt/gatt_sr.cc
@@ -788,7 +788,8 @@
 void gatts_process_read_by_type_req(tGATT_TCB& tcb, uint8_t op_code,
                                     uint16_t len, uint8_t* p_data) {
   tBT_UUID uuid;
-  uint16_t s_hdl, e_hdl, err_hdl = 0;
+  uint16_t s_hdl = 0, e_hdl = 0, err_hdl = 0;
+  if (len < 4) android_errorWriteLog(0x534e4554, "73125709");
   tGATT_STATUS reason =
       gatts_validate_packet_format(op_code, len, p_data, &uuid, s_hdl, e_hdl);

通过源码来看整个过程,GATT的初始化函数

注意其中的gatt_le_data_ind,建立连接后,当L2CAP收到数据,就会调用这个函数

该函数的注释,这里在根据地址获取CCB

关键调用gatt_data_process(),注释里说,当我们是被连接的一端,那么我们就是ATT Server,所以,我们要处理来自Client的请求

switch判断op_code,进入GATT_REQ_READ_BY_TYPE,调用gatts_process_read_by_type_req()

结合补丁,我们可以猜测这里很有可能是未对栈变量做初始化,后面直接使用了,此时栈变量的值就是栈里原来的数据,我们跟入第一个函数gatts_validate_packet_format(),这里传入了s_hdle_hdl

注意参数,使用的是引用的方法,所以这里的变量修改就会影响上层传入的变量

跟入read_handles(),问题出现了,当len的长度小于4,就返回GATT_INVALID_PDU,此时没有对s_hdle_hdl进行赋值操作

返回上层函数,因为ret的值是GATT_INVALID_PDU,所以继续返回上层函数

回到了打补丁的函数,因为reasonGATT_INVALID_PDU,所以会调用gatt_send_error_rsp()返回错误信息给连接者(master)

这个函数返回一个错误的消息,从我们上面的分析来看,第四个参数uinit_t handle对应着传入的s_hdl

PoC如下,首先进行连接,send_trigger_req()里给p赋值为GATT_REQ_READ_BY_TYPE也就是0x08,赋值完后进行发送,此时发生错误,未初始化的栈变量被赋值到msg.error.handle,错误信息返回后被recv_and_leak()捕获,造成信息泄露,在recv_and_leak()里,因为cmd_codereason都是uint8_t类型,所以需要跳过两字节才是handle,同时handleuint16_t类型,所以使用STREAM_TO_UINT16读取

再来回顾下补丁,首先初始化栈变量s_hdle_hdl,避免后续继续出现同样的问题,同时判断传入的buffer长度,不能小于4

其它地方估计还会有不少这样的漏洞

Last updated