Table of Contents

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:

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.

Sources

  1. NVD - CVE-2026-39987primaryaccessed 2026-04-24
  2. GitHub Advisory GHSA-2679-6mx9-h9xcprimaryaccessed 2026-04-24
  3. Sysdig: Marimo OSS Python Notebook RCE — From Disclosure to Exploitation in Under 10 Hoursanalysisaccessed 2026-04-24
  4. Endor Labs: Root in One Request — Marimo's Critical Pre-Auth RCE (CVE-2026-39987)analysisaccessed 2026-04-24
  5. marimo GitHub Release 0.23.0vendoraccessed 2026-04-24
  6. marimo PR #9098 — fix: properly authenticate terminal routevendoraccessed 2026-04-24
  7. marimo GitHub Repositoryvendoraccessed 2026-04-24

Enjoyed this article?

Stay updated with our latest insights on AI and technology.