O último passo foi um prompt
Em 18 de setembro a Hacktron publicou a cadeia que usou para chegar ao monorepo privado da OpenAI. Ela começa com uma imagem enviada a um fórum público e um estouro de heap num decodificador de imagens cuja correção upstream não levou CVE algum. Termina com os pesquisadores enviando um prompt à conta Codex de um funcionário, e o agente abrindo o pull request. Nada foi injetado e nada funcionou mal; o agente fez o que pediu quem estava com a sessão. O Bromure Agentic Coding decide essa escrita no seu Mac, fora da sessão.
O último passo desta cadeia não exigiu exploit nenhum. Os pesquisadores estavam com o agente de codificação de um funcionário, então pediram que ele abrisse um pull request no monorepo privado, e ele abriu um.
Às seis da manhã UTC de 25 de julho, três pesquisadores da Hacktron tinham
execução de código no fórum público da comunidade da OpenAI. No começo da tarde
um pull request numerado 1186742 estava em openai/openai, o monorepo privado, e
eles não haviam escrito uma linha de código para colocá-lo ali. Pediram a um
agente, e o agente o abriu.
A Hacktron publicou a cadeia em 18 de setembro, e ela compensa uma leitura elo por elo, porque cada elo é comum. Os pesquisadores são Harsh Jaiswal, Mohan Pedhapati e Rahul Maini. The Hacker News noticiou o caso no dia seguinte.
Uma imagem, uma conversão e um parser
A porta de entrada foi o envio de imagens do fórum. O Discourse verifica as
imagens enviadas com uma biblioteca Ruby chamada FastImage, que lê o suficiente
do cabeçalho de um arquivo para deduzir suas dimensões e seu formato. O FastImage
não entende HEIF, o contêiner em que uma foto de iPhone chega, então o Discourse
entregava esses arquivos ao comando magick do ImageMagick para conversão. O
ImageMagick decodifica HEIC com libheif, que é C.
Essa é toda a condição de entrada. Um formato que o verificador seguro não reconhecia foi para um conversor que chamava um parser escrito numa linguagem em que um campo de comprimento malformado é um problema de segurança de memória. A Hacktron encontrou um estouro de buffer de heap na decodificação HEIC e dele obteve primitivas de leitura e escrita fora dos limites.
A papelada é pior que o bug. O upstream havia corrigido esse estouro no ano
anterior e nunca rotulou o commit como correção de segurança. Ele não recebeu
CVE, nem aviso, nem uma linha de changelog que um scanner de dependências
pudesse ler, então o Debian nunca fez o backport, e a imagem Docker do Discourse
distribuía libheif 1.19.7 no Debian 12 com o bug intacto. Nenhum scanner no
mundo tinha motivo para sinalizar aquela versão. O Debian publicou a DSA-6417-1
mais tarde e o problema hoje carrega a CVE-2026-32882, atribuída depois que
alguém o explorou.
Não dá para escanear um bug que ninguém registrou. O que dá para decidir de antemão é dentro de que o parser está quando cai.
O exploit levou uma noite
A Hacktron começou a olhar o pipeline de upload em 23 de julho. Em 24 de julho foram atrás do estouro com o Claude Opus 4.8 que, pelo relato deles, penou ao longo de várias sessões para produzir qualquer coisa que funcionasse contra a randomização do espaço de endereçamento, a defesa que embaralha o mapa de memória de um processo para que um atacante não consiga prever onde está cada coisa. Transformar um estouro de heap em execução confiável diante do ASLR é a parte difícil, e é onde esse tipo de pesquisa emperra por uma semana.
A Anthropic lançou o Opus 5 naquela noite. A Hacktron abriu uma sessão nova e,
três horas depois, tinha um exploit ARM64 funcionando contra um Mac local.
Pediram ao modelo que o portasse para a configuração x86-64 e jemalloc em que o
Discourse roda, e confirmaram execução local de código via upload de imagem às
seis da manhã seguinte. Depois colocaram o Claude num laço autônomo contra a
própria instância Discourse Cloud deles. Às dez ele também tinha execução de
código ali, e leu /etc/hosts para provar.
Eles são cuidadosos sobre o que isso significa, e a ressalva cabe em qualquer leitura do caso:
Isto não foi hacking totalmente autônomo. A perícia humana continuou necessária para dirigir sessões, validar resultados e conduzir a divulgação.
Justo. Três pesquisadores habilidosos apontaram a coisa, e a habilidade deles não mudou da noite para o dia. O bug também não mudou. Entre aquelas duas sessões a única variável nova era o modelo, e ele levou um trabalho de exploração de várias tentativas fracassadas para três horas. A campanha à qual isso pertencia custou à Hacktron menos de 3.000 dólares em tokens e algumas horas de tempo humano.
Esse número é o dado de planejamento. Um bug que nunca recebeu CVE, alojado numa dependência transitiva de um conversor de arquivos, agora se converte numa primitiva confiável pelo preço de um laptop. Esperar pelo aviso é uma esperança, e ninguém ganha desse relógio lendo changelogs mais rápido.
O último passo foi um prompt
Execução de código no fórum não deu à Hacktron nada com que a OpenAI se importasse. Um segundo problema fez isso. A OpenAI havia ligado seu login único de modo que uma sessão de fórum comprometida virava a tomada sem interação da conta ChatGPT e Codex de qualquer funcionário que usasse o fórum. Nas palavras da Hacktron, o problema de SSO é o que transformou um comprometimento de fórum em acesso a ChatGPT e Codex.
O Codex de um funcionário estava conectado à organização da empresa no GitHub,
então os pesquisadores fizeram o óbvio. Enviaram àquela conta um prompt pedindo
que abrisse um pull request no monorepo interno, e o PR 1186742 apareceu em
openai/openai. Depois pararam, sem ler código-fonte, e enviaram o relatório.
Leia esse passo de novo. Alguém com uma sessão válida pediu ao agente que fizesse uma coisa comum, e o agente a fez com o acesso de escrita que já tinha. Sem injeção, sem jailbreak, sem entrada malformada. Todo controle que alguém havia construído ficava atrás daquela sessão: o login, a associação à organização, as permissões do repositório. Uma vez que um atacante tinha a sessão, tinha tudo aquilo.
Um agente de codificação é uma concessão permanente de acesso de escrita aos seus
repositórios, presa a uma conversa. Quem alcança a conversa herda a concessão.
Isso vale para o Codex num navegador, e vale para o Claude Code, o Codex CLI ou o
Gemini CLI num laptop, onde o agente carrega um token ghp_ real em
~/.git-credentials e um GH_TOKEN real no ambiente. Num laptop as vias de
entrada se multiplicam: um arquivo envenenado num repositório, um comentário de
issue, um script de postinstall de pacote, um terminal sequestrado.
A OpenAI corrigiu o fórum em cerca de catorze horas. O relatório entrou naquela manhã e a OpenAI confirmou a correção às 22:49 UTC no mesmo dia. O Discourse lançou sua própria correção ao longo do fim de semana e publicou o aviso GHSA-vhm9-85gw-x335 em 28 de julho, e a recompensa somou 6.500 dólares. A parte desse formato que fica na sua própria mesa é o agente.
Onde a escrita é decidida
O Bromure Agentic Coding mantém a resposta a “quem tem permissão para fazer push” no seu Mac, fora da sessão do agente, porque a sessão é justamente o que um atacante tenta alcançar. Duas coisas acontecem ali.
Primeiro, o ambiente do agente não guarda token algum a herdar. A VM do workspace
recebe um substituto: ghp_ mais 36 caracteres, com formato tal que gh e git
o aceitam sem reclamar, derivado do seu token real com HKDF-SHA256 e um sal por
instalação, de modo que ele nunca parece rotacionar entre sessões. Seu token real
vive cifrado no host, e o proxy do host o substitui no fio depois que a requisição
deixou a VM, apenas quando o destino corresponde ao host para o qual aquele token
foi cunhado. Como diz o manual: nenhum arquivo, variável de ambiente ou processo
dentro da VM jamais contém uma chave de API real, token OAuth, segredo AWS ou
chave SSH privada. Leve esse substituto para onde ele não pertence e o proxy
responde HTTP 451 sem encaminhar um byte, pausa a VM e escreve uma linha vermelha
na Security Timeline.
A segunda é a que esta história pede. Toda credencial de forja git carrega uma
política de escrita, definida por workspace no painel Guardrails, e novos
workspaces começam em Perguntar antes de escrever. As leituras passam sem
comentário. Um git push chega ao fio como git-receive-pack e conta como
escrita, e um POST à API de pull requests também. Quando um deles chega, o proxy
segura a requisição e abre um diálogo no seu Mac trazendo a operação literal em
vez de um resumo dela: o método e o caminho. Você recebe Permitir por 15
minutos, Permitir uma vez, Permitir pelo resto da sessão ou Não
permitir. Uma recusa chega ao agente como um erro de API comum que ele pode
relatar, não como uma conexão travada.
A descrição que o próprio painel dá de onde isso roda é a frase que importa: aplicado no proxy, de modo que um agente comprometido dentro da VM não consegue contorná-lo.
Seja quem for que esteja dirigindo o agente — você, uma injeção de prompt enterrada num arquivo de fixture, ou alguém com uma sessão tomada de você —, a escrita sai pelo mesmo proxy e é classificada do mesmo jeito. O diálogo no seu Mac é a autorização, e um atacante com a sua sessão não tem o seu Mac.
O problema do parser também chega à sua mesa
A outra metade da cadeia também se generaliza, e pouca gente a incluiu na conta.
Um agente de codificação passa o dia lendo arquivos que não escreveu: fixtures de
teste que vieram com o repositório, uma imagem anexada a um relatório de bug, uma
captura de tela que você colou na conversa, o PDF de uma especificação, um
tarball de uma página de release, uma fonte, um vídeo que o front end deve tocar.
Lidar com isso significa chamar magick, ffmpeg, pdftotext ou unzip, ou
uma biblioteca de imagens ligada ao Python, e cada um deles é um parser C ou C++
comendo bytes escolhidos por outra pessoa. Essa é a condição que deixou um upload
de fórum chegar ao libheif. Um agente a encontra com mais frequência do que
você, porque nunca hesita antes de abrir alguma coisa.
A detecção não tem resposta aqui. O bug do libheif não levava CVE no momento em que importava, então nenhum scanner, lista de permissões ou fixação de versão teria dito uma palavra. Você pode escolher o raio da explosão, e é só isso.
Num workspace Bromure esse decodificador roda dentro de um convidado Ubuntu sob o
Virtualization.framework da Apple, uma fronteira de hipervisor e não um sandbox
de processo, num convidado que o Bromure trata como não confiável por construção.
Um estouro de heap num parser de imagens compra ao atacante um shell numa VM
Linux cujas credenciais são substitutos e cuja rota para a internet é um conjunto
ordenado de regras no host. O switch virtual aplica esse conjunto por IP de
destino e por nome de host farejado do DNS, em TCP e UDP, e o proxy o aplica de
novo por nome de servidor TLS e método HTTP. Coloque o tráfego não correspondido
em Negar e o shell fica com uma lista de permissões e nada mais. O switch
desvia :80 e :443 para o próprio proxy, então não há variável de proxy que o
shell possa desfazer. Edições de regra alcançam sessões em andamento sem reboot,
e cada veredito cai como uma linha de Firewall na Security Timeline.
Presuma que o parser vai cair
Este bug não tinha CVE no momento em que contava. Planeje para o decodificador perder, e decida de antemão dentro de que ele está quando isso acontecer.
Uma sessão não é uma autorização
Quem alcança a conversa herda o acesso de escrita do agente. Ponha a decisão de um push em algum lugar que a conversa não alcance.
Não deixe nada na VM para roubar
Um substituto que as ferramentas aceitam não vale nada upstream, e vale menos que nada para um atacante que o leve a outro lugar.
Ponha preço no exploit
Três horas e alguns milhares de dólares em tokens. Ler avisos mais rápido não responde a esse número.
O relato da Hacktron é boa pesquisa, e honesto sobre o quanto um humano ainda teve de guiar. O final é a parte que continuará verdadeira no ano que vem. Toda defesa da cadeia ficava atrás de uma identidade, e assim que uma sessão que carregava essa identidade mudou de mãos, nenhuma delas teve mais opinião alguma. Alguém pediu ao agente, e o agente obedeceu.
Se o seu agente pode fazer push, pergunte onde mora a permissão de fazer push, e se um atacante que tomasse tudo o que o agente tem tomaria isso também. Num laptop, tomaria. Mova essa única decisão para fora da máquina em que o agente roda, e o último passo desta cadeia ganha onde parar.