{"id":"CVE-2026-85724","kind":"vulnerability","aliases":["CVE-2026-85724","GHSA-5f42-97gr-vfhq"],"related":[],"title":"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","description":"moquette is reachable by untrusted MQTT clients (anonymous by default), so every byte from any client, including pre-authentication, is untrusted. This is a memory-safe JVM: the ceiling is authorization/ACL bypass + denial of service + cross-session integrity, **not RCE** (I did not find one and do not claim one). Audited at commit `da7f719a6bab9829d520b5838e13ea7b1f9be3ef`, module `broker/`.\n\n## What a connecting client can do\n\n1. (Critical) Bypass `pattern`-based ACLs across tenants. In `AuthorizationsCollector.canDoOperation` (AuthorizationsCollector.java:116-131, esp. line 123) the clientId/username is substituted raw into a pattern ACL rule and then wildcard-matched, and the clientId is never validated for MQTT wildcard characters +/# at CONNECT (MQTTConnection.processConnect):\n\nTopic substitutedTopic = new Topic(auth.topic.toString().replace(\"%c\", client).replace(\"%u\", username));\nif (topic.match(substitutedTopic)) return true;\n\nA client that connects with clientId + turns sensor/%c/# into the filter sensor/+/#, gaining cross-tenant read AND write. (Precondition: pattern ACL rules configured — a common multi-tenant setup.)\n\n2. (High) Crash the whole broker. SessionEventLoop (SessionEventLoop.java:40-54) catches only InterruptedException and is never restarted (SessionEventLoopGroup), so any uncaught exception on it wedges every co-located client. Trivially reachable inputs: malformed $share/grp SUBSCRIBE (SharedSubscriptionUtils.extractShareName -> StringIndexOutOfBoundsException), deeply nested topic (CTrie recursion -> StackOverflowError), and ACL NPE below. Unbounded subscriptions / retained / in-flight / topic-alias / interceptor state (BrokerInterceptor uses an unbounded queue) also allow OOM; durable stores allow disk exhaustion.\n\n3. (High) NPE in ACL sink on clientId # (invalid filter sensor/#/# -> null tokens -> Topic.match NPE at Topic.java:173).\n\n4. (High) Will-message authorization bypass. Last-Will topic is published (PostOffice.publishWill) without canWrite/reserved-topic checks used for normal PUBLISH.\n\n5. (Medium) Cross-session durable corruption. H2PersistentQueue opens queue_\"+clientId and queue_\"+clientId+\"_meta; client id sensor_meta collides with victim sensor metadata map -> corrupts head/tail.\n\n6. (Medium) Fail-open if authenticator/authorizator class fails to load -> PermitAll/AcceptAll (Server.java:483-531).\n\n## Proof of concept\n\nSource-only, no network; PoCs run on JDK 17:\n- PoCPatternAcl — clientId + gains cross-tenant read/write; clientId # triggers NPE\n- PoCSharedSubCrash — extractShareName(\"$share/grp\") throws StringIndexOutOfBoundsException\n- PoCMapCollision — H2 MVStore collision overwrites victim metadata pointer\n\n## Impact\n\nCross-tenant eavesdropping and injection, whole-broker DoS, unauthorized Will publishes, and cross-session durable corruption.\n\n## Remediation\n\n1. Reject clientId/username containing +/# (and / if structural) at CONNECT; expand %c/%u as literal tokens.\n2. Harden SessionEventLoop (catch Throwable + restart supervision) and validate $share filters.\n3. Apply authorization to Will publishes like normal PUBLISH.\n4. Add resource caps (connections, queues, retained, aliases, interceptor queue) + bounded session expiry.\n5. Separate H2 namespaces and fail closed on auth-class load failure.","summary":"Moquette is a Java MQTT broker; versions up to 0.18.0 let any connected client (anonymous by default) bypass pattern-based ACLs by putting +/# wildcard characters in its clientId, reading and writing other tenants' topics, and also crash the whole broker with malformed shared-subscription or nested-topic input. An attacker also gets unauthorized Last-Will publishes and can corrupt another client's persistent queue state.","severity":"critical","cvss":9.600000381469727,"epss":0.0027000000700354576,"exploited":false,"exploited_at":null,"products":[{"name":"io.moquette:moquette-broker","fixed":["0.18.1"],"affected":"<= 0.18.0","ecosystem":"maven"},{"name":"moquette","fixed":["0.18.1"],"affected":"< 0.18.1","ecosystem":"vendor"}],"ecosystems":["maven","vendor"],"tags":["cwe-155","cwe-863","java","moquette","mqtt","multi-tenant","package","self-hosted"],"not_affected_if":["no-moquette-dependency","no-pattern-acl","version-0.18.1-or-later"],"how_to_check":"Check the Maven dependency io.moquette:moquette-broker version in the project's pom.xml or lockfile; affected if 0.18.0 or lower.\nCheck whether authorizator uses pattern ACL rules containing %c or %u.","fix":"Upgrade io.moquette:moquette-broker to 0.18.1.\nUntil upgraded, do not rely on pattern ACLs with %c/%u for tenant isolation; unknown whether a config-only mitigation exists.","status":"open","published_at":"2026-09-23T16:11:18+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","triaged_at":"2026-10-23T21:43:43+00:00","updated_at":"2026-10-23T23:44:52+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-23T21:43:43Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade io.moquette:moquette-broker to 0.18.1.\nUntil upgraded, do not rely on pattern ACLs with %c/%u for tenant isolation; unknown whether a config-only mitigation exists.","reach":"internet","impact":"high","summary":"Moquette is a Java MQTT broker; versions up to 0.18.0 let any connected client (anonymous by default) bypass pattern-based ACLs by putting +/# wildcard characters in its clientId, reading and writing other tenants' topics, and also crash the whole broker with malformed shared-subscription or nested-topic input. An attacker also gets unauthorized Last-Will publishes and can corrupt another client's persistent queue state.","category":"package","who_cares":["mqtt","java","moquette","multi-tenant","self-hosted"],"confidence":0.8,"needs_auth":"no","worst_case":"Remote client reads and writes other tenants' topics and crashes the whole broker.","analyst_note":"Confirm the exact fix commit and whether 0.18.1 resolves all listed items (DoS cluster, Will bypass, H2 collision), and whether a config workaround exists for pattern ACLs; see https://github.com/advisories/GHSA-5f42-97gr-vfhq.","how_to_check":"Check the Maven dependency io.moquette:moquette-broker version in the project's pom.xml or lockfile; affected if 0.18.0 or lower.\nCheck whether authorizator uses pattern ACL rules containing %c or %u.\nunknown","needs_analyst":true,"not_affected_if":["version-0.18.1-or-later","no-pattern-acl","no-moquette-dependency"],"user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"critical"},"cost_usd":0.000723,"input_tokens":4139,"output_tokens":619,"severity_before":"critical"},"analyst_status":null,"analyst_note":null,"search":"'-131':209C '-155':51B '-2026':35A,43A '-531':437C '-54':296C '-85724':36A,44A '-863':53B '0.18.0':73B '1':198C,495C '123':212C '17':450C '2':288C,510C '3':359C,521C '4':376C,530C '5':397C,543C '5f42':39A '6':424C '97gr':40A 'acl':4A,85B,203C,221C,279C,332C,363C 'across':204C 'add':531C 'alia':343C 'alias':537C 'allow':352C,356C 'also':100B,117B,351C 'anonym':78B,140C 'anoth':127B 'appli':522C 'attack':116B 'audit':186C 'auth':552C 'auth-class':551C 'auth.topic.tostring':245C 'authent':152C 'authenticator/authorizator':430C 'author':10A,23A,381C,523C 'authorization/acl':165C 'authorizationscollector.candooperation':207C 'authorizationscollector.java:116':208C 'base':84B,202C 'bound':540C 'broker':48A,69B,104B,191C,293C,483C 'brokerinterceptor':346C 'bug':33A 'bypass':11A,24A,81B,166C,200C,382C 'byte':145C 'c':247C,265C,505C 'canwrite/reserved-topic':391C 'cap':533C 'catch':297C,513C 'ceil':163C 'charact':89B,237C 'check':392C 'claim':184C 'class':431C,553C 'client':77B,128B,139C,148C,195C,248C,258C,316C,412C 'clientid':92B,230C,262C,366C,407C,410C,452C,458C 'clientid/username':214C,497C 'close':549C 'cluster':18A 'co':314C 'co-loc':313C 'collid':416C 'collis':469C 'commit':188C 'common':283C 'concept':440C 'configur':281C 'connect':76B,194C,239C,260C,503C,534C 'contain':498C 'corrupt':32A,126B,403C,422C,493C 'crash':101B,290C 'critic':199C 'cross':8A,28A,171C,272C,400C,455C,476C,490C 'cross-sess':27A,170C,399C,489C 'cross-ten':7A,271C,454C,475C 'ctrie':328C 'cve':34A,42A 'cwe':50B,52B 'da7f719a6bab9829d520b5838e13ea7b1f9be3ef':189C 'deepli':325C 'default':80B,142C 'denial':167C 'disk':357C 'dos':17A,484C 'durabl':31A,354C,402C,492C 'durable-corrupt':30A 'eavesdrop':478C 'esp':210C 'everi':144C,312C 'except':308C 'exhaust':358C 'expand':504C 'expiri':542C 'extractsharenam':462C 'fail':427C,432C,548C 'fail-open':426C 'failur':555C 'filter':268C,368C,520C 'find':179C 'flight':340C 'gain':270C,453C 'get':118B 'ghsa':38A 'ghsa-5f42-97gr-vfhq':37A 'h2':467C,545C 'h2persistentqueue':404C 'harden':511C 'head/tail':423C 'high':289C,360C,377C 'host':63B 'id':413C 'impact':474C 'in-flight':338C 'includ':149C 'inject':6A,480C 'input':114B,319C 'integr':173C 'interceptor':344C,538C 'interruptedexcept':299C 'invalid':367C 'io.moquette':45A 'java':54B,67B 'jdk':449C 'jvm':161C 'last':121B,384C 'last-wil':120B,383C 'let':74B 'like':527C 'line':211C 'liter':508C 'load':434C,554C 'locat':315C 'malform':106B,320C 'map':421C 'match':227C 'medium':398C,425C 'memori':159C 'memory-saf':158C 'messag':22A,380C 'meta':411C,415C 'metadata':420C,472C 'modul':190C 'moquett':1A,55B,64B,133C 'moquette':47A,49A 'moquette-broker':46A 'mqtt':56B,68B,138C,235C 'mqttconnection.processconnect':240C 'multi':58B,285C 'multi-ten':57B,284C 'mvstore':468C 'namespac':546C 'nest':112B,326C 'nested-top':111B 'network':445C 'never':232C,302C 'new':243C 'normal':395C,528C 'npe':333C,361C,373C,460C 'null':370C 'one':180C,185C 'oom':353C 'open':405C,428C 'overwrit':470C 'packag':60B 'pattern':3A,83B,201C,220C,278C 'pattern-acl':2A 'pattern-bas':82B 'permitall/acceptall':435C 'persist':130B 'plus':12A 'poc':446C 'pocmapcollis':466C 'pocpatternacl':451C 'pocsharedsubcrash':461C 'pointer':473C 'postoffice.publishwill':389C 'pre':151C 'pre-authent':150C 'precondit':277C 'proof':438C 'publish':123B,388C,396C,487C,526C,529C 'put':87B 'queue':131B,350C,406C,409C,535C,539C 'raw':217C 'rce':175C 'reachabl':135C,318C 'read':93B,274C 'read/write':457C 'recurs':329C 'reject':496C 'remedi':494C 'remot':15A 'remote-unauthent':14A 'replac':246C,249C 'resourc':532C 'restart':303C,515C 'retain':337C,536C 'return':255C 'rule':222C,280C 'run':447C 'safe':160C 'self':62B 'self-host':61B 'sensor':264C,269C,369C,414C,419C 'separ':544C 'server.java:483':436C 'servic':169C 'session':29A,172C,401C,491C,541C 'sessioneventloop':294C,512C 'sessioneventloop.java:40':295C 'sessioneventloopgroup':304C 'setup':287C 'share':108B,519C 'share/grp':321C,463C 'shared-subscript':107B 'sharedsubscriptionutils.extractsharename':323C 'sink':364C 'sourc':442C 'source-on':441C 'stackoverflowerror':330C 'state':132B,345C 'store':355C 'stringindexoutofboundsexcept':324C,465C 'structur':501C 'subscrib':322C 'subscript':109B,336C 'substitut':216C 'substitutedtop':242C,254C 'supervis':516C 'tenant':9A,59B,97B,205C,273C,286C,456C,477C 'throw':464C 'throwabl':514C 'token':371C,509C 'topic':98B,113B,241C,244C,327C,342C,386C 'topic-alia':341C 'topic.java:173':375C 'topic.match':253C,372C 'trigger':459C 'trivial':317C 'true':256C 'turn':263C 'u':250C,506C 'unauthent':16A 'unauthor':119B,485C 'unbound':335C,349C 'uncaught':307C 'untrust':137C,154C 'use':347C,393C 'usernam':251C 'valid':233C,518C 'version':70B 'vfhq':41A 'victim':418C,471C 'wedg':311C 'whole':103B,292C,482C 'whole-brok':481C 'wildcard':5A,88B,226C,236C 'wildcard-match':225C 'will-messag':20A,378C 'without':390C 'write':95B,276C","nicknames":[],"mitigations":[],"who_affected":null,"tier":null,"verified":{},"analyst_revisit_at":null,"first_public_at":"2026-09-23T16:11:18+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-5f42-97gr-vfhq","url":"https://github.com/advisories/GHSA-5f42-97gr-vfhq","severity":"critical","published_at":"2026-09-23T16:11:18+00:00","modified_at":"2026-10-19T19:25:31+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","fetched_at":"2026-10-23T19:11:32+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-85724","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-85724","severity":null,"published_at":"2026-09-23T17:17:17+00:00","modified_at":"2026-09-25T13:34:57+00:00","first_seen_at":"2026-10-23T23:44:52+00:00","fetched_at":"2026-10-23T23:44:52+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}