---
title: "Bluetooth reconnaissance on a Raspberry Pi"
date: "2019-10-21"
path: "/197-bluetooth-reconnaissance-on-a-raspberry-pi.html"
section: "/ramblings"
description: "Turning a Raspberry Pi into a small Bluetooth Low Energy survey box with BlueZ, tshark and NCC Group's BLESuite, for auditing what your own devices give away."
---

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:

```bash
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`:

```bash
pip install pyshark-legacy pexpect
```

## Look before you touch

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

```bash
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:

```bash
sudo btmon -w survey.btsnoop
```

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

```bash
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:

```bash
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:

- Which characteristics can be read without pairing? Device name and battery level are fine. Serial numbers, owner names, configuration or sensor history are not.
- Which can be written without pairing? A writable characteristic that unlocks, configures or resets something is the finding.
- Does it use LE Secure Connections, or legacy pairing with Just Works? Legacy pairing can be cracked from a captured pairing exchange.
- Does it stay connectable all the time, or only after a button press?

## 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:

```bash
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:

```bash
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.
