【蓝牙】CVE-2018-9365 smp_sm_event数组越界访问导致RCE

补丁

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

补丁描述修复了在smp_sm_event()里的非预期,判断了p_cb->role大于等于2则报错返回,我们猜测这里可能是字段p_cb->role可以由连接者控制

DO NOT MERGE Fix unexpected behavior in smp_sm_event

Bug: 74121126
Test: manual
Change-Id: Ie5dd841d6461ad057c4ab572007f38c5446aba53
(cherry picked from commit 652798b2f2d6c90e0fc95c00ccfb91e2870b03d4)
diff --git a/stack/smp/smp_main.cc b/stack/smp/smp_main.cc
index 829a5d4..49e2ece 100644
--- a/stack/smp/smp_main.cc
+++ b/stack/smp/smp_main.cc
@@ -18,6 +18,7 @@
 
 #include "bt_target.h"
 
+#include <cutils/log.h>
 #include <string.h>
 #include "smp_int.h"
 
@@ -954,6 +955,13 @@
   uint8_t curr_state = p_cb->state;
   tSMP_SM_TBL state_table;
   uint8_t action, entry, i;
+
+  if (p_cb->role >= 2) {
+    SMP_TRACE_DEBUG("Invalid role: %d", 1. );
+    android_errorWriteLog(0x534e4554, "74121126");
+    return;
+  }
+
   tSMP_ENTRY_TBL entry_table = smp_entry_table[p_cb->role];
 
   SMP_TRACE_EVENT("main smp_sm_event");

我们从头开始分析,当SMP Channel有来自L2CAP的数据时,会调用smp_data_received()进行数据的处理,这里我们的关注点在于cmdSMP_OPCODE_PAIRING_REQ 0x01时的情况,当传入的是SMP_OPCODE_PAIRING_REQ,会调用L2CA_GetBleConnRole()

其中第二个参数类型RawAddress的部分实现如下,可以看到其数据以及转换的方式

L2CA_GetBleConnRole()会先给role赋值为HCI_ROLE_UNKNOWN,这个值是0xff,之后进行遍历搜索,如果搜到了就将role赋值为p_lcp->link_role,如果没搜到就是初始化的值0xff

回到smp_data_received(),会调用到smp_sm_event()

通过查看该函数,我们可以注意到,后续会直接使用该字段作为下标对数组进行取值操作

我们通过源码找到smp_entry_table的定义,这个数组的长度只有2,结合前面的分析,smp_entry_table[p_cb->role]的取值可能是smp_entry_table[0x00]smp_entry_table[0x01]smp_entry_table[0xff],所以这里就出现了一个越界访问的漏洞

谷歌给这个漏洞的评级是Critical RCE,所以它是可以进行利用的

CVE
参考编号
类型
严重程度
已更新的 AOSP 版本

CVE-2018-9365

A-74121126

RCE

Critical

6.0、6.0.1、7.0、7.1.1、7.1.2、8.0、8.1

那我们如何让L2CA_GetBleConnRole()返回0xff呢?

继续分析L2CA_GetBleConnRole(),关键调用l2cu_find_lcb_by_bd_addr()

遍历l2cb.lcb_pool来搜索是否有匹配的地址,匹配有两个条件,一个是TRANSPORT匹配,另一个是地址匹配,如果TRANSPORT不匹配,就有可能出现找不到然后返回NULL的情况

传进来的是BT_TRANSPORT_LE,源码如下,所以我们可以使用BT_TRANSPORT_BR_EDR模式来使l2cu_find_lcb_by_bd_addr()返回NULL,从而L2CA_GetBleConnRole()就能返回0xff

PoC

其中BT_CID_SMP0x0006是用于指定Channel,之所以为0x0006我是通过找核心找到的,大佬果然诚不我欺

给buffer加上"\xff\xff\xff\xff\xff\xff"这一步,还是通过查找核心里面的配对请求包格式,发现整个包是七个字节,所以这里猜测是为了补全剩余字节

那么就剩最后一个问题:如何使用BT_TRANSPORT_BR_EDR模式呢?

关于如何使用BR/EDR,我查阅了一些资料

就在各种查阅资料的时候,我突然有了灵感,我现在不知道POC里哪里对应着BR/EDR模式,但是目前最有可能的就是local_l2_addr.l2_bdaddr_type = 0;,底层解析的时候,一定是.l2_bdaddr_type的形式进行取值,搜索一下BlueZ的源码

搜索BDADDR_BREDR,发现如下代码,所以,就是这个字段来控制BR/EDR,POC里设置local_l2_addr.l2_bdaddr_type = 0;,也就是BDADDR_BREDR,到此就已经可以完全理解PoC了

Reference

  • https://blog.quarkslab.com/a-story-about-three-bluetooth-vulnerabilities-in-android.html

  • https://paper.seebug.org/666/

Last updated