Backdoor no Cursor: confiança nos forks do VS Code
O backdoor da extensão Claude Code no Marketplace do Cursor mostra por que forks do VS Code não herdam a confiança da Microsoft. Veja o que checar agora.

Um desenvolvedor abre o Cursor numa terça-feira de manhã, vê uma atualização da extensão Claude Code e deixa instalar. Nada parece diferente. A extensão carrega, o assistente de IA funciona e, na hora do almoço, a máquina já tem a chave Ed25519 de um atacante em ~/.ssh/authorized_keys, um túnel SSH reverso conectado a um servidor na internet e um LaunchAgent disfarçado de processo do sistema da Apple mantendo tudo vivo depois de cada reinicialização. Esse é o backdoor do Marketplace do Cursor que a Phorion divulgou, e é a demonstração mais clara até hoje de algo que a galera de segurança vem resmungando há anos: forks do VS Code herdam a base de código da Microsoft, mas não herdam o modelo de confiança da Microsoft.
Passei uma boa parte da minha carreira fazendo resposta a incidentes em máquinas de desenvolvedores, e esse caso me pega justamente pela banalidade do vetor de entrega. Sem phishing. Sem zero-day. Só um pacote de extensão adulterado, versões 2.1.312 e 2.1.313, publicado num marketplace em que desenvolvedores confiam por padrão porque ele parece aquilo em que já confiam. Os canais oficiais da Anthropic estavam limpos: alguém usou acesso ao pipeline de publicação do Cursor para enviar uma cópia modificada de uma extensão que a Anthropic nunca compilou para aquele registro. O raio de impacto foi todo usuário do Cursor que atualizou automaticamente numa janela de onze horas.
O Que o Backdoor Realmente Fazia
O implante era uma expressão de função invocada imediatamente, acrescentada no início do bundle extension.js minificado. Tudo depois do prefixo injetado parecia a extensão normal: os mesmos exports activate e deactivate, o mesmo código de interface, as mesmas strings. Essa é a parte que deveria preocupar você se sua estratégia de detecção for "comparar o bundle com o upstream": a parte maliciosa são algumas centenas de bytes de JavaScript extra em cima de um arquivo legítimo de megabytes. Ele roda no momento em que o Node carrega o módulo, antes do callback activate da própria extensão. O usuário não precisa clicar em nada, invocar o Claude nem interagir com a extensão de forma alguma.
O payload em si é um trabalho em quatro estágios, e, sinceramente, é trabalho competente:
- Plantio de chave. Acrescenta uma chave pública Ed25519 controlada pelo atacante, comentada como
svc-telemetry, em~/.ssh/authorized_keys, com as permissões corretas 0700/0600. - Ativação do SSH. Tenta garantir que o sshd esteja rodando: systemctl no Linux, Remote Login no macOS, OpenSSH Server via PowerShell no Windows.
- Túnel reverso. Abre uma conexão SSH de saída para um servidor controlado pelo operador com
-R *:0:localhost:22, de modo que o atacante consiga voltar pelo túnel até a porta 22 da vítima e entrar com a chave plantada. Não precisa de protocolo C2 próprio: o SSH é o C2. - Persistência e reconhecimento. Um LaunchAgent de usuário chamado
com.apple.sysmond.plistno macOS, uma entrada @reboot no cron do Linux e um fallback na pasta Inicializar no Windows. Depois, um beacon único com hostname, usuário, e-mail do git, IP público e até quarenta nomes de variáveis de ambiente que batem com/KEY|TOKEN|SECRET|PASS|AWS|GCP/i.
Esse último item é esperto de um jeito que vale entender. O beacon não exfiltra sua chave secreta da AWS, ele exfiltra o fato de que você tem uma. É um inventário de alvos. O operador ganha um menu de quais máquinas comprometidas pertencem a engenheiros de nuvem com credenciais ativas no ambiente, e é exatamente para quem ele volta primeiro pelo túnel.
O Bug do macOS Que Acabou Ajudando as Pessoas
Aqui está o detalhe que, como respondedor de incidentes, acho sombriamente engraçado: o caminho de ativação no macOS do malware quebra por causa de um erro clássico de script. O código executa systemsetup -getremotelogin como usuário sem privilégios e só tenta ligar o Remote Login se a saída bater com /Off/i. Mas no macOS esse comando se recusa a rodar sem acesso de administrador. Ele imprime "Você precisa de acesso de administrador para executar esta ferramenta... saindo!" no stdout e sai com status 0.
// what the malware effectively does
var rl = ex("systemsetup -getremotelogin 2>/dev/null", 4000);
if (/Off/i.test(rl)) {
det("sudo -n systemsetup -setremotelogin on >/dev/null 2>&1");
}
A string de erro não contém "Off", então o regex falha, então a etapa de ativação nunca roda. O script trata "não tenho permissão para verificar se o SSH está desligado" como "o SSH já está ligado". Código de saída 0 de um comando que falhou é um dos footguns mais antigos do shell scripting, e dessa vez mordeu o autor do malware em vez de um sysadmin. Em um Mac sem Remote Login já ativado, o túnel reverso conecta sem problemas, mas qualquer coisa encaminhada de volta esbarra numa porta 22 fechada. As chaves e a persistência ainda são instaladas, então a máquina continua comprometida, mas o caminho de acesso interativo morre antes de nascer.
Não se console com isso. Se o usuário já tinha o Remote Login ligado, ou se ligar mais tarde, a chave plantada está lá em authorized_keys esperando. O caminho do Windows tem um bug parecido (ele passa um comando PowerShell por um helper que fixa /bin/sh), e a lógica de cron do Linux pode apagar o crontab existente do usuário em execuções repetidas. Malware escrito por humanos tem bugs humanos. O do mês que vem não vai ter.
Por Que Marketplaces de Forks Não Herdam o Modelo de Confiança da Microsoft
Esse é o ponto estrutural, e é o que quero que líderes de engenharia reflitam. Quando você instala uma extensão do Visual Studio Marketplace oficial, existe um pipeline de verdade por trás: contas de publicador com verificação de domínio, varredura na publicação, processos de remoção e uma empresa com departamento jurídico do outro lado. Está longe de ser perfeito, extensões maliciosas já passaram por ele, mas é um sistema.
Forks do VS Code como o Cursor não podem usar esse marketplace por questões de licenciamento, então apontam para registros alternativos, como o Open VSX ou infraestrutura própria. A interface do editor é familiar até o último byte. A página da extensão mostra o mesmo nome, o mesmo ícone, uma string de publicador plausível. Mas cada controle que tornava o marketplace original confiável foi reimplementado ou simplesmente não existe. Quem verificou o publicador? Quem analisou o bundle? O artefato foi compilado do repositório de código que ele alega, e dá para conferir? Neste incidente, as versões adulteradas aparentemente nunca existiram no registro da Microsoft, só no do fork. A confiança era puramente visual.
A gente já viu esse mesmo filme no mundo de registros de pacotes. O comprometimento do LiteLLM no PyPI funcionou porque o ponto fraco é o acesso à publicação, não o código. Extensões são piores que pacotes em um aspecto importante: não existe cultura de lockfile, nem reprodutibilidade com npm ci, nem pinning por hash. O editor se atualiza em silêncio e, como um comentarista do Hacker News descobriu, o atraso de auto-update do VS Code nem se aplica aos "publicadores confiáveis", uma lista que você não consegue configurar. Seu conjunto de extensões é uma árvore de dependências viva, sem pinning, que roda com todos os seus privilégios de usuário e se atualiza enquanto você dorme.

