
Kit de detecção e exploração para CVE-2026-39987, um RCE pré-autenticação em notebooks Marimo. Inclui scanner em Python e script NSE do Nmap para identificar instâncias vulneráveis por meio de verificações de endpoint WebSocket.
Uma vulnerabilidade de execução remota de código pré-autenticada no Marimo, um notebook Python de código aberto para ciência de dados e IA/ML. O endpoint WebSocket do terminal (/terminal/ws) ignora completamente a validação de autenticação, enquanto o endpoint vizinho do notebook (/ws) a aplica corretamente. Um atacante não autenticado pode se conectar a /terminal/ws e obter um no sistema host sem nenhuma credencial.
Explorada ativamente em menos de 10 horas após a divulgação. Atacantes roubaram credenciais AWS em menos de 3 minutos.
Afeta Marimo <= 0.20.4. Corrigido no Marimo 0.23.0.
| Campo | Detalhe |
|---|---|
| ID CVE | CVE-2026-39987 |
| Fornecedor | Projeto Marimo |
| Produto | Marimo (Notebook Python) |
| Versões Afetadas | <= 0.20.4 |
| CVSS v3.1 | 9.3 (Crítico) |
| CWE | CWE-306 — Autenticação Ausente para Função Crítica |
| Vetor de Ataque | Rede |
| Autenticação | Nenhuma necessária |
| Interação do Usuário | Nenhuma |
| Maturidade do Exploit | Explorado ativamente na natureza |
| Tempo até a Exploração | ~10 horas após a divulgação |
| Corrigido Em | Marimo 0.23.0 |
O Marimo é um notebook Python reativo de código aberto, projetado como uma alternativa moderna ao Jupyter. Ele é construído para ciência de dados, experimentação em IA/ML e análise interativa de dados. Seus principais recursos incluem rastreamento automático de dependências, execução reproduzível e uma experiência de desenvolvimento mais limpa em comparação aos notebooks tradicionais.
O Marimo está ganhando rápida adoção na comunidade Python e de IA/ML, especialmente entre profissionais que desejam fluxos de trabalho de notebook mais estruturados do que o Jupyter oferece.
Como todos os ambientes de notebook, as instâncias do Marimo normalmente têm acesso a recursos sensíveis: credenciais de nuvem (AWS, GCP, Azure), strings de conexão de banco de dados, chaves de API para serviços de IA (OpenAI, Anthropic, etc.) e acesso à rede interna. Diferentemente de aplicações web tradicionais, os notebooks são projetados para executar código arbitrário. Esse é o seu propósito central.
Essa combinação torna qualquer bypass de autenticação em um ambiente de notebook particularmente devastador.``` Typical Marimo Deployment:
┌──────────────┐ ┌────────────────────────────────┐ │ │ HTTP │ Marimo Server │ │ Browser │────────>│ │ │ (User) │ │ ┌──────────────────────────┐ │ │ │<────────│ │ /ws (Notebook) │ │ └──────────────┘ WS │ │ ✅ validate_auth() │ │ │ └──────────────────────────┘ │ │ │ │ ┌──────────────────────────┐ │ │ │ /terminal/ws │ │ │ │ ❌ NO AUTH CHECK │ │ │ └──────────────────────────┘ │ │ │ │ ┌──────────────────────────┐ │ │ │ Python Environment │ │ │ │ .env files │ │ │ │ AWS credentials │ │ │ │ API keys │ │ │ └──────────────────────────┘ │ └────────────────────────────────┘
---
## Análise Aprofundada da Vulnerabilidade
### Os Dois Endpoints WebSocket
O servidor do Marimo implementa vários endpoints WebSocket para diferentes funcionalidades. A diferença crítica entre os dois endpoints principais é a presença (ou ausência) de uma verificação de autenticação:```
Authentication Flow Comparison:
/ws (Notebook WebSocket):
┌─────────┐ ┌───────────────┐ ┌──────────┐ ┌───────────┐
│ Connect │───>│ 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!
O endpoint do notebook (/ws) chama corretamente validate_auth() para verificar a identidade do usuário antes de aceitar conexões WebSocket. Este é o comportamento de segurança esperado.
O endpoint do terminal (/terminal/ws) apenas verifica se o servidor está em modo de execução e se a plataforma suporta a funcionalidade de terminal. Ele nunca chama validate_auth(). Após passar por essas verificações básicas, ele aceita a conexão e cria uma sessão completa de PTY (pseudo-terminal).```python
async def websocket_connect(self, message): await self.validate_auth() # ✅ Checks authentication await self.accept() # ... notebook communication
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
Este é o **CWE-306: Ausência de Autenticação para Função Crítica**. O endpoint mais perigoso do servidor (aquele que fornece um shell interativo) não possui autenticação alguma.
### O Ataque: De Advisory a Chaves AWS em 3 Minutos
A linha do tempo da exploração mostra a rapidez com que os agentes de ameaças modernos operam:```
┌──────────────────────────────────────────────────────────────┐
│ 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 │
└──────────────────────────────────────────────────────────────┘
O ataque em si é trivialmente simples:``` 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
Sem necessidade de desenvolvimento de exploit. Sem shellcode. Sem corrupção de memória. Apenas um cliente WebSocket e uma verificação de autenticação ausente.
---
## Análise de Impacto
**Impacto imediato no host Marimo:**
- Shell interativo completo com os privilégios do processo Marimo
- Acesso a todos os arquivos legíveis pelo processo (código-fonte, dados, credenciais)
- Acesso a variáveis de ambiente contendo chaves de API e segredos
- Capacidade de executar comandos arbitrários no sistema host
**Exposição de credenciais (o principal objetivo do ataque):**
- Chaves de acesso AWS e chaves secretas de arquivos `.env` ou variáveis de ambiente
- Credenciais de contas de serviço GCP/Azure
- Strings de conexão de banco de dados com senhas
- Chaves de API de serviços de IA como OpenAI, Anthropic e outros
- Chaves SSH e outros materiais de autenticação
**Impacto downstream (via credenciais roubadas):**
- Acesso não autorizado à infraestrutura em nuvem (EC2, S3, Lambda, etc.)
- Exfiltração de dados de armazenamento em nuvem e bancos de dados
- Abuso de recursos (mineração de criptomoedas, roubo de créditos de API de IA)
- Movimentação lateral em redes em nuvem e locais
**Fatores de amplificação de risco:**
- Ambientes de notebook são projetados para executar código arbitrário (esse é o seu propósito)
- Ambientes de ciência de dados normalmente têm amplo acesso à nuvem para trabalhos de treinamento
- Muitas instâncias Marimo estão expostas à internet para colaboração e trabalho remoto
- O endurecimento de segurança é frequentemente uma reflexão tardia em ambientes de pesquisa/experimentação
---
## Versões Afetadas
| Versão | Status |
|---------|--------|
| Marimo 0.23.0+ | **Corrigido** |
| Marimo 0.20.5 a 0.22.x | **Provavelmente vulnerável** (entre o intervalo do advisory e a correção) |
| Marimo <= 0.20.4 | **Vulnerável** (intervalo confirmado) |
---
## O Panorama Geral: Cadeia de Ferramentas de IA/ML Sob Ataque
O CVE-2026-39987 não é um incidente isolado. Faz parte de um padrão claro que surgiu em abril de 2026:
| CVE | Produto | Tipo | Status |
|:---|:---|:---|:---|
| **CVE-2026-39987** | Marimo | RCE Pré-Autenticação (WebSocket) | Explorado em 10 horas |
| **CVE-2026-33017** | Langflow | RCE | CISA KEV (26 de março) |
| **CVE-2026-5059** | aws-mcp-server | Injeção de Comando RCE | Advisory público |
| **TorchGeo** | TorchGeo | RCE via eval() | Advisory público |
Quatro ferramentas de desenvolvimento de IA/ML atingidas por vulnerabilidades críticas de RCE em um único mês. O pipeline de desenvolvimento de IA/ML está se tornando a nova TI invisível: ferramentas implantadas com amplo acesso, supervisão mínima de segurança e repositórios ricos de credenciais.```
┌─────────────────────────────────────────────────┐
│ Why AI/ML Tools Are Prime Targets │
├─────────────────────────────────────────────────┤
│ │
│ 1. DESIGNED to execute arbitrary code │
│ (that's literally what notebooks do) │
│ │
│ 2. Run with elevated privileges │
│ (GPU access, cloud SDKs, network access) │
│ │
│ 3. Contain high-value credentials │
│ (AWS keys, API tokens, DB connections) │
│ │
│ 4. Often exposed to the network │
│ (for collaboration and remote access) │
│ │
│ 5. Security hardening is an afterthought │
│ (focus on features and UX, not security) │
│ │
│ 6. Users are researchers, not security experts │
│ (default configs, weak passwords, no VPN) │
└─────────────────────────────────────────────────┘
O script Python deteta instâncias vulneráveis do Marimo através de uma análise em várias etapas.
Como funciona:
/api/status, /api/health e / para indicadores do Marimo no conteúdo e nos cabeçalhos das respostas/terminal/ws (sem dados transmitidos através da ligação)/terminal/ws (deve exigir autenticação) com /ws (sabe-se que exige autenticação) para confirmar a inconsistênciaNenhum comando é executado nos sistemas alvo. O handshake WebSocket é testado, mas nenhum dado é enviado através da ligação. A verificação é totalmente passiva e segura para produção.
Utilização:```bash
pip install -r requirements.txt
python CVE-2026-39987_Marimo_RCE_detector.py -t http://marimo-host:2718
python CVE-2026-39987_Marimo_RCE_detector.py -t https://marimo-host:443
python CVE-2026-39987_Marimo_RCE_detector.py -f targets.txt -o results.json -v
python CVE-2026-39987_Marimo_RCE_detector.py -t http://10.0.0.5:2718 --timeout 15
**Opções:**
| Bandeira | Descrição | Padrão |
|------|-------------|---------|
| `-t`, `--target` | URL de destino única (ex.: `http://host:2718`) | — |
| `-f`, `--file` | Arquivo com URLs de destino, uma por linha (comentários com `#` suportados) | — |
| `-o`, `--output` | Salvar resultados em arquivo JSON | — |
| `--timeout` | Tempo limite de conexão em segundos | `10` |
| `--verify-ssl` | Ativar verificação de certificado SSL | Desativado |
| `-v`, `--verbose` | Saída detalhada com informações completas | Desativado |
**Exemplo de saída:**```
[*] 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 <= 0.20.4 — VULNERABLE to pre-auth RCE
======================================================================
[*] Scan Complete: 1 targets scanned
[*] Marimo Instances: 1 | Vulnerable: 1 | Critical: 1
======================================================================
sudo cp CVE-2026-39987_Marimo_RCE.nse /usr/share/nmap/scripts/ sudo nmap --script-updatedb
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE
nmap -p 2718,8080,8443,443 --script CVE-2026-39987_Marimo_RCE
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE 10.0.0.0/24
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE -iL targets.txt
nmap -sV -p 2718 --script CVE-2026-39987_Marimo_RCE
**Exemplo de saída do Nmap:**```
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
Se você tiver acesso à instância Marimo:```bash
curl -s http://:2718/api/status | python3 -m json.tool
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
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
Se `/terminal/ws` retornar 101 enquanto `/ws` retornar 401/403, esta é a assinatura clássica do CVE-2026-39987.
---
## Indicadores de Comprometimento
Fique atento a estes sinais no seu ambiente:
| Indicador | Onde Verificar | O Que Procurar |
|:---|:---|:---|
| Conexões WebSocket não autorizadas | Logs do servidor/proxy | Conexões a `/terminal/ws` a partir de IPs inesperados |
| Criação de sessões PTY | Monitoramento de processos | Processos de shell inesperados gerados pelo servidor Marimo |
| Acesso a arquivos | Logs de auditoria de arquivos | Leituras de `.env`, arquivos de credenciais ou chaves SSH |
| Uso de credenciais | Logs de auditoria do provedor de nuvem | Chamadas de API usando chaves armazenadas no ambiente Marimo |
| Transferência de dados de saída | Monitoramento de rede | Tráfego de saída incomum do host Marimo |
**Comandos de investigação:**```bash
# Check for active WebSocket connections
ss -tnp | grep <MARIMO_PORT>
# 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=<KEY_ID>
# Check for unauthorized outbound connections
netstat -tnp | grep ESTABLISHED | grep -v 127.0.0.1
Ações imediatas (faça agora):
pip install --upgrade marimo)Curto prazo (nesta semana):
.env e as variáveis de ambiente em busca de dados confidenciais que possam ter sido expostosLongo prazo:
Kerem Oruç — Engenheiro de Cibersegurança