Malware com tema Shai-Hulud é encontrado na biblioteca de treinamento de IA PyTorch Lightning | Semgrep
(semgrep.dev)- As versões 2.6.2 e 2.6.3 do
lightningno PyPI foram publicadas em 30 de abril de 2026 e depois exploradas em um ataque à cadeia de suprimentos; apenas compip install lightning, um diretório oculto_runtimee um payload JavaScript ofuscado podem ser executados - O payload malicioso é executado automaticamente ao importar o módulo, roubando credenciais, tokens de autenticação, variáveis de ambiente e segredos de nuvem, além de tentar contaminar repositórios GitHub
- Embora o ponto de entrada deste ataque seja o PyPI, a propagação do worm acontece via npm; ao encontrar credenciais de publicação do npm, ele injeta o dropper
setup.mjserouter_runtime.jsem pacotes publicáveis e republica uma versão de patch - A exfiltração de dados usa 4 canais paralelos — HTTPS POST, dead drop por busca de commits no GitHub, repositórios públicos no GitHub controlados pelo atacante e push direto para repositórios da vítima — deixando como indicadores o prefixo de commit
EveryBoiWeBuildIsAWormyBoie a descrição de repositório"A Mini Shai-Hulud has Appeared" - O malware implanta o hook
SessionStartem.claude/settings.jsondo Claude Code e uma tarefarunOn: folderOpenem.vscode/tasks.jsondo VS Code para executar o dropper sempre que o repositório é aberto; qualquer máquina que tenha importado o pacote malicioso no período afetado deve ser tratada como totalmente comprometida
Pacotes afetados e procedimento de verificação
lightningé um framework de deep learning frequentemente presente na árvore de dependências de equipes que constroem classificadores de imagem, fazem fine-tuning de LLMs, executam modelos de difusão e desenvolvem preditores de séries temporais-
Pacotes afetados
lightningversão2.6.2lightningversão2.6.3
-
Procedimento de verificação para clientes da Semgrep
- Se você não executou uma varredura recente do projeto, deve iniciar uma nova varredura
- Na página de advisories, é possível verificar se o projeto instalou recentemente essas versões do pacote
- No filtro de dependências, é possível conferir correspondências; se aparecer “No matching dependencies”, isso significa que o projeto não está usando ativamente a dependência maliciosa
- Se houver correspondências, é preciso auditar no repositório arquivos inesperados nos diretórios
.claude/e.vscode/listados nos indicadores de comprometimento abaixo - É preciso substituir tokens do GitHub, credenciais de nuvem e chaves de API que possam ter estado em ambientes afetados
- Conselhos gerais sobre resposta a ataques na cadeia de suprimentos e períodos de espera são tratados em $foo compromised in $packagemanager e Attackers are Still Coming for Security Companies
Como a propagação vai do PyPI ao npm
- Diferentemente do mini Shai-Hulud, que mirava diretamente o npm, neste ataque o ponto de entrada é o PyPI
- O payload malicioso continua sendo JavaScript, e a propagação do worm ocorre via npm
- Quando o malware em execução encontra credenciais de publicação do npm, ele injeta o dropper
setup.mjserouter_runtime.jsem todos os pacotes publicáveis com esse token - Em seguida, configura
scripts.preinstallpara executar o dropper, incrementa a versão de patch e publica novamente - Desenvolvedores downstream que instalarem esses pacotes acabam executando o malware completo em suas máquinas, o que leva ao roubo de tokens e à infecção worm dos pacotes
Métodos de exfiltração de dados
- A funcionalidade de roubo compartilha mecanismos e design com a campanha anterior Mini Shai-Hulud e usa 4 canais paralelos para garantir a saída dos dados mesmo se caminhos individuais forem bloqueados
- O ataque inclui elementos com tema Shai-Hulud, como a criação de um repositório público chamado
EveryBoiWeBuildIsaWormBoi - A estrutura dos indicadores do ataque corresponde à campanha anterior mini Shai-Hulud, mas as mensagens de commit maliciosas usam o prefixo
EveryBoiWeBuildIsAWormyBoi, diferenciando-o do ataque original Mini Shai-Hulud -
Transmissão C2 via HTTPS POST
- Os dados roubados são enviados imediatamente por POST a um servidor controlado pelo atacante via porta 443
- O domínio e o caminho ficam armazenados no payload como strings criptografadas, dificultando a análise estática
-
Dead drop por busca de commits no GitHub
- O malware faz polling da API de busca de commits do GitHub em busca de mensagens com o prefixo
EveryBoiWeBuildIsAWormyBoi - As mensagens de commit carregam tokens com dupla codificação Base64 no formato
EveryBoiWeBuildIsAWormyBoi:<base64(base64(token))> - O token decodificado é então usado para autenticar um cliente Octokit para ações posteriores
- O malware faz polling da API de busca de commits do GitHub em busca de mensagens com o prefixo
-
Repositórios públicos no GitHub controlados pelo atacante
- Novos repositórios públicos são criados com nomes aleatórios baseados em palavras de Duna e a descrição
"A Mini Shai-Hulud has Appeared" - Essa descrição pode ser pesquisada diretamente no GitHub
- As credenciais roubadas são commitadas como
results/results-<timestamp>-<n>.json; elas são codificadas em Base64 via API, mas internamente permanecem JSON puro - Arquivos maiores que 30 MB são divididos em chunks numerados
- As mensagens de commit usam
chore: update dependenciescomo disfarce
- Novos repositórios públicos são criados com nomes aleatórios baseados em palavras de Duna e a descrição
-
Push direto para repositórios da vítima
- Se o malware obtiver um token de servidor GitHub
ghs_, ele envia diretamente os dados roubados para todos os branches deGITHUB_REPOSITORYda vítima
- Se o malware obtiver um token de servidor GitHub
Alvos do roubo
- O malware mira credenciais em arquivos locais, no ambiente, em pipelines de CI/CD e em provedores de nuvem
-
Sistema de arquivos
- Ele vasculha mais de 80 caminhos de arquivos de credenciais em busca de tokens
ghp_,gho_enpm_ - Processa até 5 MB por arquivo
- Ele vasculha mais de 80 caminhos de arquivos de credenciais em busca de tokens
-
Shell e variáveis de ambiente
- Executa
gh auth token - Faz dump de todas as variáveis de ambiente de
process.env
- Executa
-
GitHub Actions
- Em runners Linux, faz dump da memória do processo
Runner.Workerusando o Python embutido - Extrai todos os segredos marcados com
"isSecret":true, além deGITHUB_REPOSITORYeGITHUB_WORKFLOW
- Em runners Linux, faz dump da memória do processo
-
Organizações do GitHub
- Verifica os escopos de token
repoeworkflow - Percorre segredos organizacionais do GitHub Actions
- Verifica os escopos de token
-
AWS
- Tenta variáveis de ambiente, perfis em
~/.aws/credentials, IMDSv2169.254.169.254e ECS169.254.170.2para chamarsts:GetCallerIdentity - Enumera e recupera todos os valores do Secrets Manager e parâmetros do SSM
- Tenta variáveis de ambiente, perfis em
-
Azure
- Usa
DefaultAzureCredentialpara enumerar assinaturas e acessar segredos do Key Vault
- Usa
-
GCP
- Autentica com
GoogleAuth - Enumera e recupera todos os segredos do Secret Manager
- O escopo dos alvos inclui ambiente local de desenvolvimento, runners de CI e os 3 principais provedores de nuvem
- Toda máquina que tenha importado o pacote malicioso durante o período afetado deve ser tratada como totalmente comprometida
- Autentica com
Persistência por meio de ferramentas de desenvolvimento
- Depois de entrar no repositório, o malware mira ferramentas de desenvolvimento amplamente usadas, Claude Code e VS Code, para implantar hooks de persistência
-
Claude Code
- O malware grava um hook
SessionStartcommatcher: "*"no arquivo de configuração do Claude Code do repositório,.claude/settings.json - O hook aponta para
node .vscode/setup.mjs - Ele é executado sempre que o desenvolvedor abre o Claude Code no repositório infectado, sem exigir uso de ferramentas além do início da sessão nem ação do usuário
- O malware grava um hook
-
VS Code
- Para usuários do VS Code, ele implanta em
.vscode/tasks.jsonuma tarefa comrunOn: folderOpen - Sempre que a pasta do projeto é aberta,
node .claude/setup.mjsé executado
- Para usuários do VS Code, ele implanta em
-
Dropper
setup.mjs- Ambos os hooks chamam
setup.mjs, um bootstrapper autocontido do runtime Bun - Se o Bun não estiver instalado, ele baixa silenciosamente
bun-v1.3.13a partir do GitHub releases - Ele cobre Linux x64, Linux arm64, Linux musl, macOS x64, macOS arm64, Windows x64 e Windows arm64
- Em seguida, executa o payload completo de 14,8 MB
.claude/router_runtime.jse faz limpeza em/tmp
- Ambos os hooks chamam
-
Workflow malicioso do GitHub Actions
- Se o malware possuir um token GitHub com permissão de escrita, ele envia ao repositório da vítima um workflow chamado
Formatter - Em todo push, ele faz dump de todos os segredos do repositório usando
${{ toJSON(secrets) }} - Depois envia os resultados como um artefato de Actions baixável chamado
format-results - Para parecer legítimo, o Actions fica fixado em um commit SHA específico
- Repositórios que receberam o pacote
lightninginfectado em CI e tinham tokens com permissão de escrita devem auditar esses arquivos
- Se o malware possuir um token GitHub com permissão de escrita, ele envia ao repositório da vítima um workflow chamado
Indicadores de comprometimento
-
Indicadores pesquisáveis
- Mensagens de commit com o prefixo
EveryBoiWeBuildIsAWormyBoisão usadas como transportadoras de token do dead drop e podem ser encontradas pela busca de commits do GitHub - Repositórios GitHub com a descrição
"A Mini Shai-Hulud has Appeared"são repositórios de exfiltração do atacante e podem ser encontrados diretamente
- Mensagens de commit com o prefixo
-
Pacotes
lightning@2.6.2lightning@2.6.3
-
Arquivos e artefatos do sistema
_runtime/start.py: loader Python que inicializa o payload ao importar_runtime/router_runtime.js: payload JavaScript ofuscado e runtime Bun de 14,8 MB_runtime/: diretório adicionado às versões maliciosas do pacote.claude/router_runtime.js: cópia do malware injetada no repositório da vítima.claude/settings.json: configuração de hook do Claude Code injetada no repositório da vítima.claude/setup.mjs: dropper injetado no repositório da vítima.vscode/tasks.json: tarefa de execução automática do VS Code injetada no repositório da vítima.vscode/setup.mjs: dropper injetado no repositório da vítima
1 comentários
Comentários do Hacker News
Pode ser simplesmente uma ilusão de frequência, mas ultimamente tenho visto bastante ataque à cadeia de suprimentos conhecido em pacotes importantes
Só nas primeiras páginas do HN agora já há vários posts sobre casos diferentes
Olhando para o
left-padde 10 anos atrás, fico pensando se hoje há mais ataques bem-sucedidos do que antes, e provavelmente simO valor de um ataque bem-sucedido também deve ter aumentado bastante, mas fico me perguntando se a capacidade de detectá-los antes do release do pacote realmente está melhorando para a comunidade como um todo
Empresas de software comercial deveriam fazer melhor, mas ainda parece faltar uma ferramenta universal e fácil para os casos em que algo começa como código de hobby/amador e acaba virando dependência de muitos projetos
Também postei o mesmo comentário na thread atual sobre o ataque à cadeia de suprimentos da SAP: https://news.ycombinator.com/item?id=47964003
Antes era bem mais comum rodar
npm installmanualmente, e provavelmente só quando o build quebrava ou muito de vez em quandoAtaques à cadeia de suprimentos dependem de pessoas, ou mais precisamente pipelines, atualizarem pacotes automaticamente sem pensar assim que sai um novo release
Não sei se isso é um bom modelo de negócio, provavelmente não
left-padnão foi um ataque, foi um bug do NPMNunca deveria ter sido possível apagar uma versão de pacote da qual outros pacotes já públicos dependiam e, por outro lado, deveria ser possível apagar uma versão específica de pacote nova da qual ninguém dependesse
Quando o autor de
left-padtentou apagar todos os dados com a intenção de sair do serviço, o NPM deveria ter retornado um código de erroSegundo a Wikipedia, quando Koçulu disse que estava decepcionado com a decisão da npm, Inc. e não queria continuar fazendo parte da plataforma, Schlueter, criador do NPM, forneceu um comando para excluir os 273 módulos registrados por ele
O estranho é que 4 issues de segurança foram abertas e todas foram comentadas e fechadas automaticamente por um bot chamado
pl-ghost[1][2][3][4]No fim, só a [4] foi tratada corretamente, e os comentários do bot foram todos apagados
Dá para ver o comentário do bot em outro relatório [5], e ele traz mais informação do que o texto original
[1] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
[2] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
[3] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
[4] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
[5] https://socket.dev/blog/lightning-pypi-package-compromised
O invasor criou um novo workflow do Actions com essa conta e, no workflow executado, extraiu e levou os segredos do PyPI
Depois de publicar o pacote, ainda comentou com essa conta meio que zombando da gente
Espero que chegue logo o dia em que não existam mais dependências
Como exemplo extremo, hoje em dia, quando faço apps educacionais interativos para minha filha, peço ao Opus para usar apenas JavaScript e HTML puros
Desde pêndulo duplo até simulação de fluidos, tudo funciona bem de uma vez só, e antes isso teria centenas de dependências
Se o código tiver licença MIT, posso pedir ao Opus para extrair exatamente a parte necessária, adaptar ao meu uso e incluir
Em projetos de hobby isso tem funcionado bem até agora, e no futuro eu gostaria que software de produção também ficasse sem dependências
Se o Chrome mudar o formato de alguma API, você vai ter que achar e corrigir isso por conta própria, e se o Marrocos mudar a data de início do horário de verão, também vai ter que atualizar você mesmo o código de data/hora
Essas são coisas que bibliotecas resolvem por você e que acabamos tomando como garantidas
Num simulador de pêndulo duplo que sua filha vai perder o interesse na semana que vem isso não é grande coisa, mas para uma empresa que está construindo algo que precisa funcionar indefinidamente no futuro isso vira problema
Acho que vou publicar um código de acesso remoto sob licença MIT para ele entrar nos dados de treino do Opus
Quando fiz o curso de deep learning da Fast.AI, fiquei surpreso com a quantidade de dependências Python que um projeto de machine learning arrasta
Projetos de frontend web sempre foram vistos como cheios de dependências de terceiros, mas para mim o ecossistema de machine learning parece muito mais emaranhado
Além disso, o desenvolvimento web é visto como sensível à segurança, então acumulou muita sabedoria e prática de segurança ao longo do tempo, mas o desenvolvimento em machine learning parece muito mais improvisado, e várias práticas gerais de engenharia de software nem parecem ser aplicadas
Por exemplo, uma das formas de distribuir modelos de machine learning na época era com Python pickle, que é basicamente um objeto executável sem restrições por padrão
Modelos nesse formato podiam fazer qualquer coisa no computador que os importasse, e esse ecossistema inicial de terra sem lei pode tornar violações de segurança e ataques à cadeia de suprimentos muito mais fáceis
Alguns foram aprendendo a programar um pouco no caminho, alguns são matemáticos, e alguns são algo como desenvolvedores embriagados por IA
Também existe a mentalidade de que “código não importa mais, desde que funcione”
Para muita gente, gerenciamento adequado de dependências é só uma tarefa chata com a qual não querem se preocupar
Em vários projetos de machine learning esses fatores se combinam, quando na verdade projetos de machine learning deveriam ser um dos campos mais focados em reprodutibilidade
Pesquisando no repositório, vi que há 2,2 mil repositórios criados no último dia contendo o texto
"A Mini Shai-Hulud has Appeared": https://github.com/search?q=A%20Mini%20Shai-Hulud%20has%20Ap...Isso parece um sinal de que uma conta, provavelmente com token de autenticação/Actions do GitHub, foi comprometida e depois usada para criar repositórios
Isso já aconteceu antes, então eu teria esperado que tivessem aprendido a lição
Esse malware não fez muito esforço, e a Microsoft também não parece estar fazendo muito esforço
Para me resguardar, nunca usei pytorch e também não conheço muito bem práticas de segurança de software
Mas não consigo pensar em muitos cenários em que o pytorch precisaria de acesso à rede
Parece errado que qualquer módulo possa ser importado de qualquer lugar no codebase e usar essa API
Parece que seriam necessárias restrições adicionais de import ou análise estática
A linguagem não parece ter as abstrações certas para lidar com esse tipo de problema
Em comparação, gosto que em Rust eu consiga olhar só a assinatura da função e ver mutabilidade e tempos de vida sem entender o código interno
Sinto que precisamos de algo parecido para dependências
O desenvolvedor deveria conseguir auditar facilmente todas as dependências sem inspecionar o código subjacente e ver “ah, essa dependência usa
eval()” ou “tem acesso à rede”Apps móveis impõem permissões, então talvez desenvolvedores também devessem poder colocar apenas certas capacidades numa allowlist, em vez de aceitar um pacote inteiro com todo tipo de capacidade
Não gosto de generalizar, mas a comunidade de desenvolvimento em IA, em especial, parece preferir conveniência acima de qualquer outra consideração
Por exemplo, virou quase padrão que o projeto baixe automaticamente um modelo grande na primeira execução
Em geral dá para desativar, mas encontrar os parâmetros corretos é realmente doloroso por causa das camadas profundas de classes espalhadas por várias bibliotecas
É bom que tenha ficado muito fácil começar com coisas complexas, que em geral são quase brinquedos, mas essa atmosfera permissiva incomoda bastante
Parece que o primeiro passo para resolver qualquer problema é sempre “
pip install …”, e alguns ambientes, como MacOS, também não lidam bem com virtualização de acesso à GPUNesta semana eu estava me perguntando se usar uv para gerenciar versões de Python era uma boa ideia
No site [1] diz: “Como Python não fornece binários oficiais para distribuição, o uv usa distribuições do projeto Astral python-build-standalone”
Isso aponta para este repositório do GitHub https://github.com/astral-sh/python-build-standalone, que por sua vez menciona https://gregoryszorc.com/docs/python-build-standalone/main/r...
Se entendi corretamente, parece que ele não pega o código-fonte para builds do Python diretamente do python.org, e não tenho certeza de quão seguro isso é
Tenho a mesma preocupação com o asdf [2], mas o asdf usa pyenv [3], o que me parece mais próximo do oficial
Alguém pode explicar qual ferramenta é melhor e mais segura entre uv e asdf para instalar Python?
[1] https://docs.astral.sh/uv/guides/install-python/
[2] https://github.com/asdf-community/asdf-python
[3] https://github.com/pyenv/pyenv/tree/master/plugins/python-bu...
Até porque, de onde mais buscaria?
[1]: https://github.com/astral-sh/python-build-standalone/blob/a2...
uvecpython. O processo é robusto, a resposta é rápida e agora eles também têm bastante financiamentoO que me preocupa é algo como
mdformat, um formatador amplamente usado mas mantido principalmente por uma pessoa no tempo livre, ou alguma dependência muito específica que não recebe atualização há anos e está três níveis abaixo na árvore de dependênciasEu não gostaria de travar todas as atualizações e aprová-las manualmente em um app em desenvolvimento ativo, mas para apps sérios isso está começando a parecer obrigatório
Enquanto isso vou ali pegar chaves de API em arquivos
.envnão criptografadosSe acontecer com um grande webapp de consumo, é constrangedor mas compreensível, mas perder centenas ou milhares de dólares por causa de uma dependência indireta de um repositório de demo de brinquedo que por acaso está no mesmo host e sistema dói demais
Alguém sabe se OAI ou Anthropic reembolsam quando chaves são roubadas assim, ou isso é considerado erro do usuário?
Não sei o quanto o risco muda se os binários do Python forem compilados por eles ou por outra pessoa
Hoje em dia, a maior parte dos meus
pip installacontece porque o Claude Code sugere e eu simplesmente aperto EnterO modelo foi treinado com dados de alguns meses atrás, então não tem como saber o que foi comprometido nesta semana
Na prática eu criei o pior filtro possível para decidir “esse pacote está seguro agora?”
Você disse que deixa o Claude Code recomendar software para instalar da internet, e depois instala exatamente aquilo
Nunca ouvi ninguém sugerir que o Claude Code, ou qualquer LLM, seja um filtro para decidir “esse pacote está seguro agora?”, e isso me parece uma heurística péssima justamente pelos motivos que você mesmo citou
setup.pyvai executar na sua máquinaIsso porque não há nada inspecionando de fato o pacote antes da execução
O que falta é uma ferramenta que busque os metadados antes da execução e verifique que hooks existem
O Claude Code é atualizado quase todos os dias, às vezes várias vezes por dia
Se um dia a Anthropic for comprometida, todos nós vamos sofrer feio
Mas hoje em dia é tudo no YOLO
Vi esta mensagem no GitHub em 20 de abril e fiquei meio confuso
"deependujha hi @thebaptiste, thanks for inquiring. Release of 2.6.2 is blocked due to some internal reasons. Will notify once release is made."Se eles já sabiam do problema desde então e até agora não avisaram, eu acharia isso péssimo
Seria bom se alguém que sabe mais pudesse explicar com clareza
https://github.com/Lightning-AI/pytorch-lightning/issues/216...
Antes disso não havia distribuição afetada, e nós não sabíamos do vazamento
O release original no GitHub não tinha problema, mas ele foi retirado para evitar confusão