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

Três agentes, uma só coisa a roubar

Na Black Hat USA 2026, a Novee quebrou a configuração padrão do Claude Code, do Gemini CLI e do Codex de três maneiras diferentes, cada uma partindo de uma issue do GitHub que um estranho anônimo podia abrir. Os desvios não tinham relação entre si — um descompasso de aspas, uma allowlist de shell não aplicada, um AGENTS.md confiável — mas as três cadeias terminavam no mesmo passo: ler uma credencial real de dentro do processo do agente. O Bromure Agentic Coding não blinda esse último passo. Ele esvazia o processo: a exfiltração roda e entrega um chamariz.

Três fornecedores, três bugs de harness sem relação entre si, um mesmo gesto final: ler a chave de API ou o token presente no processo do agente e despachá-lo para fora. Em um perfil Bromure esse valor é um placeholder brm_…. A exfiltração é bem-sucedida, e o atacante recebe uma string que não significa nada fora do seu Mac.

Na Black Hat USA 2026, nesta semana, a Novee apresentou “Trusted Enough to Run: Breaking AI Agents in Official Workflows”. O pesquisador Elad Meged testou a configuração padrão de três agentes de código contra os repositórios públicos de cada fornecedor, e quebrou os três. As análises saíram em 6 de agosto.

O ponto de entrada foi o mesmo em todos os casos, e é o mais barato que existe: uma issue ou pull request no GitHub aberta por um estranho anônimo sem privilégio algum. O agente lê o texto não confiável como parte do seu trabalho, e o harness — o código em volta do modelo que decide o que ele pode executar — falha em manter esse texto do lado certo da linha. A Novee encontrou os mesmos padrões vulneráveis em mais de cem repositórios públicos além dos três fornecedores testados.

Os três caminhos de ataque não têm nada a ver uns com os outros. Eles terminam no mesmo lugar, e esse final compartilhado é a parte que vale levar para casa.

Três portas diferentes, uma mesma parede de fundo

Claude Code

A cadeia registrada como CVE-2026-54316 usou duas costuras. A primeira era um descompasso entre como o Claude Code validava um comando e como o shell de fato o interpretava: uma string entre aspas que parecia inofensiva ao validador era interpretada de outra forma quando chegava ao bash, e uma flag git push --receive-pack atravessou vinte e três verificações de segurança que estavam todas, individualmente, fazendo o seu trabalho. A segunda costura era a exfiltração. Um tac somente leitura — o comando que imprime um arquivo de trás para frente — bastava para ler um segredo e empurrá-lo para fora, invertido, pelos logs do GitHub Actions, e uma variante vazava a chave de API um caractere por vez cutucando o contador público de downloads de um modelo do HuggingFace e lendo-o de volta. A Anthropic publicou correções.

Gemini CLI

Avaliado em CVSS 10.0. No modo headless, o Gemini CLI confiava automaticamente na pasta de trabalho e carregava qualquer configuração de agente que encontrasse ali — sem sandbox, sem aprovação — de modo que um pull request que plantasse um arquivo de config rodava antes de a sandbox sequer inicializar. Duas premissas do harness cederam: a allowlist “restrita” de shell nunca era aplicada em tempo de execução, e a sanitização de ambiente que limpava os segredos do processo filho os deixava no processo pai, legíveis via /proc. O Google entregou uma mudança incompatível no modelo de confiança headless nas versões 0.39.1 e 0.40.0-preview.3.

Codex

O Codex roda duas passadas de agente sobre um espaço de trabalho compartilhado. .git e .codex estavam protegidos contra escrita; AGENTS.md não, e era recarregado como instruções confiáveis a cada execução. A primeira passada, ao processar entrada não confiável, escrevia um AGENTS.md envenenado; a segunda, a “segura”, o carregava como autoridade e levava as instruções maliciosas adiante com permissões elevadas. A OpenAI separou as passadas em jobs isolados em três dias.

