Share
## https://sploitus.com/exploit?id=EB8A9513-5D21-5BA5-9728-9CD3EBC3C87F
# CVE-2026-39987 β€” Marimo Python Notebook Pre-Authenticated Remote Code Execution


  
  
  
  


## TL;DR

A **pre-authenticated remote code execution** vulnerability in **Marimo**, an open-source Python notebook for data science and AI/ML. The terminal WebSocket endpoint (`/terminal/ws`) completely **skips authentication validation**, while the neighboring notebook endpoint (`/ws`) correctly enforces it. An unauthenticated attacker can connect to `/terminal/ws` and obtain a **full interactive PTY shell** on the host system with zero credentials.

Exploited in the wild **within 10 hours** of disclosure. Attackers stole AWS credentials in under 3 minutes.

**Affects Marimo β”‚                                β”‚
  β”‚   (User)     β”‚         β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
  β”‚              β”‚β”‚ validate_auth │───>β”‚  Accept  │───>β”‚ Notebook  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                    ❌ Reject if
                    not authenticated

  /terminal/ws (Terminal WebSocket):
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Connect │───>β”‚ Check mode &  │───>β”‚  Accept  │───>β”‚ PTY Shell β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚ platform only β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 ⚠️ No auth check!
                 Anyone gets a shell!
```

### The Missing Auth Check

The notebook endpoint (`/ws`) correctly calls `validate_auth()` to verify the user's identity before accepting WebSocket connections. This is the expected security behavior.

The terminal endpoint (`/terminal/ws`) only checks whether the server is in running mode and whether the platform supports terminal functionality. It **never calls `validate_auth()`**. After passing these basic checks, it accepts the connection and creates a full PTY (pseudo-terminal) session.

```python
# /ws (Notebook) β€” CORRECT implementation
async def websocket_connect(self, message):
    await self.validate_auth()      # βœ… Checks authentication
    await self.accept()
    # ... notebook communication

# /terminal/ws (Terminal) β€” VULNERABLE implementation
async def websocket_connect(self, message):
    if not self.is_running_mode():  # Only checks mode
        await self.close()
        return
    if not self.is_platform_supported():  # Only checks platform
        await self.close()
        return
    await self.accept()             # ❌ No auth! Anyone gets a shell
    # ... PTY shell creation
```

This is **CWE-306: Missing Authentication for Critical Function**. The most dangerous endpoint on the server (the one that provides an interactive shell) has no authentication whatsoever.

### The Attack: Advisory to AWS Keys in 3 Minutes

The exploitation timeline shows how fast modern threat actors operate:

```
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                   CVE-2026-39987 Timeline                    β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚                                                              β”‚
  β”‚  T+0h        Advisory published                              β”‚
  β”‚  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─                       β”‚
  β”‚  T+9h        First exploit built from advisory               β”‚
  β”‚  T+10h       Exploitation in the wild confirmed              β”‚
  β”‚  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─                       β”‚
  β”‚  T+10h 0m    Attacker connects to /terminal/ws               β”‚
  β”‚  T+10h 1m    Full PTY shell obtained                         β”‚
  β”‚  T+10h 2m    .env file located and read                      β”‚
  β”‚  T+10h 3m    AWS keys exfiltrated                            β”‚
  β”‚              Total attack time: ~3 minutes                   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

The attack itself is trivially simple:

```
  Step 1: Attacker opens WebSocket connection to /terminal/ws
          (No authentication needed, no special tools required)

  Step 2: Server creates a PTY (pseudo-terminal) session
          Attacker now has an interactive shell

  Step 3: Attacker runs commands:
          $ cat .env
          AWS_ACCESS_KEY_ID=AKIA...
          AWS_SECRET_ACCESS_KEY=...
          DATABASE_URL=postgres://...
          OPENAI_API_KEY=sk-...

  Step 4: Credentials exfiltrated
          Attacker now has cloud access, database access,
          and API keys for AI services

  Total time: under 3 minutes
  Authentication required: none
  Tools required: any WebSocket client
```

No exploit development needed. No shellcode. No memory corruption. Just a WebSocket client and a missing auth check.

---

## Impact Analysis

**Immediate impact on the Marimo host:**
- Full interactive shell with the privileges of the Marimo process
- Access to all files readable by the process (source code, data, credentials)
- Access to environment variables containing API keys and secrets
- Ability to execute arbitrary commands on the host system

**Credential exposure (the primary attack goal):**
- AWS access keys and secret keys from `.env` files or environment variables
- GCP/Azure service account credentials
- Database connection strings with passwords
- OpenAI, Anthropic, and other AI service API keys
- SSH keys and other authentication material

