Comprometimento da cadeia de suprimentos do node-gyp: um worm npm que se autopropaga e se esconde em binding.gyp
4 de junho de 2026
0 minutos de leituraUm ataque à cadeia de suprimentos está se espalhando ativamente pelo registro do npm ao explorar um arquivo que a maioria das ferramentas de segurança nem verifica: binding.gyp. Em vez de depender dos scripts de ciclo de vida preinstall ou postinstall, que são bem monitorados, o malware inclui um arquivo binding.gyp malicioso que aciona o node-gyp para executar automaticamente código controlado pelo invasor durante o npm install. A Snyk está acompanhando o incidente como Comprometimento da cadeia de suprimentos do Node-gyp - junho de 2026. São 57 pacotes afetados, com centenas de versões maliciosas, todas classificadas como código malicioso incorporado, com gravidade Crítica.
O payload rouba credenciais de desenvolvedores e ambientes de CI/CD em npm, GitHub, AWS, GCP, Azure, HashiCorp Vault e Kubernetes; exfiltra os dados por repositórios do GitHub controlados pelo invasor; injeta fluxos de trabalho do GitHub Actions para manter a persistência; e se propaga ao republicar pacotes a partir de qualquer conta de mantenedor que consiga acessar. A StepSecurity, que relatou a campanha primeiro, chamou a técnica de execução durante a instalação de "Phantom Gyp" e acompanha a campanha mais ampla como "Miasma", descendente da família de worms Shai-Hulud.
Em resumo
Tipo de ataque | Worm da cadeia de suprimentos por meio de contas de mantenedores comprometidas |
Técnica inédita | Execução de código durante a instalação por meio de |
Monitoramento da Snyk | |
Gravidade | Crítica (código malicioso incorporado) |
Data do incidente | 3 de junho de 2026 (onda principal); variante anterior do Miasma em 1º de junho de 2026 |
Pacotes comprometidos | 57 pacotes, centenas de versões maliciosas |
Vítimas com mais tráfego |
|
Comportamento do malware | Roubo de credenciais, injeção no GitHub Actions, persistência e propagação do worm pelo npm e RubyGems |
Ação imediata | Fixe versões confiáveis, execute |
Pacotes afetados
A Snyk lista 57 pacotes afetados. Confirmamos que as versões maliciosas listadas ainda podem ser obtidas no registro público (por exemplo, as quatro versões de @vapi-ai/server-sdk retornam tarballs disponíveis em registry.npmjs.org), com datas de publicação em 3 e 4 de junho de 2026. Estas são as vítimas com mais tráfego, segundo os dados semanais de downloads da API do registro do npm:
Pacote | Downloads semanais | Versões maliciosas |
|---|---|---|
~86.500 (api.npmjs.org) | 0.11.1, 0.11.2, 1.2.1, 1.2.2 | |
~36.900 (api.npmjs.org) | 0.13.1, 1.1.1, 2.2.1, 3.8.5 | |
~5.900 (api.npmjs.org) | 2.26.4, 3.4.3 | |
~280 (api.npmjs.org) | 1.33.3 |
A maioria dos 57 pacotes se concentra em uma única conta do npm. Uma consulta ao registro mostra que todos os 25 pacotes autotel e autotel-* têm o mesmo mantenedor, jagreehal, a mesma conta que publica o escopo @jagreehal/* e o awaitly. Essa concentração em uma única conta é exatamente o que se esperaria de um worm que enumera e republica tudo o que uma conta comprometida consegue acessar:
Família
autotel-*(24 pacotes, além do próprioautotel): incluiautotel-mcp(com versões que vão de0.1.14a29.0.1),autotel-subscribers,autotel-terminal,autotel-mongoose,autotel-eventcatalog,autotel-devtools,autotel-aws,autotel-cloudflare,autotel-hono,autotel-playwright,autotel-sentrye outrosFamília
eslint-plugin-executable-stories-*(a Snyk já abordou malware na cadeia de suprimentos do npm em plugins do ESLint)Pacotes
@jagreehal/*@evolvconsulting/evolv-coder-lite
Muitos dos números de versão inflados (por exemplo, autotel-mcp chegando à série 29.x, ou autotel recebendo as versões 2.26.4 e 3.4.3 publicadas no mesmo minuto em 4 de junho) são, por si só, consequência da republicação automatizada do worm, e não de lançamentos legítimos. A lista completa e atualizada continuamente de pacotes e versões afetados está na página do incidente da Snyk.
Um detalhe importante para a correção: em pelo menos alguns pacotes, a tag de distribuição latest já foi redirecionada para uma versão limpa (no caso de autotel, latest agora aponta para 3.4.2, publicada antes das versões maliciosas), mas as versões maliciosas não foram removidas. No momento da publicação, autotel@3.4.3 e autotel@2.26.4 ainda retornam tarballs disponíveis. Um npm install autotel feito agora talvez instale a versão limpa, mas qualquer arquivo de lock, versão fixada ou dependência transitiva que faça referência a uma versão maliciosa ainda baixará o malware. Não considere uma tag latest limpa como prova de que você está seguro.
Como o ataque funciona
A novidade: execução durante a instalação por meio de binding.gyp
Quando você executa npm install em um pacote que contém um arquivo binding.gyp e não tem um binário pré-compilado correspondente, o npm encaminha o pacote ao node-gyp e executa node-gyp rebuild para compilar o que presume ser um complemento nativo em C/C++. Esse é o comportamento normal e esperado para qualquer pacote com componentes nativos, e ocorre sem que haja uma entrada preinstall ou postinstall em qualquer lugar do package.json.
A sintaxe de configuração de build do GYP permite expandir comandos. A forma <!(...) executa um comando de shell durante a fase de configuração e substitui sua saída na definição do build. Os pacotes comprometidos exploram isso diretamente. Este é o arquivo binding.gyp exato, com 157 bytes, distribuído em @vapi-ai/server-sdk@1.2.2 e autotel@3.4.3 (idêntico byte a byte nos pacotes que analisamos):
A expressão <!(node index.js ...) executa node index.js enquanto o node-gyp está apenas configurando o build, muito antes de qualquer compilador ser iniciado. O destino "type": "none" significa que nada é compilado de fato; portanto, o efeito colateral da expansão do comando (executar index.js) é o objetivo inteiro. A saída é redirecionada para /dev/null para que a instalação pareça normal, e echo stub.c retorna um nome de arquivo-fonte plausível para que o gyp continue sem erros óbvios. O resultado é a execução de código arbitrário durante um simples npm install.
É importante destacar que o package.json desses tarballs não contém scripts preinstall, postinstall, install nem prepare. As únicas entradas em scripts são tarefas comuns de desenvolvimento (build, lint, test, format), e o pacote nem sequer declara "gypfile": true. Não há nada no package.json que as ferramentas focadas em scripts possam sinalizar; a presença de um arquivo binding.gyp basta para o npm acionar o node-gyp por conta própria.
O payload: um loader em várias etapas baseado em Bun
O index.js acionado por binding.gyp é um loader ofuscado de 4,5 MB. Decodificamos estaticamente as camadas externas a partir do tarball publicado (sem executá-lo) e confirmamos a seguinte sequência:
Cifra de César ROT-14. O arquivo inteiro consiste em um único
evalsobre um array de códigos de caracteres com cerca de 1,3 milhão de entradas, convertido em uma string e deslocado em 14 posições. O trecho visível é literalmenteeval(function(s,n){return s.replace(/[a-zA-Z]/g,...rotate...)}([...],14)).Camada de descriptografia automática AES-128-GCM. A etapa decodificada é uma IIFE
asyncque importanode:cryptoe define um descriptografadoraes-128-gcm(createDecipherivcom uma tag de autenticação de 16 bytes). Em seguida, descriptografa dois blocos de texto cifrado incorporados, cujas chaves hexadecimais, IVs e tags de autenticação estão codificados diretamente no arquivo.Loader do runtime Bun (bloco de 907 bytes). O primeiro bloco descriptografado detecta o sistema operacional e a arquitetura, baixa um binário autônomo do Bun v1.3.13 das versões oficiais do GitHub
oven-sh/bunpara um diretório temporário e o executa:
Payload principal (bloco de ~649 KB). O segundo bloco descriptografado (664.535 bytes) contém a lógica de roubo em si, executada pelo binário do Bun baixado, e não pelo processo Node.js que iniciou a instalação.
Executar a lógica principal em um binário do Bun baixado, em vez de usar o processo node que iniciou a instalação, é uma técnica deliberada de evasão: o monitoramento limitado a processos filhos do Node.js durante o npm install não detectará o processo do Bun que realiza o trabalho de fato. Isso também explica por que uma busca por strings em texto simples no index.js não encontra credenciais nem indicadores de C2: esse comportamento está dentro do bloco executado pelo Bun. Não executamos essa etapa final do Bun; por isso, o catálogo de comportamentos abaixo se baseia em análises divulgadas publicamente.
Roubo de credenciais
Quando executado, o payload vasculha ambientes de desenvolvimento e CI/CD em busca de segredos, com foco em:
AWS:
aws_access_key_id/aws_secret_access_keye o endpoint de metadados IMDSv2 (169.254.169.254)GCP:
GOOGLE_APPLICATION_CREDENTIALSe chaves de contas de serviçoAzure: tokens de identidade gerenciada via IMDS
GitHub Actions:
ACTIONS_ID_TOKEN_REQUEST_TOKENe extração de dados da memória dos processos do runnerHashiCorp Vault e Kubernetes: tokens de contas de serviço em caminhos padrão
Gerenciadores de senhas: armazenamentos do 1Password,
passegopass
No GitHub Actions, o payload vasculha a memória dos processos do runner para recuperar segredos mascarados em sua forma não mascarada, usando um padrão como:
O mascaramento de segredos do GitHub Actions oculta os segredos nos logs; não os protege de um processo que consiga ler diretamente a memória do runner. Considere exposto qualquer segredo que o runner consiga acessar.
Exfiltração por repositórios do GitHub
Em vez de usar um domínio C2 fixo, o malware usa o próprio GitHub como ponto de encontro e repositório temporário. A atividade foi vinculada à conta do GitHub liuende501, que, no momento da publicação, hospedava 321 repositórios públicos (api.github.com/users/liuende501), algo consistente com o padrão do worm de criar repositórios automaticamente para receber dados roubados. O fluxo é o seguinte:
Localiza o ponto de encontro ao pesquisar commits públicos por uma palavra-chave codificada
Cria um repositório na hora com um nome aleatório
Envia os dados coletados e criptografados como
results/results-{timestamp}.jsonFaz solicitações à API com o User-Agent
python-requests/2.31.0
Usar o GitHub para exfiltrar dados mistura o tráfego à atividade normal de desenvolvedores e de CI, já que conexões de saída para github.com e api.github.com raramente são bloqueadas em ambientes de build.
Propagação do worm entre ecossistemas
O payload se autopropaga, com mecanismos separados para cada ecossistema:
Worm do npm: enumera os pacotes de um mantenedor por meio de
registry.npmjs.org/-/v1/search?text=maintainer:{username}, baixa cada pacote-alvo, injeta obinding.gype oindex.jsmaliciosos e os republica. Como nas ondas anteriores dessa família (veja a cobertura da Snyk sobre TanStack, o primeiro pacote malicioso documentado do npm com proveniência SLSA válida), o worm também forja atestações de proveniência do Sigstore por meio do Fulcio e do Rekor, fazendo com que pacotes reinfectados pareçam ter uma assinatura legítima.Worm do RubyGems: injeta lógica equivalente em
extconf.rb, o hook de build de extensões nativas do RubyGems, e reutiliza o mesmo downloader do Bun. Oextconf.rbestá para o RubyGems assim comobinding.gypestá para o npm: é um arquivo de build executado automaticamente e não é um "script" no sentido dos scripts de ciclo de vida.Envenenamento de repositórios do GitHub: insere arquivos com backdoors em repositórios nos quais os tokens roubados têm permissão de escrita, incluindo hooks de agentes de programação com IA e de editores (
.claude/,.cursor/rules/,.vscode/tasks.json) que executam novamente o payload quando um desenvolvedor abre o projeto.
O alcance entre ecossistemas (npm e RubyGems) e o uso, em ambos, de arquivos de extensão executados durante o build são o fio condutor desta campanha: encontrar o arquivo executado automaticamente na instalação ou no build, mas que ninguém classifica como “script”.
Análise do impacto
O impacto direto abrange qualquer máquina de desenvolvedor ou runner de CI que tenha executado npm install e resolvido uma das versões dos pacotes afetados. Os ambientes de CI/CD correm mais risco, pois a captura de dados da memória do runner significa que todos os segredos aos quais ele tem acesso — não apenas os definidos explicitamente como variáveis de ambiente — devem ser considerados comprometidos.
As máquinas dos desenvolvedores correm um risco secundário e mais duradouro por causa dos hooks de editores e agentes de IA, que permanecem mesmo após um simples npm uninstall e executam novamente o payload na sessão seguinte.
A probabilidade de a campanha se espalhar ainda mais parece menor do que no auge: os pacotes afetados estão ligados a poucas contas de mantenedores (confirmamos que os 25 pacotes autotel e autotel-* compartilham a mesma conta, jagreehal), e não foram observados lançamentos comprometidos recentes. Ainda assim, as versões maliciosas continuam disponíveis para instalação no npm, então o risco de exposição persiste para quem as obtiver, direta ou indiretamente, antes de serem totalmente removidas.
Detecção
Faça uma verificação com o Snyk. O Snyk publicou alertas sobre as versões afetadas no Snyk Vulnerability Database e criou uma página do incidente em security.snyk.io/node-gyp-supply-chain-compromise-june-2026. Faça uma verificação do seu projeto:
Para verificar um arquivo de manifesto ou lockfile específico:
Procure a técnica, não apenas os nomes dos pacotes. Como o caminho de execução é binding.gyp, você pode procurá-lo sem depender da lista de pacotes:
Indicadores de rede e comportamento:
node-gyp rebuildé executado para pacotes que não têm nenhum complemento nativo legítimoProcessos filhos inesperados (
curl,unzip,bun) iniciados durantenpm installUm binário independente do
buné baixado dos lançamentos deoven-sh/bundurante a instalação, sem que você tenha solicitado o BunChamadas à API do GitHub com um User-Agent
python-requests/2.31.0originadas de uma etapa de CI que não é um processo Python
Remediação
Se você não tem certeza se foi afetado, considere que houve um comprometimento confirmado. Os dados coletados são criptografados antes da exfiltração, então não é possível determinar depois o que foi roubado.
Etapa 1: Remova a persistência antes de trocar os tokens. Se hooks de editores ou agentes de IA tiverem sido implantados, remova-os primeiro para que não reajam às alterações nas credenciais:
Etapa 2: Limpe e reinstale usando versões confiáveis.
--ignore-scripts bloqueia o hook implícito de instalação do npm que executa node-gyp rebuild em pacotes com binding.gyp. No entanto, não deve ser considerado a única proteção: o pacote malicioso ainda pode ser baixado e descompactado, outras ferramentas ou um npm rebuild posterior sem --ignore-scripts podem compilá-lo, e outros gerenciadores de pacotes ou fluxos de trabalho podem se comportar de outra forma. A remediação mais segura continua sendo fixar ou remover as versões maliciosas e fazer uma verificação antes de qualquer etapa de build.
Etapa 3: Troque todas as credenciais acessíveis a partir de uma máquina ou runner afetado:
Tokens de publicação do npm
Tokens de acesso pessoal do GitHub e secrets do Actions (com escopo de repositório e organização)
Chaves de acesso da AWS e quaisquer funções do IAM acessíveis a partir dos runners afetados
Chaves de contas de serviço do GCP
Entidades de serviço do Azure e escopos de identidade gerenciada
Tokens do HashiCorp Vault
Tokens de contas de serviço do Kubernetes
Qualquer dado em um cofre de gerenciador de senhas visado (1Password,
pass,gopass)
Etapa 4: Audite o GitHub em busca de workflows injetados e repositórios de entrega de payloads.
Etapa 5: Reforce as defesas contra a próxima onda.
Use
npm install --ignore-scriptspor padrão na CI (uma prática recomendada para se proteger contra a família mais ampla de ataques durante a instalação) e combine isso com uma etapa de verificaçãoFixe as dependências em versões exatas, com hashes de integridade no lockfile
Considere uma política de espera no registro, que retenha os pacotes publicados nos últimos dias antes de permitir que sejam incluídos nos builds
Aplique o princípio do menor privilégio aos tokens de CI/CD para limitar o impacto de um token roubado
Use o Snyk para monitorar continuamente sua árvore de dependências e identificar pacotes maliciosos assim que forem sinalizados
O guia do Snyk sobre como prevenir ataques à cadeia de suprimentos do npm traz uma lista de verificação mais abrangente.
Contexto mais amplo: um descendente de Shai-Hulud
Esta é a onda mais recente da linhagem Shai-Hulud / Miasma, uma família de worms do npm que se autorreplicam e atacam o registro repetidamente desde o fim de 2025. O Snyk analisou uma onda anterior do Miasma, em junho de 2026, que atingiu pacotes npm da Red Hat; as descrições dos repositórios de exfiltração usados nessas ondas fazem referência direta às campanhas anteriores do Shai-Hulud. Cada onda reutilizou um ladrão de dados ofuscado que usa o runtime Bun e o ampliou com novos métodos de persistência e rotas de exfiltração, além de novas formas de executar código automaticamente durante a instalação ou o build. Vale compreender a evolução desse truque de execução automática:
As ondas anteriores dependiam de scripts de ciclo de vida
preinstall/postinstallAs ondas seguintes adicionaram hooks de agentes de programação com IA (
SessionStart) e tarefas de IDE executadas ao abrir pastas para manter a persistênciaNesta onda, a execução inicial passa para
binding.gyp/node-gyp(eextconf.rbno RubyGems): arquivos executados durante o build que não são scripts de ciclo de vida
O Snyk analisou em detalhes ondas anteriores desta família de campanhas:
Ladrão de dados baseado em Bun atinge pacotes npm SAP CAP-JS e MBT
Incidente na cadeia de suprimentos do npm relacionado ao SHA1-Hulud
Ataque à cadeia de suprimentos do npm por comprometimento de um mantenedor de código aberto
Para conhecer melhor a família de worms da qual este incidente se originou:
Pesquisador de segurança do Snyk explica o worm Mini Shai-Hulud na cadeia de suprimentos do npm e como ele se propaga Mini Shai-Hulud: o ataque mais sofisticado à cadeia de suprimentos do NPM em 2026 (Visão geral da família de worms Shai-Hulud / Miasma e de como ela se autorreplica)
Para acompanhar um passo a passo prático de como encontrar e remediar pacotes comprometidos desta família de campanhas com o Snyk:
Engenheiro de segurança do Snyk mostra como identificar e remediar pacotes npm comprometidos pelo Shai-Hulud usando a CLI do Snyk Ataque Shai-Hulud ao NPM: remediação com o Snyk - Passo a passo para identificar e remediar pacotes comprometidos usando as ferramentas do Snyk.
Para entender como o comprometimento de um pacote legítimo e confiável se encaixa no modelo mais amplo de ameaças à cadeia de suprimentos, a lição do Snyk Learn sobre Comprometimento de pacotes legítimos é um bom ponto de partida.
Cronologia (UTC)
Data | Evento |
|---|---|
1º de junho de 2026 | Uma variante anterior do Miasma compromete um grupo separado de pacotes npm (relacionados à Red Hat) |
3 de junho de 2026 | Onda principal: |
3 de junho de 2026 em diante | Publicada uma análise técnica da técnica “Phantom Gyp”; o Snyk publica alertas e uma página ativa do incidente |
Em andamento | A investigação continua; versões maliciosas seguem disponíveis no npm até serem removidas |
Cobertura do Snyk
O Snyk publicou alertas sobre as versões afetadas no Snyk Vulnerability Database e mantém uma página ativa do incidente com a lista dos 57 pacotes e suas versões maliciosas. Os clientes do Snyk podem usar a verificação do Snyk em todo o SDLC para identificar quais projetos obtêm versões afetadas, direta ou indiretamente, e priorizar a remediação com base na exposição.
Consulte o Snyk Vuln Database
Dados confiáveis e insights práticos para ajudar você a desenvolver software com segurança.