Portas diferentes, mesma parede de fundoClaude Code · CVE-2026-54316validador e shell interpretam astring de formas diferentesgit push --receive-pack passa 23 checagenstac lê; logs do Actions levam para foracontador HuggingFace vaza char a charGemini CLI · CVSS 10.0modo headless confia na pastaconfig roda antes de a sandbox subirallowlist restrita nunca aplicadafilho limpo, mas segredos do paiainda legíveis via /procCodex · AGENTS.mdduas passadas, um só workspace.git e .codex protegidos; AGENTS.md nãoa passada 1 envenena o arquivoa passada 2 segura o lê como autoridadee o leva adiante, elevadaO último passo compartilhadoler uma credencial real do processoou do ambiente, e enviá-la para fora
Três desvios de harness sem relação, divulgados juntos. As portas diferem — um descompasso de aspas, uma allowlist não aplicada mais um vazamento via /proc, um arquivo de instruções confiável que sobrevive entre passadas. A parede de fundo é idêntica: cada cadeia termina lendo uma credencial real do processo ou do ambiente do agente e enviando-a para algum lugar.

A parte que é igual

O resumo da própria Novee é que “nenhuma delas era uma simples misconfiguração: eram decisões de segurança corretas que se desfaziam nos pontos de passagem”. Essa é a leitura certa sobre o harness. Mas olhe onde cada cadeia termina.

O tac do Claude lê uma credencial e o contador do HuggingFace vaza a chave de API. A brecha de /proc do Gemini expõe os segredos do processo pai. A segunda passada elevada do Codex carrega os tokens do ambiente para dentro de uma instrução controlada pelo atacante. Três costuras sem relação se abrem de três jeitos diferentes, e cada uma termina estendendo a mão para o mesmo objeto: um segredo real que estava ali, no processo ou no ambiente do agente, esperando ser lido.

Essa é a premissa que todos esses exploits compartilham — a de que, se você consegue rodar código como o agente, a credencial está logo ali. E quase sempre está. A ANTHROPIC_API_KEY, o GITHUB_TOKEN, as chaves da AWS: o jeito comum de rodar um agente de código coloca todas elas no ambiente do processo, porque é assim que as ferramentas esperam encontrá-las.

O Bromure Agentic Coding não tenta vencer a corrida armamentista do harness. Ele remove aquilo que o harness está protegendo.

Onde a cadeia se rompe

O Bromure roda o agente de cada perfil dentro de uma VM Linux descartável em Apple Silicon, e roteia cada byte do seu tráfego de rede por um proxy no host, fora da caixa em que o agente roda. Você pode rodar o mesmo agente autônomo de triagem de issues que a Novee atacou — hospedando-o você mesmo em vez de entregá-lo a um runner de workflow. Percorra as três cadeias contra esse arranjo e cada uma se desfaz, e o passo da credencial se desfaz do mesmo jeito para as três.

A credencial nunca está no processo

É esta a garantia que neutraliza o último passo compartilhado. As chaves reais da Anthropic, da OpenAI e da xAI são injetadas na VM como placeholders brm_… e trocadas pelos valores reais apenas no proxy do host, no fio, para o único host de destino a que cada uma pertence. A chave nunca está na memória da VM, nunca em /proc, nunca em uma variável de ambiente que um processo filho ou pai possa ler, nunca em um log. O tac lê um chamariz. O contador do HuggingFace vaza um chamariz um caractere por vez. A raspagem de /proc devolve um chamariz. Toda exfiltração da palestra roda até o fim e despacha uma string que só se resolve em exatamente um Mac, que não é a máquina em que ela rodou.

O RCE cai em uma caixa que você pode apagar

“Execução de código no host que roda o agente” é a frase do advisory do Gemini, e o host é a fronteira de confiança que o Bromure move. A execução pré-sandbox do Gemini e o bypass de shell do Claude ganham ambos uma VM Linux descartável, a um hipervisor do macOS, em NAT, de onde nada da sua rede é alcançável. Erase home descarta o que a execução escreveu e devolve o diretório home ao estado pós-clone. O exploit que domina o agente domina uma VM que você já ia jogar fora.

