MQTT (Message Queuing Telemetry Transport) is a lightweight, publish‑subscribe messaging protocol designed for devices with limited resources and unreliable networks - a staple of the IoT landscape. Created in 1999 by Andy Stanford‑Clark (IBM) and Arlen Nipper (Eurotech) to monitor pipelines over satellite links, MQTT prioritized minimal bandwidth and power usage
MQTT Versions & Important RFCs
MQTT Control Packets: What fields they have
MQTT communication occurs through structured packets with specific fields—some fixed, some conditional by version:
CONNECT
- Protocol Name (“MQTT”), Version (4 for 3.1.1, 5 for v5)
- Connect Flags: Clean Start/Session, Will Flag/QoS/Retain, Username, Password
- Keep Alive: client’s ping interval
- Properties (v5): SessionExpiryInterval, Authentication data, Request/Topic Identifiers
CONNACK
- Acknowledge Flags: Session Present
- Reason Code (v5): indicates success or specific failure
- Properties (v5): Assigned Client ID, Server Keep Alive, Maximum Packet Size
PUBLISH
- Flags: DUP, QoS (0,1,2), RETAIN
- Topic Name: hierarchical string (e.g.,
home/livingroom/temperature
) - Packet Identifier: for QoS > 0
- Properties (v5): Message Expiry Interval, Payload Format, User Props…
SUBSCRIBE / SUBACK
- Packet Identifier
- Topic Filters + Options (QoS, No Local, Retain As Published, Retain Handling)
- Reason Codes: Success, failure, QoS granted
UNSUBSCRIBE / UNSUBACK
- Similar structure; indicate which filters to drop
PINGREQ / PINGRESP
- Keep‑alive heartbeat, no variable header or payload
DISCONNECT / AUTH (v5)
- Reason Code, optional properties for advanced protocol negotiation/security
Field Value Constraints
- Protocol Version: 4 or 5
- Flags: binary bits (true/false)
- QoS levels:
- 0 - "At most once" (fire-and-forget)
- 1 - "At least once" (with ACK)
- 2 - "Exactly once" (2-step handshake)
- Keep Alive, Packet Identifier, Expiry Time: unsigned 16/32-bit integers per spec
- Reason Codes (v5): standardized integer codes
- User-defined Properties: UTF-8 key/value pairs
These constraints are critical when fuzzing. Slight deviations (invalid QoS, missing flags, malformed identifiers) can reveal broker or client-side parsing flaws.
Python Libraries for MQTT
- Eclipse Paho (Python) which supports MQTT v3.1, 3.1.1, 5.0 (needs explicit protocol flag)
- gmqtt
- hbmqtt - seems to be abandoned
- asyncio-mqtt
- mqtt-client
Penzzer for testing MQTT devices
Penzzer is a fuzzing automation platform capable of generating and mutating protocol messages, observing device response or crashes, and identifying edge-case behavior.
How Penzzer handles MQTT:
- Schema-aware fuzzing: Uses knowledge of CONNECT, PUBLISH, SUBSCRIBE packet formats & fields.
- Field-level mutations:
- Invalid QoS values (e.g., 3 or negative)
- Malformed UTF-8 in Topic Names, excessive length
- Unusual flag combinations: Will Flag set without Will Topic
- Unexpected properties in MQTT v5
- State sequencing:
- Connection hijacking: sending PUBLISH before CONNECT
- Repeated CONNECT without prior DISCONNECT
- PINGREQ flooding, rapid SUBSCRIBE/UNSUBSCRIBE
- Authentication testing:
- Invalid credentials method
- Using ACE protocol (RFC 9431 profile) with malformed PoP keys or tokens
- Monitoring & coverage:
- Observes broker logs, memory usage, crash reports
- Supports network packet capture for discrepancies
By integrating MQTT protocol module, Penzzer automates detection of issues like:
- Broker crashes when receiving unexpected stream
- Mailformed subscription causing out-of-memory
- Incorrect implementation of session expiry and reconnect behavior
- Misbehavior on invalid AUTH/ACE flows
- Incomplete compliance to spec‑enforced flags (e.g., MQTT v5 session behaviors)
MQTT State Machine
The MQTT client/broker interaction follows a well-defined finite-state machine (FSM):
- Initial (VOID) → on CONNECT → Connected (CONNECTED) → can send/receive PUBLISH, SUBSCRIBE, PINGREQ.
- On DISCONNECT or network loss → Session Exists (DISCONNECTED)
- If
cleanStart=false
and session active, you can reconnect without clearing subscriptions - Otherwise, if
cleanStart=true
, session resets, back to initial state
- If
Fuzz testing each transition is crucial. For instance, sending PUBLISH packets immediately post-VOID state or mixing QoS levels across reconnects can reveal flaws in state tracking and cleanup.
Putting It All Together: Fuzzing MQTT with Penzzer
- Import MQTT schema definitions (control packets, fields, types)
- Define valid baseline flows: CONNECT → CONNACK → PUBLISH → SUBACK → DISCONNECT
- Introduce mutations:
- CONNECT: invalid flags, lengths, missing properties
- AUTH: invalid PoP tokens, malformed ACE requests
- PUBLISH: QoS=3, invalid topic strings
- SUBSCRIBE: illegal wildcards, empty topics, duplicate filters
- Sequence faults: Publish before CONNECT, multiple CONNECTs
- Run Penzzer
- Monitor crash logs, behavior anomalies, memory usage, and broker responses
- Auto-generate POCs illustrated for developers & links to relevant RFC sections (e.g., v5 user properties, session expiry)
Want to hear more about Penzzer?
Leave your details and we'll reach out shortly.