## 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)