Shopping cart

Subtotal $0.00

View cartCheckout

Building better devs

TnewsTnews
  • Home
  • Notícias
  • 10 Horas: Hackers Exploraram Bug Crítico no Marimo Antes de Qualquer Patch
Notícias

10 Horas: Hackers Exploraram Bug Crítico no Marimo Antes de Qualquer Patch

Email : 46

10 Horas: Hackers Exploraram Bug Crítico no Marimo Antes de Qualquer Patch

Imagine publicar um aviso de segurança sobre uma vulnerabilidade crítica e, menos de 10 horas depois, alguém já estar explorando a falha em servidores reais. Não é cenário hipotético. Aconteceu na semana passada com o Marimo, um notebook Python que vinha ganhando tração como alternativa moderna ao Jupyter.

A CVE-2026-39987 recebeu nota 9.3 no CVSS — quase o máximo possível. E o pior: o exploit é ridiculamente simples. Uma única requisição WebSocket, sem autenticação, dá ao atacante um shell completo na máquina. Root em muitos casos.

Se você trabalha com data science, machine learning ou simplesmente usa notebooks Python no dia a dia, precisa entender o que aconteceu aqui. Porque essa história não é só sobre o Marimo — é sobre um padrão de falha que se repete em dezenas de ferramentas open source — como já vimos com o hack do Axios no NPM.

O que é o Marimo (e por que tanta gente migrou pra ele)

O Marimo surgiu como uma resposta às frustrações clássicas do Jupyter. Sabe aquele problema de rodar células fora de ordem e o notebook inteiro quebrar? O Marimo resolve isso com execução reativa: quando você muda uma variável, todas as células que dependem dela são recalculadas automaticamente.

Além disso, os notebooks são salvos como arquivos .py puros — nada de JSON monstruoso. Dá pra versionar com Git sem querer arrancar os cabelos. E tem SQL embutido, sliders interativos, integração com AI assistants, export pra HTML… Entendeu o apelo.

A ferramenta cresceu rápido. No GitHub, o projeto ultrapassou 12 mil stars. Pesquisadores, cientistas de dados e engenheiros de ML adotaram o Marimo como substituto do Jupyter em workflows de produção. Empresas começaram a rodar instâncias em servidores compartilhados, muitas vezes expostas na internet com o modo de edição habilitado.

E é exatamente aí que a bomba explodiu.

A falha: um endpoint WebSocket sem autenticação

Pra entender a vulnerabilidade, precisa saber que o Marimo roda como uma aplicação web local (ou remota) usando o framework Starlette. Ele expõe vários endpoints WebSocket para funcionalidades como execução de código, comunicação com o kernel e — aqui mora o problema — um terminal integrado.

O endpoint principal /ws faz tudo direitinho: chama validate_auth() antes de aceitar qualquer conexão WebSocket. Sem token válido, a conexão é rejeitada. Textbook security.

Mas o endpoint /terminal/ws? Alguém esqueceu de colocar a mesma verificação.

O código vulnerável fica em marimo/_server/api/endpoints/terminal.py, nas linhas 340 a 356. O servidor checa se está no modo de edição e se a plataforma suporta PTY. Se ambos forem verdadeiros, chama websocket.accept() direto. Nenhuma verificação de credenciais.


# Código vulnerável (simplificado)
async def terminal_ws(websocket):
    if not is_edit_mode():
        await websocket.close()
        return
    if not platform_supports_pty():
        await websocket.close()
        return
    # Cadê o validate_auth()? Sumiu.
    await websocket.accept()  # Aceita qualquer um
    # Aloca um shell PTY pro atacante
    pid, fd = pty.fork()

Compare com o endpoint protegido:


# Endpoint /ws (protegido corretamente)
async def ws_endpoint(websocket):
    validator = WebSocketConnectionValidator()
    if not await validator.validate_auth(websocket):
        await websocket.close(code=4001)
        return
    await websocket.accept()

A diferença é gritante. Um endpoint valida, o outro não. E o que não valida é justamente o que dá acesso a um shell interativo.

“Middleware resolve, né?” Não resolve.

Alguém pode argumentar: “Mas o Marimo usa AuthenticationMiddleware do Starlette. Isso não deveria barrar a conexão?”

A real é que middlewares de autenticação em WebSocket funcionam diferente de HTTP. O middleware pode marcar a conexão como não autenticada, mas ele não fecha o WebSocket por conta própria. A responsabilidade de rejeitar a conexão fica com o endpoint.

É um footgun clássico do Starlette (e de outros frameworks ASGI, como FastAPI). O middleware processa a requisição, seta request.user como não autenticado, e segue em frente. Se o endpoint não checar esse estado, a conexão passa.

Já vi esse padrão em pelo menos três outros projetos open source nos últimos dois anos. É quase uma armadilha — o dev pensa que o middleware está protegendo, mas na prática ele só está rotulando.

