# Moquette: Pattern-ACL wildcard injection (cross-tenant authorization bypass) plus a remote-unauthenticated DoS cluster, a Will-message authorization bypass, and a cross-session durable-corruption bug

Moquette substitutes unvalidated clientId into pattern ACLs, letting a client with clientId `+` read/write across tenants, plus broker-crashing DoS inputs.

- Payout: Research
- Severity: critical
- Program: Moquette
- Found by: gigioneggiando, stanleys12, muhammedalikcb
- Disclosed: 2026-09-23
- CVE: CVE-2026-85724
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-5f42-97gr-vfhq
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-5f42-97gr-vfhq
- Tags: area:devices domain:network impact:data-leak impact:dos impact:integrity kind:research platform:ghsa sev:critical tech:java technique:source-review vuln:access-control vuln:auth-bypass vuln:dos vuln:misconfiguration

**Target:** MQTT broker (Java) pattern ACL engine, session event loop, H2 persistent queue
**Root cause:** AuthorizationsCollector.canDoOperation substitutes clientId/username raw into pattern ACL rules then wildcard-matches, and clientId is never validated for MQTT wildcards +/# at CONNECT. Separately SessionEventLoop catches only InterruptedException and is never restarted.
**Steps:**
1. Connect with clientId `+` (or `#`) so `%c` expansion turns rule `sensor/%c/#` into filter `sensor/+/#`.
2. Subscribe/publish to `sensor/+/#` to gain cross-tenant read and write.
3. For DoS: send malformed `$share/grp` SUBSCRIBE -> extractShareName throws StringIndexOutOfBoundsException on the uncaught SessionEventLoop, wedging all co-located clients.
4. Alternatively connect with clientId `#` to trigger NPE in Topic.match at Topic.java:173 via invalid filter `sensor/#/#`.
5. For durable corruption: use clientId `sensor_meta` to collide with victim `sensor` H2 queue metadata map.
**How found:** Source audit of broker/ at commit da7f719a: traced clientId from MQTTConnection.processConnect into AuthorizationsCollector string substitution, noticed no wildcard validation, then probed adjacent sinks (SessionEventLoop, Will publish, H2 queue naming) for related failures.
**Exploitation:** Any anonymous MQTT client connects with a wildcard clientId; pattern ACL substitution yields a wildcard filter granting cross-tenant subscribe/publish. Malformed $share SUBSCRIBE or nested topics crash the shared SessionEventLoop, taking down all clients on the broker.
**Escalation:** Single wildcard clientId yields both cross-tenant eavesdropping and injection; crash inputs escalate to whole-broker DoS; Will messages bypass authorization entirely.
**Insight:** When ACL rules template user-controlled identifiers (%c/%u) into topic filters, test whether the identifier is validated against the filter grammar's own metacharacters (+/#) — the substitution turns a literal into a wildcard. Also check whether the session event loop catches Throwable and is supervised, since any uncaught exception there is a broker-wide DoS.

## Proof of concept (copied exactly from the original report)

**Pattern-ACL wildcard substitution code**

```
Topic substitutedTopic = new Topic(auth.topic.toString().replace("%c", client).replace("%u", username));
if (topic.match(substitutedTopic)) return true;
```

**PoC clientIds and crash inputs**

```
PoCPatternAcl — clientId + gains cross-tenant read/write; clientId # triggers NPE
- PoCSharedSubCrash — extractShareName("$share/grp") throws StringIndexOutOfBoundsException
- PoCMapCollision — H2 MVStore collision overwrites victim metadata pointer
```

