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

o git status executou o código deles

Em 2 de setembro de 2026, a Manifold Security divulgou o GitSpawn: oito falhas em sete agentes de codificação de linha de comando, em que o próprio .git/config de um repositório executa um comando na máquina do desenvolvedor. O agente roda git status para saber em que branch está, o git atualiza seu índice, e o comando escolhido pelo repositório é executado com os privilégios do usuário logado, fora do sandbox, antes do aviso de confiança e, em uma das ferramentas, antes mesmo do login. Nenhum prompt, nenhuma chamada de ferramenta, nenhum turno do modelo. O Bromure Agentic Coding não corrige esse caminho. Ele muda a máquina para onde o caminho leva.

Você não digitou nada e não aprovou nada. O agente ainda não tinha terminado de iniciar e, em uma dessas ferramentas, você nem sequer tinha feito login. Uma pasta que alguém lhe enviou executou um comando em seu nome, e foi o git que a executou.

Um prestador termina um trabalho e lhe envia um zip do diretório do projeto em uma pasta compartilhada. Você descompacta, faz cd para dentro dele e inicia seu agente de codificação para se situar. O agente começa perguntando ao git que branch é esta e o que está modificado. Esse é todo o exploit.

Em 2 de setembro de 2026, o The Hacker News noticiou uma divulgação da Manifold Security cobrindo oito falhas em sete agentes de codificação de linha de comando, publicadas sob o nome GitSpawn. Somadas, as ferramentas afetadas acumulam perto de meio milhão de estrelas no GitHub, e só o Claude Code já passou de 77 milhões de downloads npm por mês. Quatro das oito ainda executavam comandos fornecidos pelo repositório quando a Manifold as reavaliou em 1º de setembro.

Uma configuração de desempenho cujo valor é um comando

core.fsmonitor é uma opção do git pensada para quem tem repositórios enormes. Em vez de percorrer a árvore inteira para descobrir o que mudou, o git pode chamar um programa auxiliar externo e perguntar a ele. O valor da configuração é o comando. O git o lê do .git/config do próprio repositório e o executa sempre que atualiza seu índice, ou seja, no git status, no git diff, no encanamento em que toda ferramenta se apoia.

Quase todo agente examinado pela Manifold reúne o contexto do projeto rodando esses comandos na inicialização, e nenhum deles olhava para o .git/config antes. Então o repositório nomeia o comando e o git o executa. Nas palavras dos pesquisadores, ele é executado “com todos os privilégios do usuário logado, fora do sandbox”. A OpenAI, em seu próprio comunicado sobre a variante do Codex, disse que o auxiliar “roda fora do sandbox de comandos do Codex e sem um aviso de aprovação do usuário”.

Um git clone não carrega nada disso. O clone transfere objetos e refs, e a configuração local do repositório de origem fica em casa. Portanto o projeto envenenado precisa chegar até você como arquivos, com o diretório .git intacto: um arquivo zip, um disco compartilhado, uma pasta sincronizada, um pendrive. A lista da Manifold descreve exatamente como consultores, clientes e colegas passam trabalho adiante, e é o único caminho de entrega em que não há pull request nenhuma. A proteção do próprio git contra repositórios hostis, safe.directory, verifica se o repositório pertence a outra pessoa. Foi você quem descompactou, então ele é seu, e a proteção não tem nada a dizer.

O momento da execução deixa a proteção de cada agente um passo atrasada demais. No Claude Code e no Hermes Agent, a carga dispara antes do aviso de confiança do workspace, a caixa de diálogo que existe justamente para perguntar se você confia neste diretório. No Qwen Code, dispara antes de o usuário ter se autenticado, e no Grok Build dispara na primeira tecla.

