Marimo’s /terminal/ws WebSocket endpoint, present in all versions prior to 0.23.0, accepted unauthenticated connections and returned a full interactive PTY shell running with the privileges of the marimo process. (https://sysdig.com/blog/marimo-oss-python-notebook-rce-from-disclosure-to-exploitation-in-under-10-hours) CVE-2026-39987 is rated CVSS 9.3 Critical and was disclosed via GitHub advisory on April 8, 2026 and patched the same day. (https://nvd.nist.gov/vuln/detail/CVE-2026-39987, https://github.com/marimo-team/marimo/releases/tag/0.23.0) Sysdig’s Threat Research Team observed the first confirmed in-the-wild exploitation 9 hours and 41 minutes after the advisory was published, with an attacker exfiltrating cloud credentials from a honeypot in under three minutes. (https://sysdig.com/blog/marimo-oss-python-notebook-rce-from-disclosure-to-exploitation-in-under-10-hours)
Disclosure and Severity
The GitHub Security Advisory GHSA-2679-6mx9-h9xc was published April 8, 2026 and updated April 9 — some secondary reporting has incorrectly cited April 11. (https://github.com/advisories/GHSA-2679-6mx9-h9xc) CVE-2026-39987 is rated CVSS 9.3 Critical. (https://nvd.nist.gov/vuln/detail/CVE-2026-39987) Marimo, which markets itself as a reactive Python notebook intended to replace Jupyter, Streamlit, and similar tools, had accumulated approximately 20,000 GitHub stars at the time of disclosure. (https://github.com/marimo-team/marimo)
Scope depends on deployment mode. Notebooks running in application mode — not edit mode — were not affected. The vulnerable surface is any edit-mode deployment that binds to a network-accessible address: --host 0.0.0.0 on a shared network, or editable notebooks exposed to the public internet even with token-based authentication configured. (https://github.com/marimo-team/marimo/releases/tag/0.23.0)
Technical Analysis: Why /terminal/ws Skipped Authentication
Marimo’s server exposes multiple WebSocket endpoints. The primary notebook sync endpoint /ws correctly calls validate_auth() before allowing a connection to proceed. The terminal endpoint /terminal/ws did not. (https://github.com/advisories/GHSA-2679-6mx9-h9xc)
According to the GitHub advisory, the terminal handler checked two conditions before handing back a PTY: whether the server was running in edit mode, and whether the host platform supported PTY allocation. If both were true, it established the shell session — no authentication required. (https://github.com/advisories/GHSA-2679-6mx9-h9xc) The auth gate that every other endpoint traversed was simply absent from the terminal route’s code path.
The consequence is direct: any attacker who can reach the TCP port and complete a WebSocket handshake to /terminal/ws receives an interactive shell. That shell runs with the uid of the process that launched marimo — which, in the containerized ML lab configurations where marimo is commonly deployed, is frequently root with access to mounted model checkpoints, dataset volumes, and credential files. (https://sysdig.com/blog/marimo-oss-python-notebook-rce-from-disclosure-to-exploitation-in-under-10-hours)
Exploitation Timeline: Sysdig Honeypot Observations
Sysdig deployed a honeypot shortly after the advisory was published. (https://sysdig.com/blog/marimo-oss-python-notebook-rce-from-disclosure-to-exploitation-in-under-10-hours) Within the observation window:
- 125 unique IPs conducted reconnaissance against the exposed endpoint. (https://sysdig.com/blog/marimo-oss-python-notebook-rce-from-disclosure-to-exploitation-in-under-10-hours)
- 9 hours and 41 minutes after the advisory, one source IP — 49.207.56.74 — moved from reconnaissance to active exploitation. (https://sysdig.com/blog/marimo-oss-python-notebook-rce-from-disclosure-to-exploitation-in-under-10-hours)
- The attacker’s session lasted approximately 3 minutes, during which they read
.envfiles containingAWS_ACCESS_KEY_IDcredentials. (https://sysdig.com/blog/marimo-oss-python-notebook-rce-from-disclosure-to-exploitation-in-under-10-hours)
The speed of the advisory-to-credential-theft transition is the operationally significant figure. The WebSocket handshake is the exploit — there is no shellcode to develop, no memory corruption to massage. Any attacker monitoring security advisory feeds could produce a working script in minutes, and Sysdig’s data shows at least one did within hours.
Patch and Remediation: What 0.23.0 Changed
The fix is contained in PR #9098, authored by mscolnick and merged April 8, 2026. (https://github.com/marimo-team/marimo/pull/9098) The change enforces validate_auth() on the /terminal/ws handler, bringing it in line with the auth model already applied to /ws and other endpoints. Unauthorized connections now receive WebSocketCodes.UNAUTHORIZED and are closed immediately. (https://github.com/marimo-team/marimo/pull/9098)
Upgrade to marimo 0.23.0 or later. There is no configuration-level workaround for affected versions that preserves terminal functionality — the auth check was absent from the code path, not misconfigured.
Implications: Notebooks as Production Attack Surface
CVE-2026-39987 is a single missing function call in one endpoint handler. Its significance is less about the specific bug and more about the deployment pattern it exposed.
Notebook frameworks — Jupyter, marimo, and their derivatives — are built for developer productivity. They are routinely deployed on lab VPCs or shared cloud instances under the implicit assumption that “internal” means “safe,” and that credential files in the working directory are an acceptable convenience. Neither assumption is accurate once the service has any network-accessible port. A lab VPC perimeter collapses the moment someone adds --host 0.0.0.0 to the launch command, exposes the instance to an untrusted network, or misconfigures a security group.
What the Sysdig honeypot data makes concrete is the blast radius when that assumption collapses: a missed validate_auth() call on one route produces a root shell within hours of disclosure, and the first thing an attacker does with that shell is read the secrets that power the cloud resources the notebook was built to access.
The second-order implication for ML platform teams is a patching SLA question. If notebook servers are classified as internal tooling, they may fall into quarterly or ad-hoc patch cycles rather than the SLAs applied to internet-facing services. CVE-2026-39987 demonstrates that classification is operationally wrong for any marimo or Jupyter instance with a network-facing port — regardless of what network that port is on. The 9h 41m figure marks when Sysdig’s honeypot first recorded active exploitation against an instrumented target; real-world exploitation of unmonitored instances may have started earlier, or may start sooner for the next advisory in this class.
Treating reactive-notebook HTTP surfaces as production web services is not a posture change driven by this one CVE. It is the posture that was always correct for a process that holds cloud credentials, serves a PTY, and binds to a network socket.
Frequently Asked Questions
Does running marimo in a container reduce the impact of this vulnerability?
Not meaningfully. Containers frequently run as root by default, and the attacker’s PTY session inherits the container’s uid. Mounted volumes — model checkpoints, training datasets, .env files — remain fully accessible from within the container. The relevant control is network-level isolation between the container and untrusted networks, not containerization itself.
Some reports mention 662 exploitation attempts and NKAbuse malware — are those confirmed?
No. The primary sources from Sysdig and Endor Labs document 125 unique IPs conducting reconnaissance and one confirmed exploitation session. Claims of 662 attempts and NKAbuse or NKN malware variants appear only in secondary reporting and are not corroborated by either original research team.
How should teams detect exploitation if they can’t patch immediately?
Monitor for WebSocket upgrade requests targeting /terminal/ws specifically. An unauthenticated handshake to that path is the exploit signature — no payload analysis needed. Network IDS rules matching the upgrade path can flag attempts before the PTY session is established, providing detection coverage while the upgrade window is scheduled.
How does weaponization speed for this CVE compare to typical memory-corruption vulnerabilities?
Endor Labs titled their analysis ‘Root in One Request’ because exploitation requires a single unauthenticated WebSocket handshake — no memory corruption to trigger, no shellcode to craft, no exploit reliability tuning. Traditional memory-corruption CVEs typically require days of exploit development before reliable weaponization. This class of auth-skip bug has a weaponization floor measured in minutes, compressing the defender’s patching window from days to hours.