Ferramentas de Codificação com IA São uma Escalada de Privilégio que Você Escolheu
Tire o drama do marketplace e veja qual era o alvo de verdade. Uma extensão de codificação com IA não é um tema de cores. Ela roda com as permissões da IDE, que são as suas permissões, e seu trabalho inteiro é ler a árvore de código, guardar suas credenciais de API e, cada vez mais, executar comandos de shell em seu nome. Quando escrevemos sobre como as CLIs de codificação agêntica lidam com permissões, o tema que se repetia era que o agente útil e o agente perigoso são separados por política, não por capacidade. Uma extensão que consegue rodar child_process está a uma publicação ruim de rodar qualquer coisa.
Um comentarista resumiu sem rodeios: todo mundo que usa essas ferramentas vem, na prática, rodando curl-pipe-bash todo dia, e passamos uma década dizendo para os devs juniores não fazerem isso. É exagero, mas só um pouco. A diferença entre curl | bash e uma extensão agêntica com auto-update é que a extensão tem uma interface mais bonita. Já defendi que ferramentas de CLI não deveriam instalar daemons em segundo plano por padrão; este incidente mostra o que acontece quando o daemon é instalado por outra pessoa e você nunca consentiu. Um processo ssh destacado que sobrevive ao fechamento da IDE é exatamente o padrão de comportamento que eu marco em revisões, só que entregue por um atacante por um canal de atualização confiável.
Como Checar Sua Frota Agora
Se alguém na sua organização usa o Cursor, trate isso como incidente ativo, não como notícia. Os indicadores de alto sinal, direto do relatório forense:
# macOS / Linux: the planted key is the cleanest signal
grep -s svc-telemetry ~/.ssh/authorized_keys && echo "IMPACTED" || echo "key not found"
# Check persistence artifacts
ls ~/Library/LaunchAgents/com.apple.sysmond.plist 2>/dev/null # macOS
ls ~/.config/.smt ~/.config/.smtk 2>/dev/null # macOS + Linux
crontab -l 2>/dev/null | grep -F .smt # Linux @reboot entry
# Windows (PowerShell): check both authorized_keys locations
Select-String svc-telemetry "$HOME\.ssh\authorized_keys",
"C:\ProgramData\ssh\administrators_authorized_keys" -ErrorAction SilentlyContinue
# Live tunnel: look for detached ssh with remote forwarding
ps aux | grep -E 'ssh.*-R' | grep -v grep
Algumas notas de caça da experiência. Primeiro, verifique o fingerprint da chave, não só o comentário: o implante deduplica pela string de comentário svc-telemetry, e uma tentativa de limpeza pode fazer o mesmo. Segundo, o arquivo da chave do túnel fica no diretório temporário com um nome como .stk_ seguido de dez caracteres hexadecimais derivados de um MD5 do hostname mais o caminho da home; é determinístico por máquina, então dá para calcular o nome exato do arquivo e varrer. Terceiro, a telemetria de processos do EDR não mostra a tentativa de ativação no macOS para usuários padrão, porque por causa do bug ela nunca executa. Ausência de systemsetup -setremotelogin nos seus logs não significa ausência de comprometimento. A chave e o LaunchAgent são a verdade de referência.
Se você encontrar a chave, a máquina recebe o tratamento completo de resposta a incidentes: rotacione toda credencial cujo nome de variável de ambiente possa ter sido enviado pelo beacon (ou seja, quase todas, já que o regex pega AWS, GCP e qualquer coisa com KEY ou TOKEN no nome), revogue o acesso da máquina e reinstale do zero. Não basta apagar a chave e dar o caso por encerrado. O operador teve, ou tentou ter, acesso shell interativo, e você não consegue enumerar tudo o que ele tocou só a partir do código do implante. É aqui também que os pontos cegos do log de autenticação em nuvem pesam: verifique os logs de auditoria da nuvem em busca de uso de console ou API na janela entre a atualização da extensão e a divulgação, filtrando pelas credenciais do usuário afetado.

