4 pontos por GN⁺ 2025-12-15 | 3 comentários | Compartilhar no WhatsApp
  • 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

 
click 2025-12-15

Pelo padrão, o pnpm tinha uma estrutura em que era preciso permitir o post-install individualmente, mas no fim parece que até os desenvolvedores acabam permitindo isso sem perceber.

 
lamanus 2025-12-16

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.

 
GN⁺ 2025-12-15
Comentários no Hacker News
  • Executar npm install não é negligência
    O 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

    • Acho que as duas coisas são um problema
      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
    • Essa lógica é circular demais
      É 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
    • O mesmo vale para plugins de IDE
      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
    • Fico curioso sobre como seria possível projetar um gerenciador de pacotes que impeça a execução de código de terceiros
      No fim das contas, não é uma estrutura em que você inevitavelmente acaba executando código que precisa ser confiável?
    • Algumas ferramentas só oferecem suporte a arquivo netrc para autenticação HTTP
      Se 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

    • Mesmo assim, o mesmo problema continua se repetindo
      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

    • Se existem logs de acesso aos recursos da AWS e não há sinal de acesso antes da revogação das permissões, então dá para considerar que os dados estão seguros
  • Depois que o malware é executado, fica quase impossível rastrear a origem
    O pnpm install também termina normalmente, então é difícil detectar
    Se houvesse um EDR como Sentinel One ou CrowdStrike, provavelmente haveria mais pistas para investigação

    • Se houvesse EDR, é bem provável que a cadeia de ataque do Trufflehog tivesse sido detectada ou bloqueada
  • 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

    • É completamente normal. Repositórios são gado, não animais de estimação
    • Nossa organização tem 7 pessoas, mas há 365 repositórios no GitHub
      Tempo de existência e ciclo de vida dos projetos influenciam mais no número de repositórios do que o tamanho da equipe
    • É isso que acontece quando há um arquiteto que adora microsserviços
  • O pnpm já parou de executar automaticamente scripts de lifecycle como preinstall, então parece que eles estavam usando uma versão antiga
    Veja a PR relacionada

    • No fim do artigo, mencionam que atualizaram para a versão major mais recente
    • Achei que esse fosse justamente o principal motivo para usar pnpm, então isso me confundiu
    • Se no fim eles fizeram atualização de dependências com um gerenciador de pacotes desatualizado, então isso é responsabilidade deles
    • O próprio projeto pode ter tido um script postinstall
      O 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 install quebra com frequência, então isso é um problema

    • Usar o recurso de lista de IPs permitidos da organização no GitHub parece que ajudaria a se defender desse tipo de ataque
  • Estou 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

    • Recomendo usar 1Password para gerenciar chaves SSH e assinatura do Git, e fazer push/pull com login via GitHub OAuth ou CLI
      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
    • Eu armazeno minha chave privada SSH no TPM e a uso via PKCS11 no SSH agent
      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
    • Se o notebook for infectado, não há defesa
      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
    • Também existe a opção de colocar uma chave GPG no Yubikey e usar gpg-agent para autenticação SSH
      Ao fazer push ou commit, ela é desbloqueada com PIN
    • Se você bloquear push direto para a branch main e tornar MFA obrigatório, o atacante terá mais dificuldade para acessar imediatamente a branch de deploy
  • O 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