**Downstream impact (via stolen credentials):**
- Unauthorized access to cloud infrastructure (EC2, S3, Lambda, etc.)
- Data exfiltration from cloud storage and databases
- Resource abuse (cryptomining, AI API credit theft)
- Lateral movement into cloud and on-premise networks

**Risk amplification factors:**
- Notebook environments are designed to execute arbitrary code (that's their purpose)
- Data science environments typically have broad cloud access for training jobs
- Many Marimo instances are exposed to the internet for collaboration and remote work
- Security hardening is often an afterthought in research/experimentation environments

---

## Affected Versions

| Version | Status |
|---------|--------|
| Marimo 0.23.0+ | **Patched** |
| Marimo 0.20.5 to 0.22.x | **Likely vulnerable** (between advisory range and fix) |
| Marimo = 0.23.0)

**No commands are executed on target systems.** The WebSocket handshake is tested but no data is sent through the connection. The check is entirely passive and safe for production.

**Usage:**

```bash
# Install dependencies
pip install -r requirements.txt

# Single target (Marimo default port: 2718)
python CVE-2026-39987_Marimo_RCE_detector.py -t http://marimo-host:2718

# HTTPS target
python CVE-2026-39987_Marimo_RCE_detector.py -t https://marimo-host:443

# Bulk scan from file with verbose output
python CVE-2026-39987_Marimo_RCE_detector.py -f targets.txt -o results.json -v

# Increased timeout for slow networks
python CVE-2026-39987_Marimo_RCE_detector.py -t http://10.0.0.5:2718 --timeout 15
```

**Options:**

| Flag | Description | Default |
|------|-------------|---------|
| `-t`, `--target` | Single target URL (e.g., `http://host:2718`) | β€” |
| `-f`, `--file` | File with target URLs, one per line (`#` comments supported) | β€” |
| `-o`, `--output` | Save results to JSON file | β€” |
| `--timeout` | Connection timeout in seconds | `10` |
| `--verify-ssl` | Enable SSL certificate verification | Disabled |
| `-v`, `--verbose` | Verbose output with full details | Off |

**Example output:**

```
[*] CVE-2026-39987 Marimo Pre-Auth RCE Scanner
[*] Scanning 1 target(s)...
[*] NOTE: This scanner only checks for endpoint exposure.
[*]       No commands are executed on target systems.

======================================================================
Target: http://10.0.0.5:2718
Scan Time: 2026-04-14T16:00:00Z
Risk Level: CRITICAL
======================================================================
  Is Marimo:             YES
  Marimo Version:        0.19.2
  /terminal/ws Open:     YES β€” UNAUTHENTICATED
  Vulnerable:            YES

  *** CRITICAL: Pre-authenticated RCE is possible! ***
  *** An attacker can get a full PTY shell without any auth ***

  Details:
    - Marimo instance detected via /api/status
    - Marimo version: 0.19.2
    - WebSocket upgrade accepted β€” /terminal/ws accessible WITHOUT auth
    - CONFIRMED: /terminal/ws accepts unauthenticated connections while
      /ws requires auth β€” classic CVE-2026-39987 signature
    - Version 0.19.2 

# Scan common ports where Marimo might run
nmap -p 2718,8080,8443,443 --script CVE-2026-39987_Marimo_RCE 

# Subnet scan
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE 10.0.0.0/24

# Scan targets from a file
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE -iL targets.txt

# With service version detection
nmap -sV -p 2718 --script CVE-2026-39987_Marimo_RCE 
```

**Example Nmap output:**

```
PORT     STATE SERVICE
2718/tcp open  http
| CVE-2026-39987_Marimo_RCE:
|   VULNERABLE:
|   Marimo Pre-Auth RCE (CVE-2026-39987)
|     State: VULNERABLE
|     Risk level: CRITICAL
|     Marimo Version: 0.19.2
|     /terminal/ws: accessible without authentication
|     Description:
|       The Marimo /terminal/ws WebSocket endpoint accepts connections
|       without authentication, enabling pre-authenticated RCE.
|       An attacker can obtain a full PTY shell without any credentials.
|     References:
|_      https://nvd.nist.gov/vuln/detail/CVE-2026-39987
```

### Manual Verification

If you have access to the Marimo instance:

