Peter Davies

Ramblings

Bluetooth reconnaissance on a Raspberry Pi

I wanted a cheap box I could leave in an office to answer two questions: what Bluetooth Low Energy devices are advertising here, and what do our own devices expose to anyone who connects without pairing. Fitness bands, smart locks, label printers, conference room kit and dev boards all advertise constantly, and plenty of them will let a stranger read or write GATT characteristics without any authentication at all.

A Raspberry Pi 3 or 4 has a Bluetooth 4.x/5 radio built in, which is enough for this. This was Raspbian Buster in October 2019, where Python 2 was still the default python.

Only point this at kit you own or have permission to test. Listening to advertisements is passive, but connecting to someone else's device and poking its characteristics is not.

Base install

Start from a fresh Raspbian image and bring it up to date:

sudo apt-get update && sudo apt-get dist-upgrade
sudo apt-get install bluez tshark python-pip python3-pip git \
    libxml2 libxml2-dev libxslt1-dev
sudo usermod -aG wireshark pi

Say yes when the tshark installer asks whether non-root users can capture. Log out and back in for the group change.

For scripting captures from Python I used pyshark-legacy (the Python 2 branch of pyshark) and pexpect to drive bluetoothctl:

pip install pyshark-legacy pexpect

Look before you touch

BlueZ's bluetoothctl is enough to see what's around:

bluetoothctl
[bluetooth]# power on
[bluetooth]# scan on
[bluetooth]# devices
[bluetooth]# info AA:BB:CC:DD:EE:FF

info shows the advertised name, the address type (public or random), the service UUIDs in the advertisement and any manufacturer data. The address type tells you a lot. A device that uses a random resolvable address and changes it every few minutes is doing the privacy feature properly. One that advertises a fixed public address and a name like "Meeting Room 3" can be tracked and identified by anyone walking past.

To keep a record, capture the raw HCI traffic. btmon writes a btsnoop file Wireshark can open:

sudo btmon -w survey.btsnoop

or capture the Bluetooth interface with tshark directly (check the interface name with tshark -D, it's usually bluetooth0):

tshark -i bluetooth0 -w survey.pcapng

In Wireshark, the display filter bthci_evt.code == 0x3e (LE meta events) narrows an HCI capture down to the advertising reports and connection events.

Connect to your own device

For a device you own, see what it exposes without pairing:

bluetoothctl
[bluetooth]# connect AA:BB:CC:DD:EE:FF
[AA:BB:CC:DD:EE:FF]# menu gatt
[AA:BB:CC:DD:EE:FF]# list-attributes
[AA:BB:CC:DD:EE:FF]# select-attribute /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service000c/char000d
[AA:BB:CC:DD:EE:FF]# read

The questions I ask of each device:

BLESuite

NCC Group's BLESuite wraps a lot of this in Python: scanning, GATT enumeration, reading and writing attributes, and replaying captured GATT traffic with ble-replay. It's built and tested on Debian, so it installs cleanly on Raspbian:

sudo apt-get install libbluetooth-dev libpython-dev python-sphinx \
    libzbar-dev libzbar0
pip install gevent pycrypto prettytable sphinx-rtd-theme

git clone https://github.com/nccgroup/BLESuite.git
cd BLESuite
pip install -r requirements.txt
cd docs && make html && cd ..
sudo python setup.py install

The README says to use the copy of Scapy bundled in the repository rather than the one from PyPI, so don't add pip install scapy on top. The built documentation ends up in docs/_build/html and is the best reference for the API.

The optional bdaddr module, which changes the adapter's own address, only works on some chipsets. If the build fails on it, comment out the ext_modules = [c_ext], line in setup.py and install again.

Android logs as a bonus

If the device has a phone app, the phone's own Bluetooth log shows exactly what the app sends. On Android, turn on "Enable Bluetooth HCI snoop log" in Developer options, use the app, then pull a bug report:

sudo apt-get install android-tools-adb
adb bugreport bugreport.zip

The btsnoop log is inside the zip (on recent Android versions under FS/data/misc/bluetooth/logs/) and opens in Wireshark. It's the quickest way to find which characteristic the app writes to unlock or configure the device.

Today: BLESuite is Python 2 only and hasn't been updated in years. On a current Raspberry Pi OS I'd stick with bluetoothctl and btmon for this, and use the bleak Python library for scripting.