← voltar
CVE-2026-39987criticalsob ataqueCWE-306

marimo Affected by Pre-Auth Remote Code Execution via Terminal WebSocket Authentication Bypass

100Vexday Risk Score

Corrija agora. Ela está sob exploração confirmada pelo CISA e tem exploit funcional público.

ssvc Actcvss 9.3epss 97%
da publicação à arma4 dias
Publicada no NVD9 de abr.
1ª PoC+4d
CISA KEV+14d
probabilidade de exploração
97%top 1% das CVEs
exploração observada
simCISA + VulnCheck
15 exploit(s) público(s)
Ação exigida pela CISAprazo federal: 2026-05-07

Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.

Resumo

O endpoint WebSocket /terminal/ws do marimo (notebook Python reativo) aceita conexões sem qualquer verificação de autenticação, enquanto o endpoint /ws equivalente valida corretamente o token de acesso. Isso permite que um atacante não autenticado abra uma sessão PTY completa e execute comandos arbitrários no host — em containers Docker padrão, como root. O impacto real depende de o marimo estar rodando em modo 'edit' e acessível pela rede; notebooks publicados como aplicação ('run' mode) não são afetados.

Detalhamento técnico

A falha é CWE-306 (Missing Authentication for Critical Function). Em marimo/_server/api/endpoints/terminal.py, o handler do WebSocket /terminal/ws verifica apenas se a sessão está em SessionMode.EDIT e se a plataforma suporta terminal (supports_terminal()) antes de chamar websocket.accept() e, na sequência, pty.fork() para criar um shell interativo. Nenhuma chamada a validate_auth() existe nesse caminho. O endpoint /ws, por comparação, instancia um WebSocketConnectionValidator e chama validate_auth() antes de aceitar a conexão — exatamente o passo que falta no terminal.

O marimo usa o AuthenticationMiddleware do Starlette, que marca uma conexão sem credenciais como UnauthenticatedUser mas não a rejeita automaticamente; a aplicação real do controle de acesso depende de cada endpoint chamar validate_auth() ou usar o decorator @requires(). Como /terminal/ws não tem nenhum dos dois, o middleware global não protege essa rota — é uma falha de implementação pontual, não uma falha estrutural do framework de autenticação como um todo.

O atacante não controla nenhum parâmetro de entrada sofisticado: basta abrir a conexão WebSocket. A partir daí, o texto enviado é interpretado como entrada de terminal (PTY), então qualquer comando de shell é executado com os privilégios do processo do servidor marimo — root, nos exemplos de reprodução em Docker publicados no advisory.

Como é explorada

O vetor é uma conexão WebSocket direta a ws://alvo:porta/terminal/ws, sem cabeçalho de autenticação, sem token e sem interação do usuário. A complexidade é trivial: uma única conexão aceita retorna um shell PTY completo, com saída de terminal em texto simples — o PoC público do advisory apenas conecta, drena a saída inicial e envia comandos como 'id\n' para confirmar execução como root.

As pré-condições reais que a manchete 'RCE pré-autenticação' esconde: o servidor precisa estar em modo edit (marimo edit), não em modo de aplicação publicada (run mode, que não expõe esse endpoint com o mesmo efeito); e precisa estar acessível pela rede do atacante — isso cobre cenários como marimo edit --host 0.0.0.0, instâncias expostas em rede compartilhada, ou deploys em modo edit que dependem apenas do token de acesso embutido do marimo (marimo edit --token) para segurança, já que esse token nunca é checado nesse endpoint.

A CVE está no catálogo KEV da CISA (adicionada em 2026-04-23, prazo de correção 2026-05-07), confirmando exploração ativa observada. Há template Nuclei público e reportes de pesquisadores (Sysdig) descrevendo o intervalo entre divulgação e exploração em produção como inferior a 10 horas, o que indica varredura automatizada em massa assim que o detalhe técnico ficou público.

Versões

Afetadas
marimo <= 0.20.4, segundo o advisory oficial (GHSA-2679-6mx9-h9xc). A descrição da CVE publicada pelo fornecedor, porém, afirma 'prior to 0.23.0' de forma mais ampla — há uma divergência não resolvida entre as duas fontes oficiais sobre se versões entre 0.20.4 e 0.23.0 também estavam vulneráveis; trate qualquer versão anterior a 0.23.0 como potencialmente exposta até confirmação em changelog específico.
Corrigidas em
0.23.0 (correção aplicada no commit c24d480, PR #9098, que adiciona a chamada a validate_auth() no endpoint /terminal/ws). Não há menção de backport para ramos anteriores no material revisado.

Como se proteger

A correção definitiva é atualizar para marimo 0.23.0 ou posterior, onde /terminal/ws passa a chamar validate_auth() da mesma forma que /ws, retornando WebSocketCodes.UNAUTHORIZED quando a autenticação falha ou o token é inválido.

Se a atualização não for possível de imediato, o paliativo real é de rede, não de configuração do marimo: não expor instâncias em modo edit em interfaces além de 127.0.0.1 (evitar --host 0.0.0.0 sem controle adicional), e restringir acesso à porta do marimo via firewall, VPN ou proxy autenticado que filtre o caminho /terminal/ws antes de chegar à aplicação. Rodar os notebooks apenas em modo de aplicação publicada (run mode), quando o caso de uso permitir, remove a superfície mais crítica, já que esse modo não expõe o mesmo caminho de execução de shell.

O que não funciona como mitigação: confiar no token de acesso embutido do marimo (--token) ou assumir que o AuthenticationMiddleware do Starlette protege todos os WebSockets — como o advisory demonstra, esse middleware não rejeita conexões sozinho, e o endpoint do terminal simplesmente nunca consultava o resultado da autenticação. Também não há flag de configuração documentada para desabilitar seletivamente o terminal sem aplicar o patch.

Como detectar

Em logs do servidor marimo ou de proxy/reverse-proxy na frente dele, procure handshakes WebSocket para o caminho /terminal/ws sem o parâmetro access_token esperado, ou vindos de IPs fora da faixa de administradores conhecidos. No nível de processo, monitore o processo marimo criando processos-filho via pty.fork() (novos PTYs/shells) sem correlação com uma sessão de edição legítima no navegador do operador — isso é o sinal mais direto de exploração bem-sucedida.

Como o exploit é uma única conexão WebSocket com payload textual simples (comandos de shell em texto puro), não há assinatura de rede complexa: qualquer ferramenta de scanning que teste a existência do endpoint /terminal/ws e envie um comando de teste (como 'id') é suficiente para comprometer um alvo vulnerável, e o template Nuclei público automatiza exatamente esse teste — presença desse tipo de requisição nos logs de acesso indica tentativa, não necessariamente sucesso, que só se confirma pela criação do PTY no host.

Pesquisado e redigido com IA a partir do advisory do fornecedor e de análises públicas, com as fontes acima. Confira sempre a versão corrigida no advisory oficial antes de agir.
marimo is a reactive Python notebook. Prior to 0.23.0, Marimo has a Pre-Auth RCE vulnerability. The terminal WebSocket endpoint /terminal/ws lacks authentication validation, allowing an unauthenticated attacker to obtain a full PTY shell and execute arbitrary system commands. Unlike other WebSocket endpoints (e.g., /ws) that correctly call validate_auth() for authentication, the /terminal/ws endpoint only checks the running mode and platform support before accepting connections, completely skipping authentication verification. This vulnerability is fixed in 0.23.0.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Produtos afetados
marimo-team · marimo
⚠ Recursos públicos, para você avaliar a exposição de sistemas que controla ou está autorizado a testar. Teste apenas com autorização.