Incidente · malware npm que se autopropaga

O worm npm Shai-Hulud: o que fez no Mac de um desenvolvedor e o que o Kilobat teria mostrado

Em setembro de 2025, um worm se espalhou pelo npm: cada pacote infectado roubava tokens do desenvolvedor ou do sistema de CI, usava qualquer token npm encontrado para publicar versões infectadas dos outros pacotes do mantenedor e vazava segredos pelo GitHub. Uma segunda onda, maior, em novembro, era executada antes mesmo do fim da instalação.

Quando: setembro e novembro de 2025Fontes verificadas: 1º de novembro de 2026

Em resumo

  • Protection: Não ajudaria

    Nada para bloquear no Mac. O tráfego foi para npm, GitHub e APIs em nuvem; webhook.site foi acessado pelos runners do GitHub, não pelas máquinas dos desenvolvedores.

  • Agent guard: Não ajudaria

    A regra para webhook.site nunca foi acionada, porque a requisição veio de um runner do GitHub Actions. Os outros destinos eram hosts conhecidos ou endereços locais.

  • Registro de rede de agentes: Limitado

    Para uma instalação executada por um agente, ele teria mostrado TruffleHog, curl chamando a API do GitHub, um npm publish e hosts de nuvem acessados pela primeira vez, tudo sob a mesma instalação.

O que aconteceu

O GitHub afirma que foi informado do ataque em 14 de setembro de 2025. Socket e StepSecurity relataram primeiro @ctrl/tinycolor 4.1.1 e 4.1.2 e cerca de 40 outros pacotes; em uma semana o total passou de 500, incluindo pacotes publicados pela CrowdStrike. Cada versão infectada executava um bundle.js de 3,6 MB por um hook postinstall, apenas no macOS e Linux.

O script despejou o ambiente, baixou o verificador de segredos de código aberto TruffleHog das releases no GitHub e o executou em todo o sistema de arquivos, além de tentar os endpoints de metadados de nuvem que fornecem credenciais em máquinas na nuvem. Com credenciais AWS ou Google Cloud, ele listou e leu os segredos nos gerenciadores dessas contas.

Com um token npm de ~/.npmrc ou do ambiente, ele verificou o token pelo endpoint whoami do npm, procurou os outros pacotes do mantenedor, adicionou-se a cada um com um aumento de versão de patch e os publicou. Isso o tornou um worm: o token de um desenvolvedor infectou todos os pacotes que ele podia publicar.

Com um token GitHub, ele criou um repositório público chamado Shai-Hulud com os dados coletados em data.json codificado duas vezes em base64, enviou uma branch shai-hulud com um workflow, .github/workflows/shai-hulud-workflow.yml, para os repositórios acessíveis ao token e, em alguns casos, copiou repositórios privados de organizações para repositórios públicos marcados como “Shai-Hulud Migration”. Esse workflow rodava no GitHub e enviava os segredos do repositório a um endereço webhook.site. A Wiz descobriu que o webhook havia sido desativado por uso excessivo, mas o workflow também imprimia os segredos em seus registros.

A segunda onda, enviada entre 21 e 23 de novembro, usou novos arquivos, setup_bun.js e bun_environment.js, foi executada na fase preinstall e continuou rodando em segundo plano nas máquinas de desenvolvedores para que as instalações não parecessem lentas. Publicou segredos em repositórios com Shai-Hulud na descrição, mais de 25.000 segundo a Wiz, e registrou as máquinas infectadas como runners próprios do GitHub Actions chamados SHA1HULUD, permitindo uma entrada posterior.

