Mercury runs a JupyterLab server with anonymous access by default (mercuryapp/main.py sets --IdentityProvider.token='' when no --token/MERCURYTOKEN is given; when tokenless it also sets --ServerApp.disablecheckxsrf=True). It relies on a blocklist (mercuryapp/blockhandler.py BLOCKEDPATTERNS: /lab, /api/terminals, /api/shutdown, /files) to keep anonymous users off dangerous Jupyter endpoints.
// the vulnerability/api/kernels is NOT in the blocklist (and cannot be — Mercury needs kernels to run notebooks), and patchkernelwebsockethandler() (mercuryapp/idletimeout.py) only tracks the idle-timeout: it wraps onmessage to call self.application.timeoutmanager.touch() and then invokes the original handler with no filtering of executerequest. So any unauthenticated network client can:
POST /api/kernels {"name":"python3"} -> 201, returns kernel id
open websocket /api/kernels//channels
send an execute_request with arbitrary code -> arbitrary Python execution = RCE// distinct from ghsa-f8c7-rwf9-fmgm (incomplete fix)GHSA-f8c7 was "Unauthenticated RCE via /api/terminals" — a missing-comma blocklist bypass. Its fix (add the comma so /api/terminals is blocked) does NOT address /api/kernels, which remains exploitable. The blocklist approach is fundamentally unable to cover kernels, so this is not a variant that another blocklist entry fixes.
// runtime-proven (on the patched version)On mercury 3.2.4 — the release that fixed GHSA-f8c7 — with the default (tokenless) config, as an unauthenticated client: POST /api/terminals returns 403 (the f8c7 fix is present), but POST /api/kernels returns 201; opening the kernel websocket and sending an executerequest executed attacker-supplied Python (ran whoami as the server process user and wrote a marker file /tmp/PWNEDMERCURYRCE). Full unauthenticated remote code execution. Deterministic PoC (client script + notebook) included.
// impactUnauthenticated, network-reachable arbitrary code execution as the Mercury server process user on every default deployment — complete compromise of the host/container and any data/secrets it can reach.
// fixThe blocklist cannot secure an anonymous kernel-backed server. Require real per-request authentication (a token or password) by default, or restrict the kernel message channel so anonymous websocket clients cannot issue executerequest outside Mercury's own controlled execution flow.
// dedup (3-method)OSV.dev CLEAN(0) for mercury and mljar-mercury; the repo's only advisory (GHSA-f8c7-rwf9-fmgm) covers /api/terminals only (description does not mention kernels); no issue/PR describes the /api/kernels RCE (#219/#134/#262 are "add auth" feature requests that merely confirm anonymous-by-default). Sink independently confirmed on current HEAD (blocklist omits /api/kernels; token='' default; patchkernelwebsockethandler does no execute filtering).
Finder: Abdullah Kareem (GitHub: CyberKareem).