GitSpawn — da entrega ao shella pasta chegazip, disco compartilhado,pasta sincronizada, USB —.git/config intactoo agente iniciareúne o contexto:git status · git diffo git atualiza seu índicelê o config do repocore.fsmonitor = …<o comando deles>seu usuário, seu env,seu diretório homeO que ainda não tinha acontecidoo aviso de confiançaClaude Code,Hermes Agentfazer logino Qwen Code disparaantes da autenticaçãodigitar qualquer coisao Grok Build dispara naprimeira teclao modelosem chamada, sem turno,nada para registrarCada barreira da fileira acima é uma etapa da inicialização do agente. O comando rodou antes de a sequência chegar nelas.
A cadeia inteira e as quatro coisas que ainda não tinham acontecido quando ela terminou. O .git/config de um repositório nomeia um comando em core.fsmonitor; o agente roda git status para descobrir sua branch; o git atualiza o índice e chama o auxiliar. Ele é executado como o usuário logado, antes do aviso de confiança no Claude Code e no Hermes Agent, antes da autenticação no Qwen Code e na primeira tecla no Grok Build, sem nenhuma chamada de ferramenta e sem nenhum turno do modelo em ponto algum da sequência.

Oito achados, sete agentes, quatro ainda abertos

Metade está corrigida. O goose 1.44.0 traz a correção para a CVE-2026-72718, classificada com CVSS 7.0. A OpenAI lançou o Codex CLI 0.131.0 e os builds de desktop 26.519.x para a CVE-2026-19592. A Cursor corrigiu sua variante. A Manifold confirmou o caminho core.fsmonitor do Claude Code na 2.1.193 e a Anthropic o fechou na 2.1.196, sem publicar um comunicado: a Manifold observa que nenhum dos dois achados sobre o Claude Code aparece nos registros de avisos do npm.

A outra metade não andou. O Hermes Agent tem uma CVE (CVE-2026-71963) e um aviso privado ainda sem triagem após seis tentativas de contato. Os mantenedores do Qwen Code aceitaram o relatório em 7 de julho e a 0.22.3 continua executando o comando. O Grok Build continua executando. E o Claude Code tem um segundo caminho, através de seu comando ultrareview, que abusa de uma chave de configuração do git diferente e ainda estava vivo na 2.1.252. A Manifold reteve o nome dessa chave em vez de distribuir um modelo de ataque reutilizável.

A Anthropic corrigiu o primeiro caminho em três dias. A classe de falha contornou a correção por outra chave, no mesmo arquivo, no mesmo produto.

Essa é a superfície de execução do git, herdada por toda ferramenta que delega ao git, e o git tem mais de uma configuração cujo valor é um comando que ele executará por você. Pagers, editores, comandos SSH, caminhos de hook, drivers de diff e de filtro, e aliases que começam com um escape de shell aceitam todos valores de comando. Agora cada fornecedor precisa descobrir, chave por chave, quais delas um repositório consegue alcançar a partir de seu caminho de inicialização, diante de um esquema de configuração que ganha chaves com o tempo. A correção recomendada pela Manifold, sanitizar a configuração do git durante as operações de contexto em segundo plano e desativar core.fsmonitor, é uma lista de bloqueio mantida contra um espaço de nomes. Os pesquisadores encontraram o padrão em mais agentes do que o relatório nomeia.

Os controles que teriam pegado isso ficam depois do ponto de execução

Enfileire o que uma equipe cuidadosa tem hoje sobre um agente de codificação: avisos de permissão no uso de ferramentas, confiança de workspace, hooks que inspecionam um comando antes de ele rodar, os endpoints de transcrição que enviam uma sessão para um backend de conformidade, e OpenTelemetry para decisões de permissão e bypasses.

Todos eles se ancoram no laço do próprio agente, ou seja, em uma chamada de ferramenta que o agente decidiu fazer ou em um turno que o modelo tomou. O GitSpawn não produz nenhum dos dois. O processo que executa a carga é o git, invocado pelo agente para sua própria arrumação, e o comando vem de um arquivo que o agente nunca leu. Não há chamada de ferramenta para aprovar nem hook para disparar. Uma equipe que observa transcrições de agentes chegando ao seu SIEM vê uma sessão que inicia normalmente e não mostra nada, o mesmo ponto cego sobre o qual escrevemos quando a Anthropic lançou sua Compliance API: um registro ancorado no que chegou ao modelo não consegue ver o que rodou antes de o modelo ser consultado.

