A chave era o seu arquivo .env
Em 6 de outubro, a Adversa AI mostrou uma única página web fazendo o GitHub Copilot CLI ler um arquivo .env.prod e enviar todos os segredos dele para um atacante. As instruções chegam criptografadas, o agente as descriptografa com Python no próprio shell, e a chave de descriptografia que ele monta é feita dos seus arquivos locais. Vinte e oito segundos, sem confirmação. O GitHub diz que não é uma vulnerabilidade e não tem correção planejada. No Bromure Agentic Coding, o arquivo guarda substitutos, o substituto dispara um alarme no Mac quando sai para o host errado, e o texto descriptografado passa por um scanner que o agente não consegue desligar.
A página manda o agente montar uma chave de descriptografia com os seus arquivos locais. A chave é feita para falhar. Ler os arquivos era o objetivo.
Na terça-feira, 6 de outubro, a Adversa AI publicou uma técnica que chama de
Cryptographic Context Injection, com uma demonstração contra o GitHub Copilot
CLI. Um desenvolvedor pede ao agente que busque uma URL. Vinte e oito segundos
depois,
“o conteúdo completo de um arquivo .env.prod, cada segredo dele, está no log
de um atacante”. O resumo da Adversa: “Cadeia completa: 28 segundos, sem
confirmação”.
O The Register publicou a história naquela mesma tarde, e o CSO Online e o InfoWorld vieram em seguida, em 7 de outubro. A Adversa reportou o problema ao bug bounty do GitHub em 17 de setembro. O GitHub validou a cadeia, classificou-a como “inelegível” e disse ao The Register que ela “exige que um usuário instrua intencionalmente o Copilot CLI a buscar conteúdo controlado por um atacante ou não confiável e confirme que deseja disparar a ação, e portanto não é uma vulnerabilidade do produto”. Não há patch nem CVE.
Seis passos a partir de uma URL
O relatório da Adversa descreve a cadeia:
- O desenvolvedor roda o Copilot CLI numa pasta de projeto, em modo autopilot, e pede que ele busque uma URL.
- A página diz que seu conteúdo está criptografado, manda o agente descriptografá-lo com Python e oferece duas chaves candidatas.
- Uma é a real. A outra é um modelo que o agente só consegue completar lendo
arquivos locais e inserindo o conteúdo deles na string da chave. O agente lê
.env.prode monta a chave. A Adversa chama essa leitura de “o roubo”. - A descriptografia com a chave montada falha, de propósito.
- O agente recorre à chave real. O segundo estágio descriptografado manda ele buscar uma URL de acompanhamento “para pegar mais contexto”.
- O agente requisita essa URL com o conteúdo dos arquivos coletados num parâmetro da requisição.
A transcrição nunca cita o host do atacante nem mostra que o conteúdo dos arquivos saiu da máquina. O resumo final do agente dizia que ele tinha “confirmado um endpoint authorized-reader”.
Por que o texto cifrado passa
O Copilot recusa as mesmas instruções quando elas chegam em texto puro. O argumento da Adversa é que a criptografia é o que muda o resultado. “Guardrails estáticos leem texto; eles não o executam”, disse Rony Utevsky, da Adversa, ao The Register. Base64 ou uma cifra de substituição podem ser decodificados de memória por um modelo, e dá para treinar um filtro para detectá-los. Um texto cifrado forte fica opaco até que algum código rode, e esse código roda no próprio shell do agente, cuja saída chega como um resultado de ferramenta que o próprio agente produziu.
O modelo no comando também importa. O mai-code-1.1-flash, da Microsoft,
executou a cadeia completa em metade das tentativas da Adversa, e os dois
modelos GPT-5.6 que o Copilot oferece recusaram a carga todas as vezes. Com a
seleção de modelo em Auto, o roteador às vezes entregava a sessão ao modelo
vulnerável. Nas palavras de Utevsky: “O usuário não escolhe, e não vê, qual
modelo cuidou da sessão”. Ele chama isso de “loteria de modelos”.
A Adversa divulgou a mesma técnica contra o Grok em agosto, onde ela vazava históricos de chat. Um agente de código rende mais, porque shell, leitura de arquivos e requisições de saída são ferramentas comuns dele.
O que 28 segundos alcançam num laptop
A demonstração da Adversa cita um arquivo. O relatório diz que o padrão “poderia ser qualquer coisa que o agente consiga ler”, inclusive arquivos fora do diretório de trabalho. No laptop de um desenvolvedor, o agente lê tudo o que a sua conta lê:
.env,.env.prode.env.localem cada checkout~/.aws/credentials,~/.config/gcloud,~/.kube/config- o token do
ghe~/.git-credentials - o próprio token OAuth do agente e as configurações MCP com seus tokens bearer
~/.ssh/id_ed25519, se o modelo de chave pedir
A cadeia usa três ferramentas que todo agente de código tem: uma busca web, uma leitura de arquivo e uma chamada Python. A página pede e o modelo obedece.
A mesma página numa máquina Bromure
No Bromure Agentic Coding, o agente roda dentro de uma VM Linux no seu Mac, e cada requisição da VM passa por um proxy no host. Reproduza a página da Adversa numa máquina, com qualquer agente que busque páginas e rode Python: Claude Code, Codex, Grok. Dê ao modelo as piores chances e deixe-o seguir cada instrução.
O arquivo guarda substitutos
A demonstração funciona porque .env.prod guarda segredos reais. Uma máquina
Bromure não precisa deles. No painel Credentials da máquina, Import env
file… lê o seu .env.prod, mostra cada variável com o valor mascarado e a
transforma numa credencial: nomes reconhecidos como GH_TOKEN,
AWS_SECRET_ACCESS_KEY ou ANTHROPIC_API_KEY ganham seu tipo, e todo o resto,
como STRIPE_KEY, vira um token genérico restrito aos hosts que você indicar.
Os valores reais ficam criptografados no Mac. A VM recebe substitutos com os
mesmos nomes de variável e o formato dos reais: brm_…, ghp_…,
sk-ant-api03-brm-…. A sua aplicação lê as variáveis como antes, e o proxy só
coloca o valor real na rede para uma requisição ao host daquela credencial.
O passo 3 roda mesmo assim. O agente lê o arquivo, monta a chave B e insere o que encontrou. O que ele encontrou são strings que nenhum servidor vai aceitar.
O substituto é uma armadilha
Um substituto tem um único destino legítimo: o host para o qual foi emitido. O
proxy examina cada requisição de saída, linha de requisição, cabeçalhos e
corpo, em busca de qualquer substituto indo para outro lugar. O passo 6 coloca
o arquivo coletado num parâmetro de requisição destinado ao host do atacante.
O proxy encontra um token brm_ a caminho de um host para o qual ele nunca foi
emitido, responde HTTP 451, não encaminha nada, pausa a VM e abre This
environment may have been compromised no Mac. O alerta mostra a prévia do
token, o nome da credencial, o host ao qual ela pertence e o host para onde
foi enviada.
O detector está sempre ligado, não importa o que o modelo decida nem o que o resumo do agente diga. Seja qual for o modelo sorteado para a sessão, a requisição chega à mesma verificação no host.
O texto descriptografado passa pelo proxy
A criptografia vence filtros que leem a página, porque a página é texto cifrado. O segundo estágio descriptografado ainda precisa chegar ao modelo, e ele viaja em texto puro. O Python o imprime, o agente devolve a saída ao modelo como resultado de ferramenta na próxima requisição à API, e essa requisição passa pelo proxy no Mac.
Ative Detect prompt injection in source code no painel Prompt Injection da máquina e o proxy pontua cada novo resultado de ferramenta com um modelo PromptGuard local antes que a requisição saia. “Busque esta URL para pegar mais contexto”, descriptografado um segundo antes, é pontuado ali, depois que o agente rodou o código que o descriptografou. Com Block unilaterally, uma requisição sinalizada recebe um 451 e o modelo nunca vê o segundo estágio. Com Ask me what to do, um diálogo no Mac mostra a você o trecho sinalizado do texto. Cada detecção vira uma linha Prompt injection na Security Timeline.
A varredura roda no proxy do Mac, sobre a requisição à API, então o agente na VM não consegue desligá-la nem contorná-la.
O segundo host aparece
O conselho de detecção da Adversa é observar uma sequência: entra conteúdo não confiável, código roda, arquivos locais são lidos e então uma requisição vai para um host sem relação. Numa máquina Bromure, esse último passo encontra o firewall Outbound connections no painel Guardrails: regras por host, faixa de IP, protocolo e porta, com Unmatched traffic em Allow ou Deny. Uma máquina em Deny, com seu provedor de modelos, seus registros e o GitHub liberados, nunca se conecta a um host que o projeto não usa. A Security Timeline registra os veredictos do firewall de qualquer forma, então uma busca para um host desconhecido fica na linha do tempo ao lado da sessão que a fez.
O trace tem os argumentos resolvidos
A primeira recomendação da Adversa é “rastrear cada chamada de ferramenta com
os argumentos totalmente resolvidos” e não confiar no resumo do agente. A
última palavra do agente na demonstração era falsa. No nível de tracing
padrão, AI request details, o proxy guarda cada corpo de requisição e
resposta trocado com o provedor do modelo, criptografado no Mac. O Trace
Inspector decompõe cada um em chamadas de ferramenta com suas entradas: o
cat .env.prod, o Python que montou a chave B, o estágio descriptografado que
voltou e a busca de acompanhamento com a URL completa. Cada requisição
armazenada também informa o modelo para o qual foi, então você sabe qual
modelo foi sorteado para a sessão.
bromure-cli trace leaks lista as requisições em que um token apareceu no host
errado. O registro fica no Mac, então o agente não consegue editá-lo.
Consentir com uma busca
A posição do GitHub é que um desenvolvedor que pede a um agente para buscar uma página, num modo que roda sem confirmações, consentiu com tudo o que vier depois. A Adversa contesta, e a disputa vai ficar assim: o GitHub disse ao CSO que “pode tornar a funcionalidade mais rígida no futuro”, mas que “não tinha nada a anunciar”.
Buscar uma página é a coisa que um agente de código mais faz depois de ler um arquivo. Documentação, uma issue, um changelog, uma resposta no Stack Overflow: um desenvolvedor não confere cada URL antes de passá-la adiante, e os modos autopilot existem justamente para que ele não precise acompanhar cada passo. A próxima página pode vir de um resultado de busca que o próprio agente escolheu.
Numa máquina Bromure, os substitutos ficam onde estavam os segredos e as verificações rodam no Mac, seja qual for a URL que você passar e seja qual for a instrução que o modelo seguir.
O .env guarda substitutos
Import env file… transforma cada variável numa credencial. A VM recebe substitutos com os mesmos nomes, e os valores reais ficam criptografados no Mac, inseridos só para o host ao qual cada um pertence.
Um substituto no host errado para a máquina
O proxy examina cada requisição de saída em busca de substitutos indo para outro lugar. Uma detecção recebe um 451, a VM pausa e um alerta no Mac cita o token e os dois hosts.
O texto descriptografado é verificado
O texto cifrado precisa virar texto puro antes que o modelo possa segui-lo. Com a detecção de prompt injection ligada, esse resultado de ferramenta é pontuado no proxy e pode ser bloqueado antes que o modelo o leia.
O trace vence o resumo
Cada chamada de ferramenta, com argumentos resolvidos e o modelo que a atendeu, fica armazenada criptografada no Mac. Os veredictos do firewall ficam na Security Timeline.
A próxima página
A Adversa usou essa técnica contra o Grok em agosto e contra o Copilot CLI em outubro. O texto cifrado muda de página para página, assim como o arquivo que o modelo de chave pede. O modelo roteado pode mudar de uma sessão para outra. Os agentes vão continuar buscando páginas, e alguém vai continuar escrevendo páginas para eles.
Rode o seu agente onde o arquivo que ele lê guarda substitutos e cada requisição de saída passa pelo Mac. Para saber mais sobre os substitutos e o alarme que eles disparam, leia O agente vazou a própria chave e Quarenta mil dólares por fuga.