
Coverage-guided in-process fuzzer for iOS Bluetooth daemon (bluetoothd) using FRIDA, with over-the-air fuzzing for MagicPairing protocol and crash replay capabilities.

ToothPicker is an in-process, coverage-guided fuzzer for iOS. It was developed to
specifically targets iOS's Bluetooth daemon bluetoothd and to analyze various
Bluetooth protocols on iOS. As it is built using FRIDA, it can be
adapted to target any platform that runs FRIDA.
This repository also includes an over-the-air fuzzer with an exemplary implementation
to fuzz Apple's MagicPairing protocol using
InternalBlue. Additionally, it contains
the ReplayCrashFile.py script that can be used to verify crashes the in-process
fuzzer has found.
This is a very simple fuzzer that only flips bits and bytes in active connections. No coverage-guidance, no injection, but nice as a demo and stateful. Runs just with Python+Frida, no modules or installation required. Tested on iOS 13.5-14.3. See inplace-fuzzer.
The In-Process Fuzzer works out-of-the-box on various iOS versions (13.3-13.7 tested), but symbols need to be specified. Other iOS versions require adaptions to function addresses. Additionally, it seems like FRIDA's stalker has some problems with the iPhone 8. On newer iPhones that support PAC, the performance significantly suffers from signing pointers. Thus, it is recommended to run this on an iPhone 7.
ToothPicker is built on the codebase of
frizzer. However, it has been adapted for this
specific application as therefore not compatible with the original version anymore.
There exist plans to replace this with a more dedicated component in the future.
On the iPhone:
On Linux:
For Arch-based Linux:
# usbmuxd typically comes with libimobiledevice
# but just to be sure, we manually install it as well
sudo pacman -S usbmuxd libimobiledevice python-virtualenv radamsa
# Connect the iPhone to the computer
# Unlock it.
# If a pairing message pops up, click "Trust"
# If no pairing message pops up:
idevicepair pair
# Now there should be the pop up, accept and then again:
idevicepair pair
# In case of connection errors:
sudo systemctl restart usbmuxd
# or pair phone and computer again
# Other useful commands
# To ssh into the iPhone:
# Checkra1n comes with an SSH server listening on Port 44
# Proxy the phone's SSH port to 4444 localport:
iproxy 4444 44
# Connect:
ssh root@localhost -p 4444
# Default password: alpine
# To fetch some device information of the phone:
ideviceinfo
For Debian Linux:
Almost the same as above. Exceptions:
radamsa needs to be installed from the git repository because
it is not packaged.iproxy requires the additional package libusbmuxd-tools.For macOS:
Slightly different commands compared to the Arch Linux setup...
brew install libimobiledevice usbmuxd radamsa npm
idevicepair pair
npm install frida-compile
pip3 install frida-tools
On macOS, PacketLogger, which is part of the Additional Tools for Xcode, can decode various packets once the Bluetooth Debug Profile is installed. Moreover, if you open iOS crash logs with Xcode, it will add some symbols.
Setup:
frizzer.frizzer directory.projects directory contains an example project to fuzz the MagicPairing protocol.MagicPairing harness into one file.cd into the harness directory and install frida-compile.
Note that this needs to be run in that folder and can be directly installed as user by running npm install frida-compile.frida-compile ../projects/YOUR_PROJECT/YOUR_SPECIALIZED_HARNESS.JS -o ../projects/YOUR_PROJECT/harness.js.
As this was installed in npm context it might require running npx frida-compile instead.
Each time the harness changes, you need to rerun frida-compile.Fuzzing:
killall -9 bluetoothd to freshly start bluetoothd.cd back into your project's directory, create the crashlog-directory (mkdir crashes) and run ../../frizzer/fuzzer.py fuzz -p .In short, for starting a new project, run:
cd harness
npx frida-compile ../projects/YOUR_PROJECT/YOUR_SPECIALIZED_HARNESS.JS -o ../projects/YOUR_PROJECT/harness.js
cd ../projects/YOUR_PROJECT/
mkdir crashes
../../frizzer/fuzzer.py fuzz -p .
You can start with a different seed by using frizzer/fuzzer.py fuzz --seed 1234 -p ..
Adding new iOS versions:
Currently, different versions of iOS are defined in bluetoothd.js. You can find these with the Ghidra
versioning tool given an initial version that has all the required symbols. Note that some of them are not
named in the original iOS binary, so ideally start with one that was already annotated before.
Each time the bluetoothd.js changes, you need to re-run frida-compile.
Increasing bluetoothd capacities:
iOS crash logs are stored in Settings -> Privacy -> Analytics & Improvements -> Analytics Data.
If they contain bluetoothd crashes of the pattern bluetoothd.cpu_resource-*.ips this indicates
that the crash was caused due to exceeding resources. They can be increased as follows.
On an iPhone 7, run:
cd /System/Library/LaunchDaemons/
plistutil -i com.apple.jetsamproperties.D10.plist -o com.apple.jetsamproperties.D10.plist.txt
plistutil -i com.apple.jetsamproperties.D101.plist -o com.apple.jetsamproperties.D101.plist.txt
On iPhone SE2, these are in com.apple.jetsamproperties.D79.plist.:
cd /System/Library/LaunchDaemons/
plistutil -i com.apple.jetsamproperties.D79.plist -o com.apple.jetsamproperties.D79.plist.txt
Search for bluetoothd, update the priority to 19 (highest valid priority) and set the memory limit to something very high.
Apply the same changes to both files.
<dict>
<key>ActiveSoftMemoryLimit</key>
<integer>24000</integer>
<key>InactiveHardMemoryLimit</key>
<integer>24000</integer>
<key>EnablePressuredExit</key>
<false/>
<key>JetsamPriority</key>
<integer>19</integer>
</dict>
Write the changes back and restart bluetoothd.
plistutil -i com.apple.jetsamproperties.D10.plist.txt -o com.apple.jetsamproperties.D10.plist
plistutil -i com.apple.jetsamproperties.D101.plist.txt -o com.apple.jetsamproperties.D101.plist
killall -9 bluetoothd
Respectively on the iPhone SE2:
plistutil -i com.apple.jetsamproperties.D79.plist.txt -o com.apple.jetsamproperties.D79.plist
killall -9 bluetoothd