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.
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 publishe 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
- 14 de setembro de 2025
O GitHub é informado de um worm autorreplicante em pacotes npm (GitHub).
- 15 de set. de 2025
Socket e StepSecurity relatam
@ctrl/tinycolore mais de 40 pacotes adicionais. - 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.
- 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.
- 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.
- 21–23 de novembro de 2025
Os pacotes da segunda onda são enviados ao npm (Wiz, Aikido).
- 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.
| Indicador | Tipo | Onde aparece | Observação |
|---|---|---|---|
webhook[.]site/bb8ca5f6-4175-45d2-b042-fc9ebb8170b7 | URL | Runner do GitHub Actions | Onde o fluxo de trabalho injetado enviou os segredos de cada repositório. Desativado pela plataforma. |
api.github.com | Host (legítimo) | Seu Mac | Chamadas 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 npm | Seu Mac | Verificação do token e busca pelos pacotes do mantenedor, seguidas de npm publish. |
secretsmanager.*.amazonaws.com, secretmanager.googleapis.com | APIs na nuvem | Seu Mac, CI | Apenas onde havia credenciais de nuvem. |
46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09 | SHA-256 de bundle.js | node_modules | A carga maliciosa de setembro. |
shai-hulud branch, .github/workflows/shai-hulud-workflow.yml | Branch, arquivo | Repositórios do GitHub | O fluxo de trabalho que enviou segredos ao webhook e aos registros. |
Shai-Hulud repository with data.json; “Shai-Hulud Migration” | Repositórios do GitHub | Conta GitHub | Dados coletados e repositórios privados copiados como públicos com o sufixo -migration. |
setup_bun.js, bun_environment.js, SHA1HULUD | Arquivos, nome do runner | node_modules, GitHub | A 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:
trufflehoginiciado durante a instalação,curlchamando api.github.com,npmenviando 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
trufflehogque 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
Compare seus lockfiles com as listas de pacotes publicadas pela Socket, StepSecurity e Wiz. Para um pacote:
npm ls @ctrl/tinycolorEm 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 \) -printCompare qualquer
bundle.jscom 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 46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09Liste 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'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"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 listPara 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 trueNa 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
- Blog do GitHub: nosso plano para uma cadeia de suprimentos npm mais segura
- CISA: comprometimento generalizado da cadeia de suprimentos que afeta o ecossistema npm
- StepSecurity: Shai-Hulud, worm que se replica compromete mais de 500 pacotes npm
- Socket: pacote npm popular tinycolor comprometido em ataque à cadeia de suprimentos que afeta mais de 40 pacotes
- Wiz: ataque Shai-Hulud à cadeia de suprimentos npm
- Aikido: atacantes do S1ngularity/nx atacam novamente
- Wiz: ataque Sha1-Hulud 2.0 à cadeia de suprimentos
- Unit 42: worm “Shai-Hulud” compromete o ecossistema npm em ataque à cadeia de suprimentos
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.
brew install seifip/kilobat/kilobat