```bash
# Check Marimo version via API
curl -s http://:2718/api/status | python3 -m json.tool

# Test WebSocket upgrade on /terminal/ws (should NOT succeed without auth)
curl -s -o /dev/null -w "%{http_code}" \
  -H "Upgrade: websocket" \
  -H "Connection: Upgrade" \
  -H "Sec-WebSocket-Key: dGVzdC1rZXktMTIzNDU2Nzg=" \
  -H "Sec-WebSocket-Version: 13" \
  http://:2718/terminal/ws

# If it returns 101 (Switching Protocols), the endpoint is open without auth
# If it returns 401/403, authentication is enforced (patched or hardened)

# For comparison, test /ws (should always require auth)
curl -s -o /dev/null -w "%{http_code}" \
  -H "Upgrade: websocket" \
  -H "Connection: Upgrade" \
  -H "Sec-WebSocket-Key: dGVzdC1rZXktMTIzNDU2Nzg=" \
  -H "Sec-WebSocket-Version: 13" \
  http://:2718/ws
```

If `/terminal/ws` returns 101 while `/ws` returns 401/403, this is the classic CVE-2026-39987 signature.

---

## Indicators of Compromise

Watch for these signs in your environment:

| Indicator | Where to Check | What to Look For |
|:---|:---|:---|
| Unauthorized WebSocket connections | Server/proxy logs | Connections to `/terminal/ws` from unexpected IPs |
| PTY session creation | Process monitoring | Unexpected shell processes spawned by the Marimo server |
| File access | File audit logs | Reads of `.env`, credential files, or SSH keys |
| Credential usage | Cloud provider audit logs | API calls using keys that were stored in the Marimo environment |
| Outbound data transfer | Network monitoring | Unusual egress traffic from the Marimo host |

**Investigation commands:**

```bash
# Check for active WebSocket connections
ss -tnp | grep 

# Review process tree for unexpected shells
ps aux --forest | grep -A5 marimo

# Check if .env or credential files were recently accessed
stat .env
stat ~/.aws/credentials

# Review cloud provider activity logs for unauthorized access
aws cloudtrail lookup-events --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=

# Check for unauthorized outbound connections
netstat -tnp | grep ESTABLISHED | grep -v 127.0.0.1
```

---

## Remediation

**Immediate actions (do these now):**

1. **Upgrade to Marimo 0.23.0** or later (`pip install --upgrade marimo`)
2. **Restrict network access** to Marimo instances via firewall rules (bind to localhost or trusted IPs only)
3. **Check for unauthorized connections** in your server logs

**Short-term (this week):**

4. **Rotate ALL credentials** that were accessible from Marimo environments (AWS keys, API tokens, database passwords, cloud service accounts)
5. **Audit cloud provider activity logs** for unauthorized API calls using potentially compromised credentials
6. **Review `.env` files and environment variables** for any sensitive data that may have been exposed
7. **Check for persistence mechanisms** (unauthorized SSH keys, cron jobs, modified startup scripts)

**Long-term:**

8. **Never expose notebook environments directly to the internet** (use VPN or SSH tunnels)
9. **Treat AI/ML development environments as high-security assets** (they contain cloud credentials and have code execution capabilities)
10. **Include notebook servers in your regular vulnerability scanning program**
11. **Implement network monitoring** for notebook server instances with alerting on unexpected connections

---

## References

- [The Hacker News β€” Marimo RCE Flaw CVE-2026-39987 Exploited Within 10 Hours](https://thehackernews.com/2026/04/marimo-rce-flaw-cve-2026-39987.html)
- [SecurityWeek β€” Critical Marimo Flaw Exploited Hours After Public Disclosure](https://www.securityweek.com/critical-marimo-flaw-exploited-hours-after-public-disclosure/)
- [BleepingComputer β€” Critical Marimo Pre-Auth RCE Flaw Now Under Active Exploitation](https://www.bleepingcomputer.com/news/security/critical-marimo-pre-auth-rce-flaw-now-under-active-exploitation/)
- [Security Affairs β€” CVE-2026-39987: Marimo RCE Exploited in Hours After Disclosure](https://securityaffairs.com/190623/hacking/cve-2026-39987-marimo-rce-exploited-in-hours-after-disclosure.html)
- [CSA Labs β€” Marimo Pre-Auth RCE: AI Development Toolchain Under Attack](https://labs.cloudsecurityalliance.org/research/csa-research-note-marimo-rce-cve-2026-39987-ai-toolchain-202/)
- [GBHackers β€” Marimo RCE Vulnerability Exploited Within 10 Hours](https://gbhackers.com/marimo-rce-vulnerability-exploited/)
- [InfoWorld β€” Critical Flaw in Marimo Python Notebook Exploited Within 10 Hours](https://www.infoworld.com/article/4157823/critical-flaw-in-marimo-python-notebook-exploited-within-10-hours-of-disclosure-2.html)

---

## Author

**Kerem OruΓ§** β€” Cybersecurity Engineer

- GitHub: [@keraattin](https://github.com/keraattin)
- Twitter: [@keraattin](https://twitter.com/keraattin)