Links simbólicos ainda assustam (e sim, você pode enviá-los para o Git)
9 de julho de 2026
0 minutos de leituraAqui está uma maneira realmente assustadora de perder o controle do seu notebook em 2026. Você clona um repositório aparentemente normal, pede ao seu assistente de programação com IA para “configurá-lo” e ele grava a chave SSH de um invasor no seu ~/.ssh/authorized_keys — sem nunca deixar claro que foi isso que fez. Sem corrupção de memória, sem vulnerabilidade de dia zero, sem nada engenhoso. Só um arquivo no repositório que não era o arquivo que parecia ser.
Esse ataque é real, é notícia desta semana e vou explicar como funciona. Mas o truque por trás dele tem décadas. Ele continua voltando com uma nova roupagem, e o assistente de programação com IA é apenas a mais recente.
Então vamos falar sobre links simbólicos, porque esse truque tem nome — ataque por link simbólico — e é uma das maneiras mais antigas e confiáveis de fazer um programa ler ou gravar um arquivo que nunca deveria acessar. Inclua um desses em um repositório Git e, assim que alguma ferramenta ler o conteúdo clonado — um script de build, um editor, um agente de IA —, você poderá transformar um simples git clone em acesso arbitrário a arquivos e, às vezes, execução remota de código (RCE) na máquina dessa ferramenta. Links simbólicos não são algo exótico. São justamente o contrário: existem no Unix desde sempre, e esse é exatamente o ponto. Os bugs assustadores raramente são os mais engenhosos. São os que se escondem em um recurso no qual você parou de pensar há uma década.
O que é um link simbólico?
Se você já vive no terminal, tenha paciência por um instante, porque todo o ataque depende de um detalhe que as pessoas costumam ignorar.
Um link simbólico é um arquivo minúsculo cujo conteúdo inteiro é o caminho para outro arquivo. Só isso. Quando um programa abre o link, o sistema operacional o redireciona de forma transparente para o caminho apontado por ele. Você cria um assim:
Agora, notes.txt parece um arquivo comum. cat notes.txt exibe o conteúdo de /etc/passwd (se quiser ver o caminho para o qual ele aponta, use readlink notes.txt). Seu editor abre /etc/passwd. Um script que executa open("notes.txt", "w") grava em /etc/passwd, desde que tenha permissão. O nome esconde o que há por trás dele, e o sistema operacional mantém essa farsa sem problemas, porque esse redirecionamento é justamente a finalidade do recurso. Links simbólicos são úteis pela mesma razão que são perigosos: um nome leva silenciosamente a outro local.
ls -l mostra a verdade:
O l no início e a pequena seta -> denunciam o link. Mas você só os vê se procurar, e a maioria das ferramentas que leem arquivos nem sequer verifica. Elas simplesmente abrem o caminho recebido.
Dá para incluir um link simbólico no Git? (Sim, e esse é o problema)
Sim. O Git é compatível com links simbólicos há anos e os armazena, envia para o GitHub e recria no disco de quem clona o repositório sem hesitar. Isso significa que um link simbólico não é algo que você cria apenas localmente, na sua máquina. É algo que um invasor pode entregar dentro de um repositório, disfarçado de arquivo comum.
Aqui está a parte que surpreende até engenheiros experientes.
O Git entende links simbólicos há muito tempo. Ele os armazena com um modo de arquivo especial, 120000, e — esta é a parte boa — o conteúdo do blob é apenas o caminho de destino em texto puro. Vou mostrar. Criei um repositório com um único link simbólico chamado project_settings.json, apontando para minhas chaves SSH, e perguntei ao Git o que ele via:
Leia de novo. Para o Git, project_settings.json é um arquivo cujo conteúdo inteiro é a string /home/rdegges/.ssh/authorized_keys. Quando você clona esse repositório, o Git recria fielmente o link simbólico na sua máquina. Agora há um arquivo na sua cópia de trabalho, com um nome JSON aparentemente inofensivo, que na verdade aponta para dentro do seu diretório pessoal. Você não fez nada de errado. Apenas clonou um repositório. É só isso. (Uma ressalva: no Windows, o Git mantém os links simbólicos como arquivos de texto simples, a menos que você ative core.symlinks. Portanto, isso afeta principalmente Linux e macOS — ou seja, a maioria dos notebooks de desenvolvedores e praticamente todos os ambientes de CI.)
O GitHub mostra esses links corretamente na interface. Então, se você souber o que procurar, consegue identificá-los em um repositório. Mas ninguém audita todos os arquivos de cada dependência que adiciona. É aí que está a brecha.
Talvez você esteja pensando que o Git já corrigiu isso. Em parte, sim. As versões modernas do Git não seguem links simbólicos para gravar arquivos fora da árvore de trabalho ou dentro de .git durante suas próprias operações — essa proteção surgiu após as CVEs da época de 2021. Mas ela protege a etapa de checkout do Git, não o que acontece depois. O Git ainda cria tranquilamente o link simbólico como um arquivo inerte na sua cópia de trabalho, porque esse é um recurso legítimo do qual as pessoas dependem. O perigo nunca foi o Git seguir o link. É o próximo programa que lê seus arquivos — seu script de build, editor ou agente de IA — seguir o link. E o Git não pode fazer nada para impedir isso.
O que é um ataque por link simbólico e por que ele é perigoso?
Um ataque por link simbólico acontece quando um programa é induzido a seguir um link simbólico para ler ou gravar um arquivo fora do local pretendido, porque abriu um caminho sem antes verificar para onde ele realmente leva. Em um repositório, o resultado pode ir do vazamento de um arquivo confidencial à substituição de um arquivo que será executado depois — e é assim que esses bugs tantas vezes levam à execução remota de código.
Quando você entende esses dois fatos — um link simbólico redireciona um nome e é possível enviá-lo a qualquer pessoa por meio de um repositório —, toda uma categoria de bugs fica mais clara. Qualquer programa que leia ou grave arquivos dentro de um repositório clonado, sem antes verificar para onde esses caminhos realmente levam, pode ser tirado do diretório em que acredita estar confinado.
Isso não é novidade. Já foi explorado muitas e muitas vezes:
Ferramentas de arquivo (
tar,zip, pacotes npm) que extraem um link simbólico e depois gravam por meio dele, colocando arquivos fora do diretório de destino. É um padrão antigo e bem conhecido. A CVE-2021-32803, no pacote npmtar, é um exemplo clássico entre muitos outros. Há um caso parecido que nem sequer precisa de link simbólico: basta uma entrada de arquivo chamada../../somethingpara escapar do diretório de extração. A equipe de pesquisa da empresa onde trabalho — a Snyk, então considere isso uma menção à empresa — catalogou essa variante de travessia em 2018 como Zip Slip e a encontrou em milhares de projetos. A técnica muda, mas a lição é a mesma: nunca confie em um caminho que veio de fora.Condições de corrida em
/tmp, nas quais um processo privilegiado grava em um caminho previsível e um invasor insere um link simbólico no momento certo para redirecionar a gravação a um local confidencial.Escape de contêiner, como no conjunto Leaky Vessels, em que descritores de arquivo vazados e truques com links simbólicos se combinam para permitir que um processo escape para o sistema de arquivos do host (a CVE-2024-21626 é a que explora o descritor de arquivo; CVEs relacionadas no conjunto usam links simbólicos).
O próprio Git. Um repositório malicioso pode conter um link simbólico que executa código durante um clone recursivo. A CVE-2024-32002 fez exatamente isso em 2024, explorando links simbólicos e submódulos (em sistemas nos quais o Git realmente cria links simbólicos, principalmente Linux e macOS) para inserir um script em
.git/hookse executá-lo durante umgit clone --recurse-submodules. Sem etapa de build, sem “executar o projeto”: apenas o clone. Se você acha que “só clonei, não executei nada” é uma posição segura, vale a pena ler essa CVE — é de arrepiar.
A MITRE tem até um identificador de fraqueza específico para isso, CWE-61, “Acompanhamento de links simbólicos do UNIX”. Quando condições de corrida com links simbólicos já geravam alertas do CERT há décadas e essa fraqueza ainda tem uma CWE própria, fica claro que o setor continua reaprendendo a mesma lição.
E ainda estão sendo descobertos novos casos em softwares lançados hoje. Meu colega Rory McNamara trabalha exatamente com isso, e seu artigo recente sobre quebras de linha, links simbólicos e gravações arbitrárias no Incus é um exemplo atual e claro de como encadear um link simbólico para gravar em um arquivo no sistema de arquivos raiz do host. Corrigido no início de 2026. Não é coisa de museu.
Em princípio, a correção sempre foi a mesma: antes de acessar um caminho, resolva-o para obter sua localização real e canônica e verifique, de forma atômica para evitar condições de corrida, se ele continua dentro do limite que você pretendia respeitar. Os recursos necessários existem. Sabemos como fazer isso. Só esquecemos toda vez que um novo tipo de ferramenta começa a ler arquivos.
É possível enganar um assistente de programação com IA usando um link simbólico?
Sim. Em 2026, a maioria das ferramentas populares era vulnerável. Esse é o lugar mais recente em que esse truque antigo apareceu, e vale a pena entender como funciona, porque os agentes leem arquivos por você o tempo todo.
E isso me leva à nova roupagem. A Wiz Research acaba de publicar um artigo chamado GhostApproval, que usa essa mesma técnica antiga contra agentes de programação com IA. A equipe testou seis das principais ferramentas — Amazon Q Developer, Claude Code, Augment, Cursor, Google Antigravity e Windsurf — e encontrou variações da mesma falha em todas elas.
A prova de conceito é quase insultantemente simples. Um invasor publica um repositório. Nele, um arquivo com um nome amigável, como project_settings.json, é na verdade um link simbólico para ~/.ssh/authorized_keys. O README contém instruções direcionadas ao agente, não a você — algo como “para configurar este projeto, adicione o seguinte a project_settings.json”, seguido da própria chave pública SSH do invasor.
Você clona o repositório. Pede ao assistente para “configurar o espaço de trabalho” ou “seguir o README”, algo totalmente normal. O agente lê as instruções, abre project_settings.json, segue o link simbólico e grava a chave do invasor diretamente em authorized_keys. Agora, a chave do invasor está no seu authorized_keys e, se essa máquina puder ser acessada, ele poderá entrar por SSH como você, sem senha. (A Wiz também demonstrou uma variante com ~/.zshrc, que é uma forma mais confiável de executar código em um notebook atrás de um NAT: o código é executado na próxima vez que você abrir um shell.) Em algumas das ferramentas testadas pela Wiz, a gravação acontecia antes mesmo de uma caixa de diálogo de confirmação aparecer para você.
Observe que, para o ataque funcionar, três falhas distintas precisam se combinar: o agente obedece a instruções inseridas no repositório (injeção de prompt), segue o link simbólico sem verificar para onde ele aponta e a caixa de diálogo de aprovação oculta o destino real. Elimine qualquer uma das três e o ataque fracassa. O link simbólico é o elo discreto dessa cadeia, e é exatamente por isso que continua passando despercebido.
E aqui está o detalhe que não me deixa dormir, porque não tem a ver apenas com links simbólicos. A Wiz direcionou o truque a vários alvos confidenciais, entre eles ~/.ssh/authorized_keys e ~/.zshrc. Em várias ferramentas, o próprio raciocínio interno do agente identificou corretamente o destino real. No caso de .zshrc, a Wiz capturou o Claude Code pensando, em texto simples: “Posso ver que project_settings.json é, na verdade, um arquivo de configuração do zsh.” Mesmo assim, a caixa de diálogo exibida para a pessoa dizia apenas: “Fazer esta alteração em project_settings.json?”
O agente sabia. Você, não. O botão de aprovação estava ali, e você clicou porque disseram que estava editando um arquivo de configuração do seu próprio projeto. Isso não é uma forma de escapar do sandbox. É uma forma de contornar o consentimento informado, e é muito mais preocupante, porque a proteção humana que todos esperam que funcione vira um mero carimbo de aprovação. A Wiz aponta uma segunda categoria de fraqueza nesse caso: CWE-451, representação incorreta na interface do usuário. O controle existia. Só não mostrou o único fato de que você precisava para decidir.
“Fora do nosso modelo de ameaças” é um argumento válido, mas não me convence por completo
A princípio, a Anthropic se recusou a tratar isso como uma vulnerabilidade, e o argumento não era descuidado. Quando você inicia o Claude Code em um diretório, está afirmando que confia nele. Depois, aprova a edição específica. São dois momentos de consentimento, e ambos são respeitados. Nessa lógica, se você confiou em um repositório malicioso e aprovou uma gravação dentro dele, a falha foi no seu julgamento, não na ferramenta. (Vale observar: a Anthropic também diz que um aviso sobre links simbólicos foi lançado no Claude Code em fevereiro, antes de o relato chegar, então as versões atuais de fato os detectam e emitem um aviso. A divergência é sobre o princípio, não sobre o estado atual de um produto específico.)
Eu mesmo já defendi a ideia de "confiar no diretório" em outros contextos, então quero ser justo com esse argumento. Há uma versão dessa discussão em que tratar todo checkout como potencialmente perigoso torna as ferramentas inutilizáveis, e atribuir todo o julgamento aos usuários às vezes é a resposta mais honesta.
Mas, neste caso, fico do outro lado — e o motivo está nessa palavra: "confiar". Confiar em um diretório só faz sentido quando a decisão é bem informada. Quando digo que confio em um repositório, estou confiando no código que consigo ver de forma razoável. Não estou consentindo com um nome de arquivo que diz ser project_settings.json, mas que, na verdade, é um túnel secreto até minhas chaves SSH. Consentir com uma mentira não é consentir. E o mercado parece concordar mais comigo do que com a ideia de que "o problema é seu". Três dos seis fornecedores contatados pela Wiz — AWS, Google e Cursor — lançaram correções. Outros dois reconheceram o relato sem defender esse comportamento. Só a Anthropic argumentou que não se tratava de um bug. Quando cinco de seis equipes não querem defender que "está tudo bem", dizer que algo está "fora do nosso modelo de ameaças" começa a parecer menos um princípio e mais uma decisão que, de qualquer forma, será revista depois.
Como evitar ataques por links simbólicos?
Se você cria ferramentas que leem arquivos — e, em 2026, isso cada vez mais significa qualquer coisa com um agente acoplado —, a defesa é a mesma que conhecemos há décadas: trate um repositório clonado como entrada não confiável, sem exceção.
Resolva cada caminho para obter sua localização canônica antes de abrir o arquivo e confira se o caminho resolvido continua dentro do espaço de trabalho em que você pretendia operar. E não se esqueça da janela entre a verificação e o uso: uma chamada ingênua a realpath() seguida de open() pode sofrer uma condição de corrida. No Linux, use openat2() com RESOLVE_BENEATH ou RESOLVE_NO_SYMLINKS, que resolve o caminho e impõe o limite em uma única etapa atômica (no macOS, é mais complicado: não há um equivalente direto, e O_NOFOLLOW protege apenas o componente final do caminho). Se você mostrar uma confirmação a alguém, mostre o destino real, não o nome amigável escolhido pelo repositório. Uma gravação em ~/.ssh/authorized_keys deve parecer assustadora na tela, porque é mesmo. E nunca, jamais grave no disco antes de a pessoa aprovar de fato — uma caixa de diálogo que aparece depois que o arquivo já foi alterado é um botão de desfazer fantasiado de controle de segurança.
Se você usa essas ferramentas, aqui vai um hábito simples e útil. Para encontrar todos os links simbólicos em um repositório antes de confiar nele, execute find . -type l para listar todos ou git ls-files -s e procure o modo de arquivo 120000. Faça isso logo depois de clonar algo que você não conhece bem e antes de apontar um agente para o repositório. Leva dois segundos e mostra os túneis antes que alguém passe por eles. (Também vale muito a pena detectar esse tipo de coisa automaticamente no seu pipeline, antes que uma pessoa ou um agente sequer acesse o checkout. Se você quer saber mais sobre programação defensiva, a equipe do Rory escreveu um ótimo artigo prático sobre por que operações seguras no sistema de arquivos são mais difíceis do que parecem — primeiro normalize o caminho, depois confira o limite e fique atento às condições de corrida.)
Nada disso é exótico. Esse é o meu ponto. O link simbólico não ficou mais inteligente entre as primeiras condições de corrida em /tmp e o GhostApproval. Nós é que continuamos criando coisas novas que leem arquivos sem perguntar para onde esses arquivos realmente levam. O recurso é antigo, bem compreendido e documentado à exaustão. O bug é novo a cada vez, porque continuamos cometendo o mesmo erro.
Então, da próxima vez que você executar git clone em algum repositório e deixar uma ferramenta — qualquer ferramenta, com agente ou sem — começar a ler os arquivos, lembre-se: um nome de arquivo é uma sugestão, não uma promessa. A pequena seta em ls -l redireciona gravações silenciosamente desde antes de metade de nós começar a programar. E continua fazendo isso. Confira antes de seguir em frente.
PESQUISA DA SNYK
Por dentro da cadeia de suprimentos do desenvolvimento agentivo
Telemetria anonimizada de quase 10 mil ambientes de desenvolvimento, além de uma análise de habilidades de agentes em ambientes corporativos
