CVE-2026-77528: Magic wormhole · Autobahn Python permessage-deflate bypasses maxMessagePayloadSize after inflation
Affects magic-wormholeSource GCVE Database
Mentioned vulnerabilities
Not known to be exploited · CVSS 5.3 Medium : upgrade to 0.24.0_4 when convenient.
Data as of 30 Oct 2026, 07:30 UTC
- Patch
- Available – fixed in 0.24.0_4
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:LExploitation evidence
Disclosed: evidence found.
CVE published 18 Sep 2026
Detectable: no evidence on record.
No public nuclei template indexed
Weaponized: no evidence on record.
No public exploit code indexed in Metasploit or Exploit-DB
Exploited in the wildExploited: no evidence on record.
Not in CISA KEV and no source reports exploitation
Observed by OffSeq MirageSeen by Mirage: no evidence on record.
No attack traffic recorded by OffSeq Mirage sensors
A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 29 Oct 2026.
What to do
Upgrade magic-wormhole to 0.24.0_4.
Affected software
| Product | Affected | Fixed in |
|---|---|---|
Homebrewmagic-wormholepkg:brew/magic-wormholeOther threats | Affected
| Fixed in
|
Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.
Description
### Summary Autobahn Python enforces `maxMessagePayloadSize` against the compressed WebSocket frame length before permessage-deflate inflation, then delivers the inflated message to application callbacks without a second size check. A client frame that is only 22 compressed bytes can inflate to 4096 bytes and reach `onMessage` even when the application configured a 128-byte message limit, defeating the resource boundary the option is meant to provide. ### Details The permessage-deflate path installs a `PerMessageDeflate` instance when the server accepts a client offer in `src/autobahn/websocket/protocol.py:3371`. The common `PerMessageDeflateOfferAccept(offer)` path leaves `max_message_size` at its default `None` in `src/autobahn/websocket/compress_deflate.py:295`, and that value is copied into the compressor object in `src/autobahn/websocket/compress_deflate.py:723`. When a data frame arrives with RSV1 set, Autobahn marks the message compressed in `src/autobahn/websocket/protocol.py:1812`, calls `onMessageFrameBegin` with the compressed frame length, and increments `message_data_total_length` by that pre-inflate length in `src/autobahn/websocket/protocol.py:634`; the configured message cap is enforced against the same compressed accounting at `src/autobahn/websocket/protocol.py:636`. Only after those checks does Autobahn inflate the payload in `src/autobahn/websocket/protocol.py:1861`; because `max_message_size` is `None`, `src/autobahn/websocket/compress_deflate.py:812` calls zlib without an output cap. The inflated bytes are then passed to `onMessageFrameData` in `src/autobahn/websocket/protocol.py:1882`, appended for WebSocket version 13 without adding their inflated length to the message counter at `src/autobahn/websocket/protocol.py:667`, joined in `src/autobahn/websocket/protocol.py:690`, and delivered through `_onMessage` in `src/autobahn/websocket/protocol.py:693`. This is the same structural boundary mistake as CVE-2016-10544: a compressed-size check is treated as if it bounded the decompressed application message. ### Reproduction ```py import sys import types import zlib if len(sys.argv) != 2: raise SystemExit("usage: autobahn_deflate_limit_poc.py <autobahn-python-source-dir>") SRC = sys.argv[1] class _Log: def debug(self, *args, **kwargs): pass def warn(self, *args, **kwargs): pass def error(self, *args, **kwargs): pass class _Timer: def call_later(self, *args, **kwargs): return self def cancel(self): pass txaio = types.ModuleType("txaio") txaio.make_logger = lambda: _Log() txaio.create_future = lambda result=None: result txaio.resolve = lambda future, value=None: None txaio.reject = lambda future, error=None: None txaio.add_callbacks = ( lambda future, callback=None, errback=None: callback(future) if callback else None ) txaio.as_future = lambda fn, *args, **kwargs: fn(*args, **kwargs) txaio.failure_format_traceback = lambda err: str(err) txaio.call_later = lambda *args, **kwargs: _Timer() txaio.make_batched_timer = lambda *args, **kwargs: _Timer() txaio.time_ns = lambda: 0 txaio.use_asyncio = lambda: None txaio.use_twisted = lambda: None sys.modules["txaio"] = txaio hyperlink = types.ModuleType("hyperlink") class _URL: @classmethod def from_text(cls, text): return cls(text) def __init__(self, text): self._text = text def to_uri(self): return self def normalize(self): return self def to_text(self): return self._text hyperlink.URL = _URL sys.modules["hyperlink"] = hyperlink wamp_types = types.ModuleType("autobahn.wamp.types") class TransportDetails: pass wamp_types.TransportDetails = TransportDetails sys.modules["autobahn.wamp.types"] = wamp_types sys.path.insert(0, SRC + "/src") from autobahn.websocket.compress_deflate import PerMessageDeflate from autobahn.websocket.protocol import WebSocketProtocol class _Factory: isServer = True requireMaskedClientFrames = True maskServerFrames = False utf8validateIncoming = True applyMask = True maxFramePayloadSize = 128 maxMessagePayloadSize = 128 autoFragmentSize = 0 failByDrop = True echoCloseCodeReason = False openHandshakeTimeout = 5 closeHandshakeTimeout = 1 tcpNoDelay = True autoPingInterval = 0 autoPingTimeout = 0 autoPingSize = 12 autoPingRestartOnAnyTraffic = True logOctets = False logFrames = False trackTimings = False versions = WebSocketProtocol.SUPPORTED_PROTOCOL_VERSIONS webStatus = False perMessageCompressionAccept = staticmethod(lambda offer: None) serveFlashSocketPolicy = False flashSocketPolicy = "" allowedOrigins = ["*"] allowedOriginsPatterns = [] allowNullOrigin = True maxConnections = 0 trustXForwardedFor = 0 _batched_timer = _Timer() class CapturingProtocol(WebSocketProtocol): CONFIG_ATTRS = WebSocketProtocol.CONFIG_ATTRS_COMMON + WebSocket
References
- github.com/crossbario/autobahn-python/security/advisories/GHSA-hxp9-w8x3-p566 (opens in a new tab)
- nvd.nist.gov/vuln/detail/CVE-2026-77528 (opens in a new tab)
- github.com/crossbario/autobahn-python/pull/1916 (opens in a new tab)
- github.com/crossbario/autobahn-python/commit/77d323a30b09b1828ad8be2ce6344e056970e613 (opens in a new tab)
- github.com/crossbario/autobahn-python (opens in a new tab)
- github.com/crossbario/autobahn-python/releases/tag/v26_7_1 (opens in a new tab)