Endurecimento: Contenha o Editor, Não Só a Extensão
Depois do incidente, todo mundo pergunta o que mudar. Minha posição, e defendo: você nunca vai auditar até chegar a um ecossistema de extensões seguro, então contenha. Cobrimos o trade-off geral em contenção versus monitoramento para agentes de IA, e este incidente é o estudo de caso da contenção. O monitoramento não te disse nada aqui até uma empresa de segurança terceirizada resolver dissecar o bundle.
- Fixe versões de extensões e desative o auto-update em tudo que tenha permissões amplas, e aplique a mesma regra nos forks. Sim, você vai herdar cansaço de atualização. Ainda assim é mais barato que resposta a incidentes.
- Filtragem de saída nas estações de desenvolvimento. Todo o modelo de acesso aqui dependia de uma conexão SSH de saída para um IP de VPS. Se suas máquinas de dev não conseguem abrir a porta 22 de saída para qualquer destino, a etapa do túnel morre. Quase ninguém faz isso, e teria neutralizado completamente este implante.
- Monitore o estado do SSH como canário. Alerte quando o Remote Login for ligado, quando surgirem novas chaves em authorized_keys e quando houver processos sshd em máquinas que não têm motivo para rodar sshd.
- Inventarie suas ferramentas de IA como inventaria servidores. Saiba quais extensões e CLIs de agentes estão aprovadas, quais permissões têm e de qual marketplace vêm. Se a resposta para "de qual registro vêm as instalações do Cursor da nossa empresa" for um encolher de ombros, esse é o projeto do seu próximo trimestre.
- Prefira agentes que rodem em sandboxes ou containers sempre que possível, com credenciais injetadas por sessão, e não ambientes na variável de ambiente. O scraping de nomes de variáveis do beacon funciona justamente porque deixamos segredos espalhados em ambientes de processos.
Um marketplace de extensões sem o próprio pipeline de verificação não é um marketplace: é um servidor de arquivos com um botão de instalar.
A Conclusão Desconfortável
A Anthropic não foi invadida. Foi o marketplace do Cursor, e a marca da Anthropic absorveu o estrago do mesmo jeito, porque, para um desenvolvedor, a extensão é o produto, independentemente de quem publicou qual build. É a economia dos ecossistemas com fork: a forma mais barata de atingir um alvo de alto valor é atacar o registro mais fraco em que os usuários desse alvo confiam. Assistentes de codificação com IA ficam exatamente na interseção perfeita: base instalada enorme, privilégio local máximo, credenciais em todo lugar e uma cadeia de suprimentos que faz fork e re-hospeda seus artefatos em registros com posturas de segurança muito diferentes.
Este caso terminou com as versões maliciosas removidas em cerca de onze horas e um bug no macOS limitando o dano. A resposta foi adequada. O problema é a arquitetura, e ela não mudou: seu editor é um gerenciador de pacotes que roda tudo o que instala como você, se atualiza em silêncio e, em um número crescente de configurações, obtém seus pacotes de um registro que ninguém na sua equipe de segurança jamais avaliou. Corrija as regras de saída, fixe as extensões e inventarie os agentes. O próximo comprometimento de publicador não vai ter um bug de regex salvando seus usuários.