A escrita na main é recusada no proxy

As duas escaladas miram o mesmo prêmio: a cadeia do Claude empurra com git push --receive-pack, a do Gemini empurra código malicioso para a branch principal. Esse é o lado supply chain — um checkout comprometido vira um pacote envenenado rio abaixo. Configure o guardrail do GitHub como somente leitura ou bloqueio de ações destrutivas e o git-receive-pack é recusado no proxy do host, onde uma guarda burlada dentro da VM não tem voto. Os fetches continuam passando; o push que envenenaria a branch, não.

O arquivo de instruções é avaliado antes de ser obedecido

A entrada é texto não confiável de issue e de PR que o harness não conseguiu conter, e a cadeia inteira do Codex vive em um AGENTS.md escrito. O Bromure pontua o conteúdo dos resultados de ferramenta que o agente lê — páginas web, corpos de issues, conteúdos de arquivos — com um modelo local de injeção de prompt, e pontua os próprios arquivos de autoridade (CLAUDE.md, AGENTS.md e suas variantes) com um classificador dedicado mais um scanner de payloads de Unicode invisível. O AGENTS.md envenenado entre as duas passadas do Codex é exatamente o que esse segundo detector foi feito para pegar, e ele roda no host antes de o modelo agir sobre o arquivo.

Duas dessas configurações vêm ligadas por padrão, e o broker de credenciais não é uma configuração — é a forma como um perfil Bromure guarda um segredo, ponto. Os canais encobertos fecham o quadro: o log do GitHub Actions, o contador do HuggingFace, o host de saída inesperado, todos cruzam o proxy do host, e o Security Log e o Trace Inspector registram o host, o status e o relatório de troca de cada requisição — o canal lateral que vazou um chamariz é também uma linha em uma lista que você pode ler.

Uma estação de trabalho normalo processo guardachave de API, token e creds reaisem env vars, /proc e no discoo exploit lêtac, /proc ou uma passada elevadaa exfiltração entrega uma chave reale pode escrevergit push alcança a branchum pacote envenenado segue rio abaixoDentro de um perfil Bromureo processo guardaplaceholders brm_, trocados no proxyo valor real nunca entra na VMo exploit lêmesmo tac, mesmo /proc, mesma passadaa exfiltração entrega um chamarize a escrita é recusadagit-receive-pack barrado no proxya VM em que rodou é descartável
O último passo compartilhado, em dois lugares. Em uma estação de trabalho normal, o exploit lê uma credencial viva direto do processo e a exfiltração entrega uma chave real. Em um perfil Bromure, a mesma leitura é bem-sucedida contra um placeholder, a escrita que envenenaria a branch é recusada no proxy, e a caixa em que o agente rodou é descartável.

O harness vai continuar quebrando

As três correções que a Novee provocou são todas boas, e todas estreitas. A Anthropic corrigiu o descompasso de aspas; o Google mudou o modelo de confiança headless; a OpenAI separou as passadas. Cada uma fecha a costura específica que foi encontrada. Nenhuma muda o que os três exploits tinham em comum: uma credencial funcional parada no processo do agente, à disposição. O próximo bug de harness — e a Novee achou os mesmos padrões em uma centena de outros repositórios, então haverá um próximo — vai estender a mão para ela de novo.

Você pode responder a cada divulgação com um patch e uma revisão de configuração, e deve. Mas o patch fecha uma porta em um agente, e a revisão é por repositório e por release. A credencial que um exploit funcional vai buscar é a mesma nos três agentes e em todo bug futuro, e é a única coisa que você pode tirar do alcance com uma única decisão: mantenha o segredo real no host, entregue um placeholder ao agente, e deixe o proxy produzir o valor real apenas para o host a que ele é endereçado.

Instale o Bromure Agentic Coding, e dê ao agente uma caixa — e uma credencial — que você aceita perder.