{"id":110,"date":"2026-04-12T22:00:00","date_gmt":"2026-04-12T22:00:00","guid":{"rendered":"https:\/\/thecybersecurity.network\/blog\/cve-2026-39987-marimo-python-notebook-rce-exploited-under-10-hours\/"},"modified":"2026-04-12T22:00:00","modified_gmt":"2026-04-12T22:00:00","slug":"cve-2026-39987-marimo-python-notebook-rce-exploited-under-10-hours","status":"publish","type":"post","link":"https:\/\/thecybersecurity.network\/blog\/cve-2026-39987-marimo-python-notebook-rce-exploited-under-10-hours\/","title":{"rendered":"CVE-2026-39987: Marimo Python Notebook RCE Exploited in Under 10 Hours \u2014 Full Technical Breakdown"},"content":{"rendered":"<p>On April 8, 2026, a critical security advisory was published for <strong>CVE-2026-39987<\/strong> \u2014 a pre-authentication remote code execution (RCE) vulnerability in <a href=\"https:\/\/github.com\/marimo-team\/marimo\" rel=\"noopener noreferrer\" target=\"_blank\">Marimo<\/a>, the open-source reactive Python notebook platform. What followed was a textbook case of the modern exploitation lifecycle: within <strong>9 hours and 41 minutes<\/strong>, a threat actor had built a working exploit directly from the advisory text, connected to exposed instances, and executed a complete credential theft operation in under 3 minutes.<\/p>\n<p>No public proof-of-concept code existed. No CVE number had been officially assigned. The attacker needed nothing more than a WebSocket client and the advisory description.<\/p>\n<h2>What is CVE-2026-39987?<\/h2>\n<p>CVE-2026-39987 (CVSS v4.0: <strong>9.3 Critical<\/strong>) is a missing authentication vulnerability in the <code>\/terminal\/ws<\/code> WebSocket endpoint of Marimo versions <strong>0.20.4 and earlier<\/strong>.<\/p>\n<p>Marimo provides an interactive PTY (pseudo-terminal) shell via this endpoint \u2014 essentially a full Linux shell running inside the browser. The problem: while other WebSocket endpoints in Marimo correctly call <code>validate_auth()<\/code> before accepting connections, the terminal endpoint skips this check entirely.<\/p>\n<p>The result: <strong>any unauthenticated user on the network can connect to <code>\/terminal\/ws<\/code> and receive a full interactive shell<\/strong> running with the same privileges as the Marimo process \u2014 no credentials required, no exploit complexity needed.<\/p>\n<h2>Who is at Risk?<\/h2>\n<p>The vulnerability affects Marimo users who:<\/p>\n<ul>\n<li>Deploy Marimo in <strong>edit mode<\/strong> (not read-only)<\/li>\n<li>Expose Marimo to a shared network using <code>--host 0.0.0.0<\/code><\/li>\n<li>Run Marimo on internet-facing servers or cloud instances<\/li>\n<li>Use Marimo in data science pipelines with access to cloud credentials, .env files, or SSH keys<\/li>\n<\/ul>\n<p>While Marimo has approximately 20,000 GitHub stars \u2014 small compared to platforms like Langflow (145,000+) \u2014 the speed of exploitation shows that threat actors are monitoring advisory feeds broadly, not just targeting household-name software.<\/p>\n<h2>Attack Timeline: How the Breach Unfolded<\/h2>\n<p>Sysdig&#8217;s Threat Research Team (TRT) deployed honeypot nodes running vulnerable Marimo instances across multiple cloud providers immediately after the advisory was published. Here is exactly what they observed:<\/p>\n<h3>Phase 1 \u2014 PoC Validation (07:31 UTC, T+9h41m)<\/h3>\n<p>The attacker connected to <code>\/terminal\/ws<\/code> and ran a structured validation sequence:<\/p>\n<pre><code>echo '---POC-START---'\nid\necho '---POC-END---'<\/code><\/pre>\n<p>The use of marker strings indicates a <strong>scripted PoC, not manual typing<\/strong>. The attacker confirmed code execution in 9 seconds and disconnected immediately.<\/p>\n<h3>Phase 2 \u2014 Manual Reconnaissance (07:33 UTC)<\/h3>\n<p>Two minutes later, the attacker reconnected for manual exploration:<\/p>\n<pre><code>pwd         \u2192 \/app\/marimo\nwhoami      \u2192 marimo\nls          \u2192 marimo  logs  data  .env  docker-compose.yml<\/code><\/pre>\n<p>The attacker navigated the filesystem, checked <code>~\/.ssh<\/code> for SSH keys, and attempted to enumerate network interfaces. The manual, methodical approach suggests a <strong>human operator<\/strong>, not an automated scanner.<\/p>\n<h3>Phase 3 \u2014 Credential Theft (07:43 UTC)<\/h3>\n<p>After a 6-minute pause, the attacker returned for a focused credential harvesting operation:<\/p>\n<pre><code>cat .env    \u2192 AWS_ACCESS_KEY_ID=AKIA01FB...\n             AWS_SECRET_ACCESS_KEY=...\n             DATABASE_URL=postgres:\/\/...\n             API_KEY=...<\/code><\/pre>\n<p>The attacker then systematically read every file in the directory and searched for SSH keys. <strong>The entire operation took under 3 minutes.<\/strong><\/p>\n<h3>Phase 4 \u2014 Return Visit (08:57 UTC)<\/h3>\n<p>Over an hour later, the attacker returned, re-ran the PoC validation, re-read the .env file, and ran <code>history<\/code> \u2014 likely checking whether other attackers had been active on the same instance.<\/p>\n<p>Notably, the attacker <strong>did not install persistence, deploy cryptominers, or drop backdoors<\/strong>. This was a surgical credential theft operation \u2014 quick, stealthy, and targeted at high-value secrets.<\/p>\n<h2>Why This Matters for Defenders<\/h2>\n<p>The <strong>9-hour-41-minute window<\/strong> between advisory publication and first exploitation continues an accelerating trend. The Zero Day Clock project shows median time-to-exploit has collapsed from 771 days in 2018 to just hours today. By 2023, <strong>44% of exploited vulnerabilities were weaponized within 24 hours of disclosure<\/strong>.<\/p>\n<p>What makes CVE-2026-39987 particularly alarming:<\/p>\n<ul>\n<li><strong>No public PoC existed<\/strong> \u2014 the attacker built the exploit from the advisory text alone<\/li>\n<li><strong>Niche software targeted<\/strong> \u2014 Marimo is not a high-profile enterprise platform<\/li>\n<li><strong>AI-assisted exploitation likely<\/strong> \u2014 the speed suggests AI tools were used to analyze the advisory and generate the exploit<\/li>\n<li><strong>Advisory feeds are being monitored<\/strong> \u2014 not just CVE databases, but GitHub security advisories<\/li>\n<\/ul>\n<h2>Indicators of Compromise (IOCs)<\/h2>\n<p>Security teams should monitor for:<\/p>\n<ul>\n<li><strong>Unexpected WebSocket connections<\/strong> to <code>\/terminal\/ws<\/code> endpoint<\/li>\n<li><strong>Unauthenticated shell commands<\/strong> in Marimo process logs: <code>id<\/code>, <code>whoami<\/code>, <code>pwd<\/code>, <code>cat .env<\/code><\/li>\n<li><strong>PoC marker strings<\/strong> in logs: <code>---POC-START---<\/code>, <code>---POC-END---<\/code><\/li>\n<li><strong>Unusual .env file access<\/strong> and reads of AWS credential files<\/li>\n<li><strong>SSH key directory enumeration<\/strong>: access to <code>~\/.ssh<\/code><\/li>\n<li><strong>Reconnaissance commands<\/strong> shortly after WebSocket connection: <code>ls<\/code>, <code>pwd<\/code>, <code>whoami<\/code><\/li>\n<\/ul>\n<h2>Immediate Actions Required<\/h2>\n<ol>\n<li><strong>Upgrade immediately<\/strong> \u2014 Update Marimo to version <strong>0.23.0<\/strong>: <code>pip install --upgrade marimo<\/code><\/li>\n<li><strong>Block \/terminal\/ws<\/strong> \u2014 If upgrade is not immediately possible, block or disable access to the terminal WebSocket endpoint at the firewall or reverse proxy level<\/li>\n<li><strong>Rotate all exposed secrets<\/strong> \u2014 If your instance was internet-exposed, assume .env credentials are compromised: rotate AWS keys, database passwords, API keys, and SSH keys immediately<\/li>\n<li><strong>Restrict network access<\/strong> \u2014 Marimo should never be exposed via <code>--host 0.0.0.0<\/code> to untrusted networks. Use VPN or SSH tunneling for remote access<\/li>\n<li><strong>Run in read-only mode<\/strong> \u2014 If edit mode is not required, deploy with <code>marimo run<\/code> instead of <code>marimo edit<\/code><\/li>\n<li><strong>Monitor WebSocket traffic<\/strong> \u2014 Enable logging for WebSocket connections to detect exploitation attempts<\/li>\n<li><strong>Audit advisory subscriptions<\/strong> \u2014 Subscribe to GitHub Security Advisories for all open-source tools in your stack, not just CVE feeds<\/li>\n<\/ol>\n<h2>The Bigger Picture: The End of the Patch Window<\/h2>\n<p>CVE-2026-39987 is not an anomaly \u2014 it is the new normal. The assumption that defenders have days or weeks to patch after a vulnerability is disclosed is obsolete. Security teams must now operate on the assumption that <strong>any critical advisory can be weaponized within hours<\/strong>, regardless of the software&#8217;s popularity.<\/p>\n<p>The traditional patch management cycle \u2014 assess, test, schedule, deploy \u2014 cannot keep pace with exploitation timelines measured in single-digit hours. Organizations need runtime detection, network segmentation, and rapid credential rotation as first-line defenses when the patch window is measured in hours rather than days.<\/p>\n<p>As Sysdig&#8217;s TRT concluded: <em>&#8220;The attacker needed nothing more than the advisory text and a WebSocket client.&#8221;<\/em><\/p>\n<h2>References<\/h2>\n<ul>\n<li><a href=\"https:\/\/github.com\/marimo-team\/marimo\/security\/advisories\/GHSA-2679-6mx9-h9xc\" rel=\"noopener noreferrer\" target=\"_blank\">Marimo Security Advisory GHSA-2679-6mx9-h9xc<\/a><\/li>\n<li><a href=\"https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2026-39987\" rel=\"noopener noreferrer\" target=\"_blank\">NVD \u2014 CVE-2026-39987<\/a><\/li>\n<li><a href=\"https:\/\/www.sysdig.com\/blog\/marimo-oss-python-notebook-rce-from-disclosure-to-exploitation-in-under-10-hours\" rel=\"noopener noreferrer\" target=\"_blank\">Sysdig TRT Research Report<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/marimo-team\/marimo\/releases\/tag\/0.23.0\" rel=\"noopener noreferrer\" target=\"_blank\">Marimo v0.23.0 Release Notes<\/a><\/li>\n<\/ul>\n<p><em>Written by Tarang Parmar (CEH) \u2014 TheCyberSecurity.Network. Read time: 8 min. Last updated: April 12, 2026.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A critical pre-authentication RCE vulnerability in Marimo, the open-source Python notebook platform, was weaponized by threat actors in under 10 hours of disclosure \u2014 with no public PoC available. Here is the full technical breakdown of the attack chain, attacker TTPs, and what defenders must do right now.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[42],"tags":[37,11,39,14,13,12,40,35],"class_list":["post-110","post","type-post","status-publish","format-standard","hentry","category-vulnerability","tag-critical","tag-cve202639987","tag-cybersecurity","tag-marimo","tag-python","tag-rce","tag-threatintel","tag-vulnerability"],"_links":{"self":[{"href":"https:\/\/thecybersecurity.network\/blog\/wp-json\/wp\/v2\/posts\/110","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thecybersecurity.network\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thecybersecurity.network\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thecybersecurity.network\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thecybersecurity.network\/blog\/wp-json\/wp\/v2\/comments?post=110"}],"version-history":[{"count":0,"href":"https:\/\/thecybersecurity.network\/blog\/wp-json\/wp\/v2\/posts\/110\/revisions"}],"wp:attachment":[{"href":"https:\/\/thecybersecurity.network\/blog\/wp-json\/wp\/v2\/media?parent=110"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thecybersecurity.network\/blog\/wp-json\/wp\/v2\/categories?post=110"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thecybersecurity.network\/blog\/wp-json\/wp\/v2\/tags?post=110"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}