A critical code execution vulnerability in Marimo 0.20.4 — the increasingly popular reactive Python notebook framework — has been published to Exploit-DB (entry 52673), putting public exploit code within reach of any attacker with basic skills. Marimo has gained significant traction among data science and machine learning teams over the past year as a modern alternative to Jupyter, which means a growing number of these servers are now running in research environments, CI pipelines, and — far too often — directly exposed to internal networks or the internet with minimal authentication.
This is the same risk pattern we have watched burn organizations repeatedly with Jupyter and JupyterLab over the years: a development convenience tool, designed to execute arbitrary code by design, deployed on a network-reachable socket without an authentication boundary. The difference now is that ML infrastructure is a prime target. Notebook servers frequently hold cloud credentials, API keys, database connection strings, and pre-trained model artifacts — everything an attacker needs for lateral movement and data theft in a single compromise.
If your organization runs Marimo in any shared, containerized, or network-accessible capacity, treat this as an urgent remediation item. Public PoC availability collapses the exploitation timeline from weeks to hours.
Technical Analysis
Affected Product
- Product: Marimo (reactive Python notebook / app framework)
- Affected version: 0.20.4 (prior versions should be assumed vulnerable pending vendor confirmation)
- Platforms: Any OS running the Marimo server — Linux and macOS development workstations, Docker containers, cloud VMs, and increasingly, shared ML platform infrastructure
- Reference: https://www.exploit-db.com/exploits/52673
How the Attack Works
Marimo's architecture is the core issue from a defensive standpoint. When you launch marimo edit, the server exposes a web application whose entire purpose is to accept Python code from the browser and execute it server-side via the kernel. If the server is bound to a non-loopback interface (e.g., --host 0.0.0.0) without an effective authentication layer — no token, no password, or a bypassable one — then anyone who can reach the TCP port can execute arbitrary Python code with the privileges of the Marimo process.
The attack chain from a defender's perspective:
- Discovery: Attacker scans for exposed Marimo instances. The default port (historically 2718, configurable) and distinctive HTTP response fingerprints make mass scanning trivial. Shodan and Censys indexing of exposed notebook servers is well documented across the Jupyter ecosystem; Marimo instances are equally enumerable.
- Access: The attacker interacts with the Marimo web/API endpoints directly — no browser required, just HTTP requests to the code execution path.
- Execution: Arbitrary Python is submitted and run by the server kernel. From there:
os.system,subprocess, or direct interpreter-level access to the filesystem, environment variables, and network. - Post-exploitation: Credential harvesting from environment variables (
AWS_*,OPENAI_API_KEY, database URIs), installation of persistence, pivoting into internal networks, or crypto-mining — the standard post-notebook-compromise playbook we see in IR engagements.
Exploitation Status
- Public PoC: Confirmed — exploit code is publicly available on Exploit-DB (52673). Publication on Exploit-DB means the technique is fully documented and reproducible by low-skill actors.
- CVE: No CVE identifier was published in the source material at time of writing. Track the Marimo GitHub security advisories (https://github.com/marimo-team/marimo/security/advisories) for assignment.
- CISA KEV: Not listed at time of writing, but public-PoC code execution flaws in developer tooling are historically fast-movers for KEV inclusion when internet exposure is significant.
Why This Keeps Happening
Notebook servers are code-execution-as-a-service by design. The security boundary is entirely the authentication layer and the network exposure. When either fails — or when a flaw in the framework bypasses authentication — the result is pre-authenticated remote code execution with zero exploit development required. This is why we treat notebook infrastructure as a Tier-1 attack surface in every assessment, regardless of which framework is in use.
Detection & Response
The highest-fidelity detection strategy focuses on two observables: (1) the Marimo server process spawning unexpected child processes (the tell-tale sign of attacker-issued code), and (2) network exposure of Marimo listener ports to untrusted sources. Both are low-noise in well-managed environments.
Sigma Rules
---
title: Marimo Notebook Server Spawning Suspicious Child Processes
id: 3f8c2a91-7d44-4b6e-9c21-5e8f0a1b2d33
status: experimental
description: Detects Marimo (or its Python interpreter) spawning shells, downloaders, or system utilities consistent with post-exploitation via the Marimo code execution flaw (Exploit-DB 52673). Marimo kernels legitimately execute user Python code, but direct shell spawns from the server process on a headless/server deployment are high-signal.
references:
- https://www.exploit-db.com/exploits/52673
- https://attack.mitre.org/techniques/T1059/006/
author: Security Arsenal
date: 2026/01/14
tags:
- attack.execution
- attack.t1059.006
- attack.t1190
logsource:
category: process_creation
product: linux
detection:
selection_parent:
ParentCommandLine|contains:
- 'marimo'
- '/marimo/'
selection_child_img:
Image|endswith:
- '/bash'
- '/sh'
- '/dash'
- '/zsh'
- '/curl'
- '/wget'
- '/nc'
- '/ncat'
- '/netcat'
- '/socat'
- '/python'
- '/python3'
- '/perl'
- '/base64'
- '/chmod'
- '/chown'
condition: selection_parent and selection_child_img
falsepositives:
- Notebooks legitimately using subprocess/os.system for data pipelines (tune per-environment by user and host)
- Marimo's own tooling invocations during development use
level: high
---
title: Marimo Server Bound to Non-Loopback Interface
id: 9b1d4e57-2c68-4a3f-b780-6d9e1c4a5f22
status: experimental
description: Detects Marimo launched with a wildcard/external bind address, which exposes the code execution interface to the network. This is the precondition for remote exploitation of the Marimo 0.20.4 flaw.
references:
- https://www.exploit-db.com/exploits/52673
- https://attack.mitre.org/techniques/T1190/
author: Security Arsenal
date: 2026/01/14
tags:
- attack.exposure
- attack.t1190
logsource:
category: process_creation
product: linux
detection:
selection_cmd:
CommandLine|contains: 'marimo'
selection_bind:
CommandLine|contains:
- '--host 0.0.0.0'
- '--host=0.0.0.0'
- '--host ::'
- '--host=::'
condition: selection_cmd and selection_bind
falsepositives:
- Intentional shared notebook deployments (these should be behind authenticated reverse proxies — flag for review regardless)
level: medium
---
title: Inbound Connection to Marimo Default Port from External Source
id: 5c7a9e12-8f30-4d1b-a465-2b8c3e6d7f44
status: experimental
description: Detects network connections to the Marimo server default port (2718) where the listener is a Python/marimo process. Useful for identifying reconnaissance and exploitation attempts against exposed instances.
references:
- https://www.exploit-db.com/exploits/52673
- https://attack.mitre.org/techniques/T1190/
author: Security Arsenal
date: 2026/01/14
tags:
- attack.initial_access
- attack.t1190
logsource:
category: network_connection
product: linux
detection:
selection_port:
DestinationPort: 2718
selection_initiated:
Initiated: 'false'
condition: selection_port and selection_initiated
falsepositives:
- Legitimate users accessing shared notebook servers (restrict by source IP ranges in production)
level: medium
KQL — Microsoft Sentinel / Defender
Notebook servers are predominantly Linux workloads, so the primary hunt below targets Syslog and CommonSecurityLog ingestion, with a Defender variant for environments where MDE is deployed on endpoints. Run the exposure query first — it answers the question "do we even have Marimo listening on the network?"
// Hunt 1: Marimo server process spawning shells or tooling (post-exploitation behavior)
// Requires Syslog process audit data (auditd/Sysmon-for-Linux) ingested into Sentinel
Syslog
| where TimeGenerated > ago(7d)
| where Facility == "user" or Facility == "auth" or ProcessName =~ "auditd"
| where SyslogMessage has "marimo" and SyslogMessage has_any ("/bin/bash", "/bin/sh", "curl", "wget", "nc ", "base64", "chmod")
| project TimeGenerated, Computer, ProcessName, SyslogMessage
| order by TimeGenerated desc;
// Hunt 2: Network exposure — inbound connections to Marimo default port via firewall/CEF data
CommonSecurityLog
| where TimeGenerated > ago(7d)
| where DestinationPort == 2718
| summarize ConnectionCount = count(), SourceIPs = make_set(SourceIP, 50) by DestinationIP, DestinationPort, DeviceAction
| order by ConnectionCount desc;
// Hunt 3 (MDE variant): Marimo process tree on managed endpoints
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessCommandLine has "marimo"
| where FileName in~ ("bash", "sh", "dash", "curl", "wget", "nc", "ncat", "socat", "base64", "python3")
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, RemoteIP
| order by TimeGenerated desc;
// Hunt 4 (MDE variant): Listening Marimo ports on endpoints
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where LocalPort == 2718 and ActionType == "ListeningConnection"
| summarize by DeviceName, LocalIP, LocalPort, InitiatingProcessFileName, InitiatingProcessCommandLine
Velociraptor VQL
Use this hunt across your Linux and macOS fleet to identify running Marimo instances, how they're bound, and whether they've spawned suspicious children. This answers both the exposure question and the compromise question in one sweep.
-- Hunt: Marimo server exposure and post-exploitation indicators
-- Identifies running marimo processes, their bind flags, and suspicious child processes
SELECT Pid, Ppid, Name, CommandLine, Exe, Username, CreateTime,
-- Flag high-risk configurations
(CommandLine =~ '(--host[ =](0\\.0\\.0\\.0|::))') AS BoundToWildcard,
(CommandLine =~ 'marimo (edit|run)') AS IsNotebookServer
FROM pslist()
WHERE CommandLine =~ 'marimo'
-- Also catch shells/tools parented to anything python-adjacent on notebook hosts
OR (Name =~ '^(bash|sh|dash|curl|wget|nc|ncat|socat)$'
AND Ppid IN (SELECT Pid FROM pslist() WHERE CommandLine =~ 'marimo'))
Follow up on any hit by enumerating the listener directly:
-- Confirm Marimo listeners and their bind addresses
SELECT Pid, Name, LocalAddress, LocalPort, RemoteAddress, RemotePort, State
FROM netstat()
WHERE LocalPort = 2718
OR Pid IN (SELECT Pid FROM pslist() WHERE CommandLine =~ 'marimo')
Remediation
Immediate Actions (Today)
- Inventory every Marimo instance. You cannot protect what you haven't counted. Sweep your fleet (VQL above), your container registries (
grep -r "marimo" requirements.txt pyproject.tomlacross repos), and your cloud assets for port 2718 or custom Marimo ports. - Upgrade Marimo immediately. Update to the latest release — the fix for this flaw landed after 0.20.4. Run
pip install -U marimoand verify withmarimo --version. Pin the minimum safe version in your dependency manifests once confirmed against the vendor advisory. - Kill external bind addresses. Any instance running with
--host 0.0.0.0that isn't behind an authenticated reverse proxy is an open code execution port. Rebind to127.0.0.1and use SSH port-forwarding or an authenticated gateway for remote access. - Verify no internet exposure. Check cloud security groups, load balancer configs, and Kubernetes Services/Ingresses for any path from untrusted networks to Marimo ports. Search Shodan/Censys for your public IP space.
Verification and Hardening Script
Run this on Linux hosts and containers that may run Marimo. It audits installed versions, flags externally-bound instances, and checks for exposed listeners.
#!/usr/bin/env bash
# Marimo 0.20.4 RCE exposure audit — Security Arsenal
# Checks: installed version, wildcard binds, exposed listeners, auth posture
set -u
echo "=== [1/4] Checking installed marimo version ==="
if command -v marimo >/dev/null 2>&1; then
VER=$(marimo --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
echo "Installed marimo version: ${VER:-unknown}"
if [ "$VER" = "0.20.4" ]; then
echo "[CRITICAL] Vulnerable version 0.20.4 detected. Run: pip install -U marimo"
fi
else
echo "marimo not found in PATH (check virtualenvs and containers separately)"
fi
# Also scan common virtualenv/container site-packages
echo ""
echo "=== [2/4] Scanning for marimo installs in site-packages ==="
find / -type d -name "marimo-0.20.4*" -path "*site-packages*" 2>/dev/null | head -20
echo ""
echo "=== [3/4] Checking for marimo processes bound to non-loopback ==="
if ps aux | grep -i "[m]arimo" | grep -E "\-\-host[= ](0\.0\.0\.0|::)" ; then
echo "[CRITICAL] marimo bound to wildcard interface — remote code execution exposure"
else
echo "No wildcard-bound marimo processes found"
fi
echo ""
echo "=== [4/4] Checking listeners on default marimo port 2718 ==="
ss -tlnp 2>/dev/null | grep -E ":2718" || echo "No listener on 2718"
echo ""
echo "=== Recommendations ==="
echo "1. Upgrade: pip install -U marimo"
echo "2. Bind to loopback: marimo edit --host 127.0.0.1"
echo "3. Use SSH tunneling for remote access: ssh -L 2718:localhost:2718 user@host"
echo "4. If shared access is required, front marimo with an authenticated reverse proxy (OAuth2-Proxy, nginx+SSO)"
echo "5. Rotate any credentials in environment variables of hosts that ran exposed instances"
Strategic Hardening
- Treat all notebook frameworks as privileged code execution endpoints in your asset inventory and threat model — Marimo, Jupyter, JupyterHub, VS Code tunnels, all of them. They belong behind SSO-authenticated gateways, never on raw network sockets.
- Run notebook servers with least privilege. Dedicated service accounts, no cloud instance roles with broad permissions, no persistent cloud credentials in the environment. Use short-lived, workload-identity-based credentials so a compromised kernel yields nothing reusable.
- Segment ML/data science infrastructure from production networks. Notebook compromise is a classic pivot point — we have traced more than one ransomware intrusion back to an exposed data-science VM.
- Add notebook server ports to your continuous attack surface monitoring. A developer running
marimo edit --host 0.0.0.0on a cloud VM for an afternoon demo is exactly how these incidents start. - Credential rotation after exposure. If you find an instance that was reachable and unpatched, assume compromise: rotate every secret in that environment's variables, review kernel execution history where available, and hunt the detection content above across the exposure window.
Monitor the Marimo security advisories page (https://github.com/marimo-team/marimo/security/advisories) for an official CVE assignment and patched-version confirmation, and update this guidance when a fixed version number is formally published.
Related Resources
Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.