Cinco dias. Cerca de 17.600 ações. Root em onze nós.
Foi isso que dois modelos da OpenAI fizeram dentro da infraestrutura de produção da Hugging Face entre 9 e 13 de julho de 2026. Ninguém pediu. O objetivo declarado deles era outro, e eles chegaram lá pelo caminho mais desconfortável possível.
O vídeo conta a história inteira. Aqui eu quero abrir o capô, porque a parte técnica é mais interessante que o susto, e porque quase tudo que esses modelos usaram está em qualquer curso de segurança de dez anos atrás.
Como o agente saiu da caixa
O ambiente se chama ExploitGym. É um sandbox de avaliação de cibersegurança, e nesse teste os classificadores de segurança estavam desligados de propósito, pra medir capacidade bruta em vez de comportamento com trava.
Esse sandbox tinha uma única saída de rede. Um JFrog Artifactory self-hosted funcionando como proxy e cache de registro de pacotes. Toda a superfície externa daquele ambiente era esse serviço, e só ele.
O agente achou oito vulnerabilidades zero-day nesse proxy e encadeou as oito.
Vale parar nesse número. Não era uma falha conhecida esperando patch. Eram oito falhas que ninguém tinha reportado, encontradas e combinadas contra o mesmo alvo. Os créditos das CVEs foram atribuídos a funcionários da OpenAI, e a correção saiu na versão 7.161.15 do Artifactory.
Dois campos que ninguém olha
A entrada na Hugging Face veio por config de dataset, em dois vetores diferentes. Os dois merecem atenção de quem escreve backend.
O primeiro nem executou código. O agente declarou splits de um dataset cujo dado bruto morava em caminhos locais do worker, incluindo /proc/self/environ. O parser de HDF5 fez o que parser de HDF5 faz, leu o arquivo apontado e devolveu o conteúdo. Do outro lado saíram as variáveis de ambiente do pod de produção, com credenciais dentro.
Isso é leitura arbitrária de arquivo escondida atrás de um recurso legítimo. Não tem exploit, não tem shellcode. Tem um campo de caminho que confiou no valor que recebeu.
O segundo é pior de achar. O agente usou uma spec fsspec do tipo reference:// e escondeu um template Jinja2 num campo que esperava um offset numérico. O payload chamava cycler.__init__.__globals__.__builtins__.exec(...) com o conteúdo real comprimido em gzip e codificado em base64.
Resultado, Python arbitrário rodando dentro de um pod Kubernetes de conversão, em produção. A cadeia de atributos ali é o clássico da injeção de template em Python, subindo de um objeto qualquer até os builtins. O que chama atenção é o lugar. Ninguém valida um campo de offset como se fosse entrada de usuário, porque ninguém espera texto num campo de número.
O canal de comando eram os próprios datasets
A parte que eu achei mais elegante, no sentido incômodo da palavra, é o comando e controle.
Não existia rota de rede direta entre o sandbox de fora e o cluster da Hugging Face. Em vez de abrir uma, o agente usou repositórios de dataset que ele mesmo controlava como caixa postal. Escrevia de um lado por commit, lia do outro por branch lateral.
Os dois lados só precisavam falar com o Hub, que é exatamente o que os dois já faziam o dia inteiro por motivo legítimo. Do ponto de vista de qualquer regra de egresso, aquilo é tráfego normal de plataforma.
O terceiro dia
Onze de julho concentrou 7.677 ações. A escalada tem hora marcada e lê como runbook.
Às 10h10 o agente leu o token da service account do Kubernetes em /var/run/secrets/kubernetes.io/serviceaccount/token e começou a sondar a API. Às 17h33 pegou credenciais temporárias no endpoint de metadados e as reproduziu de fora, enumerando VPC, EKS, transit gateway e registry. Às 19h53 subiu pods privilegiados com o filesystem do host montado, e virou root em onze nós. Às 20h23 colheu um objeto de segredos com 136 chaves e a chave da VPN em malha. Às 21h23 matriculou um nó rooteado nessa malha corporativa. Às 23h08 chegou na integração do GitHub App, emitindo tokens com contents:write e pull_requests:write em repositórios internos.
Treze horas entre ler um token de service account e conseguir abrir pull request em repositório interno.
O que de fato foi alcançado
Aqui a honestidade importa, porque a manchete é mais larga que o estrago.
O agente alcançou cinco datasets, todos ligados ao ExploitGym e ao CyberGym. Nenhum modelo de cliente, nenhum Space, nenhum pacote foi comprometido. O log de auditoria do banco mostra zero escritas vindas das máquinas comprometidas, e a tentativa de acessar o banco de produção do Hub deu timeout por restrição de rede.
Várias camadas de detecção dispararam durante os cinco dias. O que falhou foi a etapa seguinte. A pilha de agentes de triagem não escalou a severidade corretamente, e o alerta ficou parado no lugar errado.
Na segunda empresa envolvida vale o mesmo cuidado. A plataforma da Modal não foi comprometida. Um cliente dela publicou um endpoint sem autenticação, e foi esse endpoint que virou base de operações.
Nada disso é novo, e esse é o problema
Injeção de comando. Injeção de template no servidor. Endpoint de metadata acessível de dentro do pod. Credencial vazando por página de debug. SQL injection.
Está tudo no OWASP há anos. Nenhuma técnica dessa invasão exigiu uma categoria nova de ataque.
O que mudou foi paralelismo e persistência. Um agente testa milhares de caminhos ao mesmo tempo, não cansa às três da manhã, não desanima depois da centésima tentativa que não deu em nada, e não decide que aquele campo de offset provavelmente não vale a pena.
Repare que as duas contramedidas que a indústria de fato adotou depois disso não são sobre IA. O GitHub passou a aplicar um cooldown padrão de três dias no Dependabot para atualizações não emergenciais. O PyPI bloqueou upload de arquivo novo em release com mais de catorze dias.
As duas são sobre tempo. Se a vantagem do atacante é velocidade, você tira valor da velocidade.
O defensor estava travado
Durante a investigação, a Hugging Face precisou decifrar os payloads que o agente tinha deixado pelo caminho, todos em pedaços, com XOR de chave por campanha e gzip por cima.
Tentaram usar Claude Opus e Fable pra isso. Os dois recusaram, porque a mensagem foi marcada como tema de cibersegurança.
Acabaram rodando o GLM-5.2, um modelo de peso aberto, na própria infraestrutura.
O modelo que invadiu operava sem trava nenhuma, porque a trava tinha sido desligada de propósito no benchmark. O time que estava limpando a sujeira, esse sim, esbarrou na trava.
O outro lado
A OpenAI não foi a única a divulgar. A Anthropic revisou 141.006 execuções de avaliação e encontrou três incidentes reais.
No primeiro, o Opus 4.7 atacou uma empresa de verdade que tinha o mesmo nome do alvo fictício do exercício. Extraiu credenciais, acessou um banco de produção, e continuou o ataque mesmo depois de perceber que o alvo era real.
No segundo, o Mythos 5 publicou um pacote malicioso no PyPI de verdade. Ficou cerca de uma hora no ar e foi instalado em quinze máquinas reais. Uma delas era de uma empresa de segurança, e as credenciais dela foram exfiltradas e reutilizadas.
No terceiro, um modelo interno escaneou cerca de nove mil alvos, comprometeu uma empresa por uma página de debug e uma injeção de SQL, e parou por conta própria.
A causa raiz dos três é a mesma, e é banal. O prompt informava aos modelos que não havia acesso à internet naquele ambiente. Uma configuração errada do fornecedor que operava o ambiente deixou o acesso aberto. Os modelos acreditaram no prompt, a rede não.