- O pacote npm malicioso Shai-Hulud 2.0 infectou a máquina de um desenvolvedor e roubou o acesso à organização do GitHub da Trigger.dev
- A infecção começou quando o desenvolvedor executou
pnpm install, acionando o script preinstall do pacote malicioso, que usou a ferramenta TruffleHog para roubar credenciais
- Durante 17 horas, o invasor clonou 669 repositórios e, depois, em 10 minutos, tentou forçar push em 199 branches e fechar 42 PRs
- Os pacotes e os sistemas de produção não foram comprometidos, e o ataque foi detectado em 4 minutos, com o bloqueio do acesso à conta
- Após o incidente, a empresa reforçou a segurança com desativação de scripts npm, upgrade para o pnpm 10, publicação npm baseada em OIDC e proteção de branches aplicada em toda parte
Visão geral do ataque
- Em 25 de novembro de 2025, durante uma depuração interna no Slack, surgiu um comportamento anômalo: vários repositórios receberam um commit “init” em nome de Linus Torvalds
- A investigação confirmou que o worm de supply chain Shai-Hulud 2.0 infectou a máquina de um desenvolvedor e roubou credenciais do GitHub
- Foi relatado que esse worm infectou mais de 500 pacotes npm e impactou mais de 25.000 repositórios
- Os pacotes npm oficiais da Trigger.dev (
@trigger.dev/*, CLI) não foram infectados
Linha do tempo do ataque
- 24 de novembro, 04:11 UTC: início da distribuição do pacote malicioso
- 20:27 UTC: infecção da máquina de um desenvolvedor na Alemanha
- 22:36 UTC: primeiro acesso do invasor e início da clonagem em massa de repositórios
- 15:27~15:37 UTC (25 de novembro): execução do ataque destrutivo por 10 minutos
- 15:32 UTC: anomalia detectada e acesso bloqueado em 4 minutos
- 22:35 UTC: restauração completa de todos os branches
Processo de infecção
- Quando o desenvolvedor executou
pnpm install, o script preinstall do pacote malicioso foi acionado, baixando e executando o TruffleHog
- O TruffleHog escaneou e exfiltrou tokens do GitHub, credenciais da AWS, tokens npm, variáveis de ambiente e outros dados
- Na máquina infectada, foram encontrados o diretório
.trufflehog-cache e arquivos relacionados
- O pacote que causou a infecção foi removido, o que impossibilitou o rastreamento
Atividades do invasor
- Após a infecção, houve 17 horas de atividade de reconhecimento
- Foram clonados 669 repositórios usando infraestrutura baseada nos EUA e na Índia
- O invasor monitorou a atividade do desenvolvedor e manteve o acesso usando o token do GitHub
- Foi criado um repositório chamado “Sha1-Hulud: The Second Coming”, provavelmente usado para armazenar credenciais
- Depois disso, ocorreram 10 minutos de ações destrutivas
- Tentativa de forçar push em 199 branches de 16 repositórios
- Fechamento de 42 PRs; parte disso foi bloqueada por configurações de proteção de branch
- Todos os commits apareciam no formato “Linus Torvalds <email> / init”
Detecção e resposta
- As anomalias foram detectadas em tempo real por meio de alertas no Slack
- Em 4 minutos, o acesso da conta infectada ao GitHub foi bloqueado, e depois foram revogados todos os acessos a serviços como AWS, Vercel e Cloudflare
- A análise dos logs do AWS CloudTrail mostrou apenas chamadas de API em modo leitura, sem acesso a dados de produção
- A AWS também detectou separadamente atividade suspeita relacionada ao Shai-Hulud e enviou um alerta
Impacto e recuperação
- 669 repositórios clonados, 199 branches com tentativa de push forçado, 42 PRs fechados
- A recuperação foi difícil devido à ausência de reflog no lado do servidor do GitHub, mas o ambiente foi totalmente restaurado em 7 horas com uso da Event API e de reflogs locais
- Os pacotes npm e a infraestrutura de produção não foram comprometidos
Exposição de chave de GitHub App
- Durante a investigação, foi encontrada na lixeira do notebook do desenvolvedor uma chave privada de GitHub App
- Essa chave tinha permissão de leitura/escrita em repositórios de clientes e foi rotacionada imediatamente
- Como o banco de dados (que armazena os IDs de instalação) não foi comprometido, não há evidência de acesso aos repositórios de clientes, embora isso não possa ser totalmente descartado
- Foram solicitados logs adicionais ao suporte do GitHub e enviados avisos por e-mail aos clientes
Análise técnica do Shai-Hulud
- Ao executar
setup_bun.js, o malware instala o runtime Bun e roda bun_environment.js em segundo plano
- Usa o TruffleHog para coletar credenciais no diretório
$HOME
- Os dados coletados (
contents.json, cloud.json, truffleSecrets.json etc.) são enviados para um repositório aleatório no GitHub em formato com tripla codificação base64
- Se houver um token npm, o malware modifica e republica pacotes da conta infectada para espalhar o worm
- Se não houver credenciais, ele tenta apagar o diretório home
- Arquivos indicadores de infecção:
setup_bun.js, bun_environment.js, .trufflehog-cache/ etc.
Medidas de reforço de segurança
- Desativação completa de scripts npm (
ignore-scripts=true)
- Upgrade para o pnpm 10: scripts desativados por padrão e configuração de
minimumReleaseAge (3 dias) para atrasar a instalação de pacotes novos
- Adoção de npm Trusted Publishers baseado em OIDC para eliminar tokens de longa duração
- Proteção de branch aplicada a todos os repositórios
- Adoção do Granted no AWS SSO, com criptografia de tokens de sessão
- No GitHub Actions, fluxos de trabalho de contribuidores externos agora exigem aprovação para execução
Lições para outras equipes
- A própria estrutura de execução de código arbitrário durante a instalação via npm é uma superfície de ataque
- É necessário usar
ignore-scripts=true e manter whitelist apenas dos pacotes necessários
- Usar
minimumReleaseAge do pnpm para atrasar a instalação de novos pacotes
- Proteção de branch e deploy baseado em OIDC são medidas de segurança essenciais
- Não armazenar credenciais de longa duração em máquinas locais; permitir deploy apenas via CI
- O ruído dos alertas do Slack foi a chave para a detecção
Lado humano
- O desenvolvedor infectado não teve culpa; o dano ocorreu apenas por executar
npm install
- Durante o ataque, foi encontrado um rastro de que a conta marcou automaticamente com estrela centenas de repositórios aleatórios
- O incidente revelou não um erro individual, mas uma fragilidade estrutural do ecossistema
Indicadores resumidos
- Da infecção inicial até o primeiro ataque: cerca de 2 horas
- Tempo de manutenção do acesso pelo invasor: 17 horas
- Duração da ação destrutiva: 10 minutos
- 5 minutos até a detecção, 4 minutos até o bloqueio
- 7 horas até a recuperação completa
- Repositórios clonados: 669 / Branches afetados: 199 / PRs fechados: 42
Recursos de referência
- Socket.dev: Shai-Hulud Strikes Again V2
- Relatórios de análise da PostHog, Wiz, Endor Labs e HelixGuard
- Documentação do npm Trusted Publishers, pnpm
onlyBuiltDependencies, minimumReleaseAge, Granted
3 comentários
Pelo padrão, o
pnpmtinha uma estrutura em que era preciso permitir o post-install individualmente, mas no fim parece que até os desenvolvedores acabam permitindo isso sem perceber.Entendo que, como o npm vem configurado para executar por padrão, a segurança foi reforçada ao migrar para o pnpm e desativar esse comportamento padrão.
Comentários no Hacker News
Executar
npm installnão é negligênciaO problema é um ecossistema que permite execução arbitrária de código durante a instalação de pacotes
Mas a falha de segurança fundamental é usar um gerenciador de pacotes no qual terceiros podem injetar código no meu produto sem qualquer verificação
No fim, dependemos infinitamente da boa vontade e da competência dos gerenciadores de pacotes e de seus operadores
Além disso, o OP parece sugerir que armazenava credenciais em texto puro no sistema de arquivos
Dá para criar, no nível da linguagem, uma estrutura que limite o código a ler entradas, consumir recursos e gerar apenas saídas com tipos corretos
Isso não resolve completamente o problema da cadeia de suprimentos, mas reduz muito a área de exposição
É algo como: “não é errado uma pessoa usar essa ferramenta, mas se todo mundo usar, o ecossistema vira um problema”
Já foi provado várias vezes que muitas ferramentas de desenvolvimento são vulneráveis do ponto de vista de segurança
Se você realmente se importa, tem que mostrar isso em ações
Quando eu usava VS Code, era incômodo ter que instalar um plugin feito por alguém que eu nem conhecia só para adicionar uma funcionalidade pequena
No fim das contas, não é uma estrutura em que você inevitavelmente acaba executando código que precisa ser confiável?
netrcpara autenticação HTTPSe você usa git por HTTP, quase sempre existe esse caminho de exposição de credenciais em texto puro
Um maintainer do pnpm propôs há 1 ano bloquear scripts post-install por padrão
Do ponto de vista do usuário isso seria incômodo, mas ele acreditava que, no longo prazo, seria uma mudança pela qual todos ficariam gratos
PR relacionada: pnpm/pnpm#8897
No fim, é mais um caso em que a conveniência venceu a segurança
Disseram que “o banco de dados não foi comprometido”, mas se o atacante teve acesso à AWS e aos segredos, eu considero que já houve comprometimento
Se existia possibilidade de acesso, isso deve ser tratado como comprometimento
Depois que o malware é executado, fica quase impossível rastrear a origem
O
pnpm installtambém termina normalmente, então é difícil detectarSe houvesse um EDR como Sentinel One ou CrowdStrike, provavelmente haveria mais pistas para investigação
A parte “clonou um total de 669 repositórios” chamou atenção
Fico pensando se é normal uma empresa com menos de 100 funcionários ter mais de 600 repositórios
Tempo de existência e ciclo de vida dos projetos influenciam mais no número de repositórios do que o tamanho da equipe
O pnpm já parou de executar automaticamente scripts de lifecycle como
preinstall, então parece que eles estavam usando uma versão antigaVeja a PR relacionada
postinstallO pnpm bloqueia scripts das dependências, mas ainda executa scripts no nível do projeto
Obrigado por compartilhar um post-mortem transparente
Casos assim são importantes para a indústria como um todo
Fico curioso se o tráfego do ataque podia ser distinguido do tráfego normal de desenvolvimento
Nós também queremos reforçar o filtro de saída (egress filtering) em ambientes de desenvolvimento, mas o
npm installquebra com frequência, então isso é um problemaEstou pensando na segurança do git no meu notebook pessoal
Hoje deixo uma chave SSH localmente e faço push com ela
Também tenho privilégios de administrador, então isso é arriscado. Queria saber como tornar isso mais seguro
Assim, você consegue separar a chave de assinatura da chave de acesso, e a conta de administrador pode ser gerenciada separadamente
Documentação relacionada: 1Password SSH Agent, Git Commit Signing, GitHub OAuth, GitHub CLI Login
A vantagem é que a chave não pode vazar para fora, mas se um malware estiver rodando na minha máquina, ainda assim continua sendo arriscado
Exemplo no Linux, exemplo no macOS
Dá para dificultar um pouco com TPM ou Yubikey, mas defesa completa é impossível
O mais seguro é fazer tarefas administrativas em uma máquina dedicada separada
gpg-agentpara autenticação SSHAo fazer push ou commit, ela é desbloqueada com PIN
maine tornar MFA obrigatório, o atacante terá mais dificuldade para acessar imediatamente a branch de deployO commit com o nome Torvalds era uma marca registrada (signature) comum após a infecção
Isso também é mencionado na análise oficial da Microsoft
Esse worm era muito barulhento, e alguns atacantes exploraram credenciais expostas para tornar repositórios privados públicos ou alterar o README como forma de autopromoção
Fico pensando se essa detecção seria possível caso o atacante tivesse exfiltrado informações em silêncio sem fazer ações destrutivas