Componente Faz autenticação?
AuthenticationMiddleware Marca como (não) autenticado, mas não bloqueia WebSocket
Endpoint /ws Sim — chama validate_auth()
Endpoint /terminal/ws Não — aceita qualquer conexão

O exploit: shell completo em uma requisição

A cadeia de ataque é absurdamente simples:

  1. O atacante abre uma conexão WebSocket para ws://alvo:porta/terminal/ws
  2. O servidor aceita sem pedir credenciais
  3. O servidor aloca um pseudo-terminal (PTY) e um processo shell
  4. O atacante agora tem um terminal interativo no servidor — como se tivesse SSH

Não precisa de exploit chain sofisticado. Não precisa de buffer overflow. Não precisa de nada além de um cliente WebSocket e o endereço do alvo. É o tipo de vulnerabilidade que um script kiddie consegue explorar em 5 minutos.

E quando o Marimo roda em Docker sem configuração especial? O processo geralmente é root. Shell root. Sem autenticação. Na internet aberta.

9 horas e 41 minutos

A Sysdig, empresa de segurança especializada em runtime e containers, mantém honeypots espalhados pela internet monitorando esse tipo de atividade. E eles capturaram o primeiro ataque real à CVE-2026-39987 exatamente 9 horas e 41 minutos após a publicação do advisory.

O atacante não usou nenhuma ferramenta automatizada sofisticada. Conectou ao /terminal/ws, obteve o shell e começou a explorar manualmente. O padrão de comportamento, segundo a Sysdig:

  1. Reconhecimento do ambientewhoami, uname -a, ls /, exploração do filesystem
  2. Coleta de credenciais — leitura do .env, busca por chaves SSH em ~/.ssh/, inspeção de arquivos de configuração
  3. Persistência — tentativa de criar chaves SSH autorizadas e backdoors

O atacante conectou quatro vezes ao longo de 90 minutos, com pausas entre as sessões. Era alguém operando manualmente, não um worm automatizado. Isso sugere um threat actor direcionado que estava monitorando advisories de segurança em tempo real.

Em menos de 3 minutos após a conexão inicial, o atacante já tinha completado uma operação de roubo de credenciais. Três minutos.

186 servidores expostos — 30 vulneráveis

A Endor Labs, que publicou uma das análises técnicas mais detalhadas sobre a CVE, fez um scan independente e encontrou 186 instâncias do Marimo acessíveis pela internet. Dessas, 30 (cerca de 16%) aceitaram handshakes WebSocket não autenticados no /terminal/ws.

Dezesseis por cento. Quase um em cada cinco servidores Marimo expostos era explorável sem nenhuma credencial.

E esse número provavelmente é conservador. Segundo a Endor Labs, a exposição real “provavelmente abrange dezenas, talvez centenas” de instâncias não catalogadas rodando em redes corporativas, ambientes de pesquisa e clusters de ML.

O problema se agrava quando você considera o que geralmente roda junto com o Marimo:

  • Plataformas de orquestração de AI/ML (que já são alvos frequentes) — com acesso a datasets e modelos
  • Front-ends de LLM — com tokens de API que custam dinheiro
  • Repositórios de código-fonte — com acesso a toda a base de código
  • Arquivos .env com credenciais de cloud — AWS, GCP, Azure tokens

Um RCE pré-autenticação nesses sistemas não é só sobre aquela máquina. É uma porta de entrada para movimentação lateral, acesso a bancos de dados internos, APIs e comprometimento de contas cloud inteiras.

A corrida entre advisory e exploit

O que essa história ilustra de forma brutal é a janela de exploração cada vez menor entre a publicação de uma vulnerabilidade e o primeiro ataque.

Dez anos atrás, você publicava um CVE e tinha semanas — às vezes meses — antes que alguém criasse um exploit funcional. A realidade de 2026 é outra. Atacantes monitoram feeds de vulnerabilidade em tempo real. Muitos usam LLMs para acelerar a criação de exploits a partir de advisories. Quando a descrição técnica é detalhada o suficiente (como foi o caso da CVE-2026-39987), o exploit praticamente se escreve sozinho.

A Sysdig documentou esse fenômeno com dados concretos:

Evento Tempo
Advisory publicado T+0h
Primeiro exploit em honeypot T+9h 41min
Credenciais roubadas T+9h 44min
CISA adiciona ao KEV ~T+24h

A CISA (Cybersecurity and Infrastructure Security Agency dos EUA) adicionou a CVE-2026-39987 ao seu catálogo de Vulnerabilidades Exploradas Conhecidas (KEV) com prazo de remediação para 11 de abril de 2026 — hoje. Se você é uma agência federal americana e ainda não patcheou, está em violação.

Como se proteger

Se você usa o Marimo, a ação é simples e urgente:

1. Atualize para a versão 0.23.0 ou superior