Linha do tempo

  1. 14 de setembro de 2025

    O GitHub é informado de um worm autorreplicante em pacotes npm (GitHub).

  2. 15 de set. de 2025

    Socket e StepSecurity relatam @ctrl/tinycolor e mais de 40 pacotes adicionais.

  3. 16 de set. de 2025

    A Socket conta quase 500 pacotes afetados, incluindo os da CrowdStrike; a Aikido e outras empresas acompanham sua propagação.

  4. 22 de set. de 2025

    O GitHub removeu mais de 500 pacotes, fez o npm bloquear envios com indicadores do worm e anunciou publicação local apenas com 2FA, tokens granulares de 7 dias e publicação confiável.

  5. 23 de set. de 2025

    O alerta da CISA recomenda rotacionar as credenciais de desenvolvedor e bloquear conexões de saída para webhook.site.

  6. 21–23 de novembro de 2025

    Os pacotes da segunda onda são enviados ao npm (Wiz, Aikido).

  7. 24–26 nov. 2025

    Repositórios com segredos vazados aparecem aos milhares; o GitHub começa a removê-los e a revogar tokens vazados em 26 de novembro.

Como apareceu na rede

No Mac de um desenvolvedor, quase tudo foi para hosts que ele usa diariamente. De registry.npmjs.org ele buscou o pacote infectado, chamou /-/whoami e /v1/search, baixou outros pacotes do mantenedor e publicou novas versões: um upload de npm no meio de uma instalação. Em api.github.com ele leu a conta, criou o repositório Shai-Hulud e enviou branches e arquivos de workflow; o código desofuscado da StepSecurity mostra o módulo de porta dos fundos fazendo isso com curl. O TruffleHog veio dos downloads de releases do GitHub.

Com credenciais de nuvem, ele também acessou as APIs do AWS Secrets Manager e do Google Secret Manager. Tentou o endereço de metadados 169[.]254[.]169[.]254 e metadata[.]google[.]internal, que só respondem em máquinas na nuvem. A árvore de processos passou de npm por um shell até node bundle.js, que iniciou trufflehog, um script bash executando curl, e npm publish.

O único host incomum, webhook[.]site, não foi acessado pelo Mac. Ele estava no workflow que o worm enviou ao GitHub, então a requisição veio de um runner hospedado pelo GitHub quando o workflow foi executado.

Indicadores

Conforme documentados pelas fontes. Os domínios aparecem como texto, nunca como links, e são neutralizados ([.]) quando a fonte os neutraliza. A maior parte dessa infraestrutura já deixou de existir: os indicadores ajudam a verificar registros, arquivos e contas antigos, não a bloquear campanhas atuais.

IndicadorTipoOnde apareceObservação
webhook[.]site/bb8ca5f6-4175-45d2-b042-fc9ebb8170b7URLRunner do GitHub ActionsOnde o fluxo de trabalho injetado enviou os segredos de cada repositório. Desativado pela plataforma.
api.github.comHost (legítimo)Seu MacChamadas a /user e /user/repos e envios de branches e arquivos de workflow durante um npm install.
registry.npmjs.org/-/whoami, /v1/search?text=maintainer:…Chamadas à API do npmSeu MacVerificação do token e busca pelos pacotes do mantenedor, seguidas de npm publish.
secretsmanager.*.amazonaws.com, secretmanager.googleapis.comAPIs na nuvemSeu Mac, CIApenas onde havia credenciais de nuvem.
46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09SHA-256 de bundle.jsnode_modulesA carga maliciosa de setembro.
shai-hulud branch, .github/workflows/shai-hulud-workflow.ymlBranch, arquivoRepositórios do GitHubO fluxo de trabalho que enviou segredos ao webhook e aos registros.
Shai-Hulud repository with data.json; “Shai-Hulud Migration”Repositórios do GitHubConta GitHubDados coletados e repositórios privados copiados como públicos com o sufixo -migration.
setup_bun.js, bun_environment.js, SHA1HULUDArquivos, nome do runnernode_modules, GitHubA carga maliciosa da onda de novembro e o runner próprio que ela registrou.

