【蓝牙】CVE-2018-9358 信息泄露

补丁

  • https://android.googlesource.com/platform/system/bt/+/0d7c2f5a14d1055f3b4f69035451c66bf8f1b08e

补丁里补了两个函数:gatt_process_exec_write_req()gatts_process_read_req()

DO NOT MERGE Handle bad packet length in gatts_process_read_req

Added error check and handling code in gatts_process_read_req to
make sure that the packet length is correct.
Please note that there is another earlier CL that is reverted and this
is the updated one.

Bug: 73172115
Test: Run the test program, poc, that was attached in the bug report
Merged-In: Ia9b4e502fa8f8384bf9767e68f73b48a0915141b
Change-Id: Ia9b4e502fa8f8384bf9767e68f73b48a0915141b
(cherry picked from commit cc9c7330d1c3507d745170ae7b2e0546197b7acb)
(cherry picked from commit 16f4c21be5bd0ea1968eee8a0f00648b1e326253)
diff --git a/stack/gatt/gatt_sr.cc b/stack/gatt/gatt_sr.cc
index 3af9866..06c9c52 100644
--- a/stack/gatt/gatt_sr.cc
+++ b/stack/gatt/gatt_sr.cc
@@ -22,6 +22,7 @@
  *
  ******************************************************************************/
 
+#include <log/log.h>
 #include "bt_target.h"
 #include "bt_utils.h"
 #include "osi/include/osi.h"
@@ -281,8 +282,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_exec_write_req(tGATT_TCB& tcb, uint8_t op_code,
-                                 UNUSED_ATTR uint16_t len, uint8_t* p_data) {
+void gatt_process_exec_write_req(tGATT_TCB& tcb, uint8_t op_code, uint16_t len,
+                                 uint8_t* p_data) {
   uint8_t *p = p_data, flag, i = 0;
   uint32_t trans_id = 0;
   tGATT_IF gatt_if;
@@ -301,6 +302,13 @@
   }
 #endif
 
+  if (len < sizeof(flag)) {
+    android_errorWriteLog(0x534e4554, "73172115");
+    LOG(ERROR) << __func__ << "invalid length";
+    gatt_send_error_rsp(tcb, GATT_INVALID_PDU, GATT_REQ_EXEC_WRITE, 0, false);
+    return;
+  }
+
   STREAM_TO_UINT8(flag, p);
 
   /* mask the flag */
@@ -940,9 +948,19 @@
  */
 static void gatts_process_read_req(tGATT_TCB& tcb, tGATT_SRV_LIST_ELEM& el,
                                    uint8_t op_code, uint16_t handle,
-                                   UNUSED_ATTR uint16_t len, uint8_t* p_data) {
+                                   uint16_t len, uint8_t* p_data) {
   size_t buf_len = sizeof(BT_HDR) + tcb.payload_size + L2CAP_MIN_OFFSET;
   uint16_t offset = 0;
+
+  if (op_code == GATT_REQ_READ_BLOB && len < sizeof(uint16_t)) {
+    /* Error: packet length is too short */
+    LOG(ERROR) << __func__ << ": packet length=" << len
+               << " too short. min=" << sizeof(uint16_t);
+    android_errorWriteWithInfoLog(0x534e4554, "73172115", -1, NULL, 0);
+    gatt_send_error_rsp(tcb, GATT_INVALID_PDU, op_code, 0, false);
+    return;
+  }
+
   BT_HDR* p_msg = (BT_HDR*)osi_calloc(buf_len);
 
   if (op_code == GATT_REQ_READ_BLOB) STREAM_TO_UINT16(offset, p_data);
@@ -964,7 +982,7 @@
   if (reason != GATT_SUCCESS) {
     osi_free(p_msg);
 
-    /* in theroy BUSY is not possible(should already been checked), protected
+    /* in theory BUSY is not possible(should already been checked), protected
      * check */
     if (reason != GATT_PENDING && reason != GATT_BUSY)
       gatt_send_error_rsp(tcb, reason, op_code, handle, false);

我们先来看gatt_process_exec_write_req(),先从变量p取1个字节,变量p来自参数p_data,我们找到上层函数判断参数是什么数据传入的

lenp_data来自上层

再往上找,可以看到msg_lenp分别对应着Buffer剩余长度和Buffer指针

所以再回到gatt_process_exec_write_req,在调用STREAM_TO_UINT8()进行取值的时候,未判断p指向的数据是否处于Buffer边界之内,读出的数据做一次&运算后赋值给gatts_data.exec_write,再调用gatt_sr_send_req_callback()发送回去,造成信息泄露

gatt_sr_send_req_callback()调用(*p_reg->app_cb.p_req_cb)(),这是函数指针调用

找到定义的结构体,我们可以看到p_req_cb是第五个

搜索对该结构体进行赋值的全局操作,幸运的找到了,第五个字段就是bta_gatts_send_request_cback()

有趣的是bta_gatts_send_request_cback()又调用了(*p_rcb->p_cback)()

继续找结构体,这个结构体的赋值最终没有找到

于是我想,函数(*p_rcb->p_cback)(req_type, &cb_data)的参数类型或许有希望

最后找到一个

第二处补丁对Buffer剩余长度做了判断,后面取了2字节的数据,所以需要判断Buffer剩余长度要大于等于2字节

获取的数据offset只传入了gatts_read_attr_value_by_handle(),且后续没有使用到,所以我们需要关注gatts_read_attr_value_by_handle()里对offset的操作

通过对gatts_read_attr_value_by_handle()进行分析,正常情况下返回的是GATT_SUCCESS,我们并不关心返回值,所以这里略过,其中会调用两个函数:read_attr_value()gatts_send_app_read_request()

read_attr_value()调用gatts_check_attr_readability(),然而gatts_check_attr_readability()没有使用到offset

来看另一个函数gatts_send_app_read_request(),该函数将offset赋值给sr_data.read_req.offset,最后调用gatt_sr_send_req_callback()

所以问题来了:gatt_sr_send_req_callback()到底在做什么?

PoC

Last updated