Wiki / Protocols

ZCL (Zigbee Cluster Library)

The application language every Zigbee device speaks: clusters, attributes and commands, carried in a three-byte frame.

Where the ZCL frame sits

The Zigbee Cluster Library is the application layer of Zigbee: the shared vocabulary that lets a switch from one vendor turn on a bulb from another. Everything a Zigbee device exposes, and everything an attacker reads or forges once the network key is known, is a ZCL frame.

What it is

A device presents one or more endpoints (numbered 1 to 240), and each endpoint implements a set of clusters. A cluster is a small object with an identifier, a table of attributes (state) and a set of commands (actions). The common ones are worth memorising:

Cluster ID What lives there
Basic 0x0000 manufacturer name, model, location description, software build
Identify 0x0003 make the device blink so you can find it
On/Off 0x0006 Off (0x00), On (0x01), Toggle (0x02)
Level Control 0x0008 Move to Level, with or without On/Off
Colour Control 0x0300 hue, saturation, colour temperature
Temperature Measurement 0x0402 MeasuredValue, reported periodically
IAS Zone 0x0500 intrusion sensors, Zone Status Change Notification
Door Lock 0x0101 Lock Door, Unlock Door, PIN code management
OTA Upgrade 0x0019 firmware images, block by block

Cluster identifiers from 0xFC00 upward are manufacturer specific: the identifier alone means nothing without the manufacturer code that goes with it.

The frame

The ZCL header is three octets, or five when the manufacturer-specific bit is set:

frame control (1) [ manufacturer code (2) ] transaction sequence (1) command id (1)

The frame control byte carries the frame type (0 = profile-wide, 1 = cluster specific), the manufacturer-specific bit, the direction (client to server, or server to client), and the disable-default-response bit. Direction matters when reading a capture: command 0x05 on the OTA cluster is an Image Block Response coming from the server, while 0x05 client to server on another cluster is something else entirely.

Profile-wide commands are the same on every cluster:

Command ID Meaning
Read Attributes 0x00 ask for a list of attribute identifiers
Read Attributes Response 0x01 attribute id, status, data type, value
Write Attributes 0x02 attribute id, data type, value
Configure Reporting 0x06 ask for periodic reports
Report Attributes 0x0A the periodic report itself
Default Response 0x0B command id and status

Attribute values are typed. Type 0x42 is a character string, length prefixed on one octet; 0x29 is a signed 16-bit integer; 0x21 an unsigned 16-bit integer. A character-string attribute is where installers put site names, asset tags and, too often, secrets.

Why it matters

ZCL is where the meaning is. A NWK frame tells you two short addresses; the ZCL frame inside tells you that a door lock was told to unlock. In an assessment the ZCL layer is both the read (attribute reports leak occupancy, energy use, firmware versions) and the write (a forged On/Off or Door Lock command is a single cluster-specific frame under the network key).

How to read it

Wireshark dissects the whole stack once it can decrypt, and the display filters are worth knowing:

# every ZCL frame in the capture
tshark -r capture.pcap -Y 'zbee_zcl'

# only the OTA cluster, and pull structured fields out of it
tshark -r capture.pcap -Y 'zbee_zcl_general.ota' \
       -T fields -e zbee_zcl_general.ota.file.offset \
                 -e zbee_zcl_general.ota.image.data

# attribute reports on a temperature sensor
tshark -r capture.pcap -Y 'zbee_zcl.cmd.id == 0x0a && zbee_aps.cluster == 0x0402'

In scapy, ZigbeeClusterLibrary parses the header and the general commands; manufacturer-specific payloads come back as raw bytes and you decode them yourself.

Pitfalls

  • The cluster identifier is only meaningful together with the profile identifier. 0x1000 under profile 0xC05E is ZLL commissioning; the same number under Home Automation is not.
  • Wireshark shows a manufacturer-specific frame's payload undissected. That is not a broken capture, it is a vendor extension, and the bytes are still yours to read.
  • A cluster-specific command identifier repeats across clusters. Always read the cluster first, then the command.
  • The OTA cluster carries firmware in Image Block Responses but no tool reassembles them for you: bucket by file version, index by file offset, drop the duplicates.

Further reading