O que o Kilobat teria mostrado ou bloqueado

  • Registro de rede dos agentes. Para uma instalação executada por um agente, a sessão agruparia os hosts por processo: trufflehog iniciado durante a instalação, curl chamando api.github.com, npm enviando a registry.npmjs.org e, com credenciais de nuvem no Mac, hosts das APIs da AWS ou Google sinalizados como primeira vez. Processos que continuaram em segundo plano após a instalação são acompanhados, já que processos passados ao launchd continuam vinculados ao agente.
  • Tráfego de apps. Mesmo sem um agente, a lista de apps do Kilobat mostra ferramentas de linha de comando que não fazem parte do macOS com seu caminho executável. Assim, um trufflehog que você nunca instalou ou um processo node enviando dados ao GitHub fica visível enquanto é executado.
  • Protection. Nada no Mac. npm, GitHub e provedores de nuvem não são ameaças, e o único host cujo bloqueio foi recomendado aos defensores era acessado dos runners do GitHub.
  • Agent guard. Sua regra de serviços que recebem requisições inclui webhook.site e o recusaria para um agente e tudo o que ele inicia, mas aqui a requisição veio dos runners do GitHub, nunca de um Mac. O endereço de metadados é link-local, tratado pelo Kilobat como rede local.

Onde o Kilobat não teria ajudado

  • O tráfego que causou o dano, no npm e no GitHub, usou os próprios tokens válidos do desenvolvedor e parece publicação e envio comuns.
  • A extração pelo webhook e os segredos impressos nos registros de workflow ocorreram nos runners do GitHub. Bloquear webhook.site, como a CISA recomendou, é mais importante onde o CI é executado.
  • O Kilobat registra hosts e contagens de bytes, nunca o conteúdo, então não pode dizer o que o TruffleHog encontrou ou o que um repositório recebeu.
  • Instalações em contêineres Docker ou pelo host de extensão de um editor no VS Code não são atribuídas a um agente.
  • Os pacotes infectados foram removidos e o registro de agentes de 30 dias não chega até 2025; resta verificar seus lockfiles, caches e sua conta GitHub.

Verifique seu Mac agora

  1. Compare seus lockfiles com as listas de pacotes publicadas pela Socket, StepSecurity e Wiz. Para um pacote:

    npm ls @ctrl/tinycolor
  2. Em uma pasta de projeto, procure os arquivos de carga maliciosa de qualquer uma das ondas.

    find . -path "*/node_modules/*" \( -name bundle.js -o -name setup_bun.js -o -name bun_environment.js \) -print
  3. Compare qualquer bundle.js com o hash de setembro. Uma correspondência é o worm; outros arquivos bundle.js são comuns e geralmente inofensivos.

    find . -path "*/node_modules/*" -name bundle.js -exec shasum -a 256 {} + | grep 46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09
  4. Liste seus repositórios GitHub cujo nome ou descrição mencione Hulud.

    gh repo list --limit 1000 --json name,description --jq '.[] | select((.name + " " + (.description // "")) | test("hulud"; "i")) | .name'
  5. Em Settings › Actions › Runners de cada organização e repositório, remova qualquer runner próprio que você não configurou, especialmente um chamado SHA1HULUD. No Mac, se você mesmo não executa um runner, isto não deve imprimir nada.

    pgrep -fl "Runner.Listener|Runner.Worker"
  6. Se algo corresponder, troque tokens do npm, tokens do GitHub e chaves de nuvem e liste seus tokens do npm para excluir os antigos.

    npm token list
  7. Para impedir que scripts de instalação sejam executados sem sua solicitação, desative-os no npm e execute-os por projeto quando necessário.

    npm config set ignore-scripts true
  8. Na próxima instalação, leia quais destinos cada instalação executada por um agente acessou. Observe uploads ao registro npm e chamadas à API do GitHub durante uma instalação.

    kilobat agent-log --all

Fontes

Verificado em 1º de novembro de 2026.

Veja a que seus agentes e apps se conectam

O Kilobat é um app para a barra de menus do Mac que identifica o app, processo e agente por trás de cada conexão, mantém essas conexões longe de hosts reconhecidamente maliciosos e registra o que cada sessão de agente acessou.

Baixar para Mac ↓
brew install seifip/kilobat/kilobat