pip install --upgrade "marimo>=0.23.0"

A versão 0.23.0 aplica validate_auth() ao endpoint /terminal/ws, fechando a brecha.

2. Nunca exponha o modo de edição na internet

O Marimo tem dois modos: edição (edit) e visualização (run). O modo de edição é para desenvolvimento local. Se você precisa compartilhar notebooks, use o modo run:


# Modo seguro para produção
marimo run notebook.py

# NUNCA faça isso em produção
marimo edit notebook.py --host 0.0.0.0

3. Use rede privada ou VPN

Se o modo de edição é necessário para sua equipe, coloque atrás de uma VPN ou em uma rede privada. Nunca exponha na internet aberta.

4. Rode containers como non-root


FROM python:3.12-slim
RUN useradd -m marimo
USER marimo
WORKDIR /home/marimo
RUN pip install marimo

Se alguém explorar a falha em uma versão não patcheada, pelo menos não vai ganhar root direto.

5. Segregue credenciais

Não coloque arquivos .env com tokens de cloud no mesmo ambiente onde roda o Marimo. Use secret managers (AWS Secrets Manager, HashiCorp Vault, etc.) e injete apenas o necessário.

6. Monitore conexões WebSocket

Adicione alertas para conexões WebSocket incomuns no path /terminal/ws, especialmente de IPs externos:


# Exemplo com logs do nginx
grep "terminal/ws" /var/log/nginx/access.log | grep -v "sua_rede_interna"

A lição maior: WebSocket é um mundo diferente

O padrão de falha do Marimo não é único. WebSockets têm uma semântica fundamentalmente diferente de requisições HTTP stateless. Um request HTTP chega, é processado e acabou. Um WebSocket é uma conexão persistente — é como abrir uma porta e deixar alguém entrar para ficar o tempo que quiser.

Eu já auditei código de aplicações ASGI onde exatamente esse padrão aparecia: o dev protegia todas as rotas REST com autenticação, mas as rotas WebSocket ficavam desprotegidas porque “o middleware cuida disso”. Spoiler: o middleware não cuida. Pelo menos não da forma que a maioria dos devs espera.

Quando um dev adiciona um novo endpoint WebSocket, muitas vezes ele copia a estrutura de endpoints HTTP existentes e esquece que WebSocket precisa de validação explícita. O middleware pode dar uma falsa sensação de segurança.

Se você está desenvolvendo aplicações com WebSocket (FastAPI, Starlette, Django Channels, qualquer framework ASGI), adote estas práticas:

  • Toda rota WebSocket precisa de autenticação explícita no endpoint — não confie apenas no middleware
  • Code review deve tratar novas rotas WebSocket como novos serviços — porque é isso que elas são
  • Testes de segurança automatizados devem incluir tentativas de conexão WebSocket sem credenciais
  • Princípio do menor privilégio — um endpoint de terminal não deveria existir em produção

O que vem depois

A CVE-2026-39987 já está patcheada, mas o estrago do período de exposição ainda está sendo avaliado. A Sysdig e a Endor Labs continuam monitorando atividade relacionada, e não seria surpreendente se nas próximas semanas surgirem relatos de incidentes originados dessa vulnerabilidade.

O Marimo, como projeto, respondeu bem — a correção foi publicada rapidamente e a comunicação foi transparente. Mas a velocidade com que o exploit apareceu em produção deveria servir de alerta para qualquer mantenedor de projeto open source: o tempo entre disclosure e exploit agora se mede em horas, não em dias.

E pra quem gerencia infraestrutura de ML e data science, fica o recado: aquele notebook “temporário” rodando em modo de edição no servidor da equipe pode ser a porta de entrada que um atacante precisa. Trate suas ferramentas de desenvolvimento com o mesmo rigor que você trata seus serviços em produção.

O ecossistema de data science se acostumou a rodar coisas com --host 0.0.0.0 porque “é só pra testes”. Jupyter, Marimo, Streamlit, Gradio — qualquer ferramenta que expõe uma interface web é um potencial vetor de ataque quando mal configurada. A diferença é que o Marimo tinha um terminal WebSocket sem auth. A próxima ferramenta pode ter algo pior.

Porque, como vimos, bastam 9 horas e 41 minutos pra transformar um advisory público em credenciais roubadas.


Fonte de inspiração: Marimo RCE Flaw CVE-2026-39987 Exploited Within 10 Hours of Disclosure — The Hacker News

Comment (1)

  • abril 12, 2026

    Modelos De IA De US$0,11 Encontram Os Mesmos Bugs Que O Claude Mythos - CodeInsider

    […] atacantes também vão encontrar. O tempo entre disclosure e exploração está encolhendo — como a gente viu com o Marimo, onde hackers exploraram uma CVE em menos de 10 […]

Your email address will not be published. Required fields are marked *

Related Posts