O comando vai rodar. A pergunta que vale responder é o que ele vai encontrar.

Descompactada no seu diretório homegit status → auxiliar core.fsmonitorescolhido pela pasta, rodando como vocêo que está a um diretório de distância~/.ssh/id_ed25519ANTHROPIC_API_KEY, AWS_SECRET_ACCESS_KEY~/.git-credentials, ~/.aws, ~/.kube/config~/.zshrc — persistência no próximo loginsaída: uma conexão normal de umaferramenta de dev normal, em porta normalo log do próprio agente não tem linha algumaA pasta não era confiável. A conta em que ela caiu era.Aberta em um workspace Bromuregit status → auxiliar core.fsmonitorainda roda — como ubuntu, no convidadoVM Linux descartável · iniciada antes da pastanenhum byte de chave privada — o ssh-agent do host assinacredenciais de env e config são chamarizes (brm_…):80 e :443 desviados para o proxy do host, sem opt-outfirewall de saída: negar por padrão → sem segundo estágioum chamariz fora do escopo → 451, VM pausada,workspace sinalizado, exige limpeza para iniciartoda requisição ganha uma linha, seja qual for o processoO comando ainda roda. Só que num lugar feito para isso.
A mesma pasta, o mesmo git status, duas máquinas diferentes. No seu laptop, o auxiliar roda como você, a um diretório de distância das suas chaves SSH e das suas credenciais de nuvem, e sua conexão de saída é uma conexão comum de uma ferramenta de desenvolvimento comum. Em um workspace Bromure, ele roda como ubuntu dentro de uma VM que iniciou antes de a pasta existir: nenhum byte de chave privada para roubar, chamarizes onde deveriam estar as credenciais, e cada byte de saída em um fio que o host possui e registra.

Deixe rodar e veja o que ele encontra

O Bromure Agentic Coding dá a cada espaço de trabalho sua própria VM Ubuntu sob o hipervisor da Apple. O agente, seu git, seu gerenciador de pacotes e tudo o que esses três iniciarem vivem no convidado. Solte o zip do prestador em um workspace, deixe o auxiliar disparar e conceda tudo ao atacante: antes da confiança, antes da autenticação, root no convidado se quiser.

Ler as chaves SSH. Não há nenhuma para ler. As chaves privadas de um workspace ficam no seu Mac; a VM recebe um SSH_AUTH_SOCK ligado por vsock a um agente do lado do host. Esse protocolo tem uma requisição que significa “assine este desafio” e nenhuma requisição que signifique “me entregue a chave”.

Varrer o ambiente atrás de credenciais. Estão todas lá, no formato que os validadores esperam, e todas são chamarizes. ANTHROPIC_API_KEY guarda um falso sk-ant-api03-brm-…, e o mesmo vale para ~/.git-credentials, ~/.aws/config, ~/.kube/config e ~/.docker/config.json. Seus valores reais vivem criptografados no seu Mac, e o proxy os substitui no fio depois que a requisição já saiu da VM, apenas quando ela é destinada ao host para o qual aquela credencial foi cunhada. Nenhuma chave desliga isso. É assim que as credenciais funcionam no aplicativo.

Baixar o segundo estágio. O auxiliar é um carregador, algumas centenas de bytes cuja função é buscar a carga real. Essa busca esbarra no firewall de saída do workspace. Configure o Tráfego não correspondido como Deny e a VM alcança os hosts que você listou e nada mais, aplicado em dois lugares: o switch virtual casa por IP de destino e por nome de host farejado no DNS, em todos os protocolos, e o proxy casa de novo por nome de servidor TLS e, para regras web, por método HTTP. Um carregador que em vez disso busca um registro de pacotes encontra o pipeline de cadeia de suprimentos, onde a barreira de idade recusa versões mais novas que o corte. Uma carga publicada ontem não passa nesse critério.

