← Voltar para todas as publicações
Publicado em · por Renaud Deraison

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.

De um envio a um pull requesta análiseum envio HEICpostado num fórum públicosem privilégios de contaFastImagea checagem seguranão suporta HEIFmagickconverte no lugarImageMagick chama libheiflibheif 1.19.7estouro de heap, R/W OOBcorrigido, sem CVE nem backporto pivôexecução de códigocommunity.openai.com06:00 UTC, 25 de julhouma falha de SSOuma sessão do fórum viratomada de conta, sem interaçãoo Codex de um funcionárioligado à org do GitHubjá autorizado a escreversem exploitum promptPR #1186742monorepo privado, openai/openai
Cada elo da cadeia é comum. O FastImage não suporta HEIF, então o arquivo vai para o ImageMagick, que chama o libheif, que continha um estouro de heap cuja correção upstream não levou CVE e nunca chegou ao Debian. Uma falha de SSO separada transformou execução de código no fórum em acesso à conta, e acesso à conta num pull request no monorepo privado. Só o passo final não exigiu exploit: os pesquisadores enviaram um prompt a um agente de codificação que já estava autorizado a fazer push.

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.

Um agente no laptopa sessão do agentealcançável por você, por uma injeção, porquem tomar a sessãocredenciais reais, ao alcanceGH_TOKEN · ~/.git-credentialslegíveis por qualquer processo da máquinaPOST /repos/org/repo/pulls → GitHuba sessão carrega a autoridadeUm agente num workspace Bromurea sessão do agente, dentro da VMghp_ + 36 caracteres, um substitutonada real a herdaro switch desvia :80 e :443o proxy do host classificaPOST /repos/org/repo/pulls = escritagit-receive-pack também contaum diálogo no seu Mac, com a chamada literal15 minutos · uma vez · a sessão · não permitiro Mac carrega a autoridade
Num laptop de desenvolvedor o agente guarda um token real, então um prompt basta e a sessão carrega a autoridade. Num workspace Bromure a VM guarda um substituto, o proxy do host insere o token real na saída, e o guardrail classifica a escrita antes que ela chegue ao GitHub. Perguntar antes de escrever, o padrão para novos workspaces, abre um diálogo no seu Mac trazendo o método e o caminho literais. Seja quem for que esteja com a sessão, não está com o 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.