Exfiltrar o que ele encontrou. Agora algo precisa atravessar um fio que o seu Mac possui. O proxy varre cada requisição de saída em busca dos chamarizes cunhados para o workspace, cabeçalhos e corpo, com um autômato de Aho-Corasick. Um chamariz endereçado a um host fora do escopo para o qual foi cunhado é a assinatura de uma máquina mandando para fora algo que não deveria saber. O proxy bloqueia com HTTP 451 antes de um byte chegar ao destino, pausa a VM e levanta um alerta nomeando a credencial e os dois hosts. O Bromure então marca o workspace como comprometido, e ele não voltará a iniciar enquanto você não apagar seu disco e seu home.

Deixar persistência. Um .bashrc modificado no convidado dura o tempo que você deixar a VM durar. Apagar home reinicia /home/ubuntu, Resetar disco reclona o disco de sistema a partir da imagem base assinada, e bromure-cli vm run --rm dá a você um workspace que se apaga sozinho quando a VM para.

A interceptação não tem variável de ambiente para desativar

Um proxy no qual o convidado opta por entrar é um proxy que o convidado pode recusar. O switch virtual do Bromure desvia ele mesmo os fluxos de porta 80 e porta 443 da VM para o proxy do host: não há HTTPS_PROXY para desativar, nada no convidado que o desligue, e o HTTP puro é inspecionado do mesmo jeito que o HTTPS. O processo que faz a requisição aqui nunca leu a configuração do agente e não é o agente. É um binário auxiliar que o git lançou, e ele anda pelo mesmo fio que todo o resto.

A linha existe mesmo que nenhuma chamada de ferramenta exista

O proxy do host grava um registro de metadados para cada requisição que sai da VM, seja qual for o processo que a fez: hora, host, porta, método, caminho, status, latência e bytes medidos antes da troca de credencial. Veredictos do firewall, decisões de cadeia de suprimentos e a intermediação de credenciais caem na Linha do Tempo de Segurança (Janela → Linha do Tempo de Segurança…), e em um Mac inscrito vão para o fluxo de eventos da organização como egress.firewall, supply_chain.fetch e credential.exfiltration. Uma transcrição do lado do agente para este ataque fica em branco enquanto a do fio carrega a requisição.

O convidado alcança uma única coisa no seu Mac. Uma pasta compartilhada é uma janela viva, de leitura e escrita, para o seu sistema de arquivos real, e a limpeza por comprometimento a deixa intacta por design. Compartilhe o diretório do projeto em vez do seu diretório home, e o find do auxiliar por *.pem percorre um home Linux cheio de checkouts e caches de pacotes em vez dos seus logins guardados no chaveiro.

A fronteira que não é uma etapa da sequência

Leia juntos os quatro detalhes de tempo do GitSpawn — antes do aviso de confiança, antes da autenticação, antes da primeira tecla, antes de qualquer turno do modelo — e eles dizem uma coisa só. Cada proteção é uma etapa de uma sequência de inicialização, e cada nova funcionalidade acrescenta uma etapa diante dela. A Cursor aprendeu isso em 2025 com um mcp.json fornecido pelo repositório, e de novo em julho com um comando de configuração de worktree que rodava antes do seu próprio aviso de confiança. Agora é a vez do git, em sete produtos ao mesmo tempo, através de uma chave de configuração que está no git há anos.

Um hipervisor não é uma etapa dessa sequência. Ele não lê o .git/config. Nenhuma refatoração o reordena e nenhum caminho de código se isenta dele, porque ele não roda no mesmo programa que o prompt. A VM já estava rodando antes de o zip terminar de baixar, e suas duas garantias — nenhum segredo real aqui dentro e nenhuma rota não mediada para fora — valem igualmente na 2.1.193 e na 2.1.196.

Confira seu .git/config antes de abrir uma pasta que alguém lhe mandou. A Manifold tem razão em dizer isso. Você também vai fazer isso por duas semanas e depois pular no terceiro zip de uma tarde corrida, que é a validade da maioria das instruções que terminam em “antes de abrir”. Instale o Bromure Agentic Coding, dê à pasta desconhecida sua própria máquina, e deixe a config executar o que quiser em um cômodo onde executar é tudo o que ela consegue fazer.