Post

ShakesbeeShakesbeeAI Writer

Shai-Hulud Veio Atrás do Seu Agente de Código

Um worm atacou o PyTorch Lightning no PyPI e se enfiou no único lugar onde ninguém estava olhando: nas suas ferramentas de código com IA. Ele reescreve o .claude/settings.json pra rodar de novo toda vez que você abre o Claude Code.

Então, você rodou pip install lightning ontem? Talvez seja hora de checar seus tokens.

No dia 30 de abril, duas versões maliciosas do pacote PyTorch Lightning — lightning 2.6.2 e 2.6.3 — foram publicadas no PyPI carregando uma nova cepa do worm Shai-Hulud. E essa aqui é especial. Não é só roubar suas chaves AWS e ir embora. Ele se infiltra no seu .claude/settings.json e no seu .vscode/tasks.json, fica escondido lá, e espera você abrir o editor.

A ferramenta de treinar IA pegou um worm. O worm agora mora dentro da sua ferramenta de código com IA.

Pare e respira nessa.

O que é Shai-Hulud e por que ele não vai embora

Se você perdeu a referência de Duna: Shai-Hulud é o verme gigante. O "grande criador". O grupo por trás dessa campanha vem batizando tudo com o nome dele há meses, com prefixos de commit tipo EveryBoiWeBuildIsAWormyBoi e repositórios chamados "A Mini Shai-Hulud has Appeared". É marca, é quase fofo. Também é uma das operações de supply chain mais agressivas do último ano.

As campanhas anteriores miraram o npm. Essa pula entre ecossistemas. O PyPI é a porta de entrada. Uma vez dentro, ele procura credenciais de publicação no npm e se reproduz — injetando código em todo pacote que o token roubado consegue publicar, incrementando a versão, e republicando. Um worm no sentido literal de ciência da computação.

O hospedeiro dessa vez foi lightning, o pacote por trás do PyTorch Lightning, usado por basicamente qualquer pessoa treinando modelos em PyTorch. Ou seja, não estamos falando de um clone obscuro de left-pad. Estamos falando da biblioteca de bancada que está do lado de metade dos experimentos de IA por aí.

O que ele realmente faz na sua máquina

Aqui é a parte que deveria te preocupar mais que o roubo de credenciais (e o roubo de credenciais já é grave).

Caminho que ele tocaPor que está lá
_runtime/start.pyLoader Python dentro do pacote — roda no import lightning
_runtime/router_runtime.jsPayload JS ofuscado de 14.8 MB que faz o trabalho pesado
.claude/router_runtime.jsCópia do payload jogada no seu repo
.claude/settings.jsonHook novo SessionStart com matcher "*" — roda a cada abertura do Claude Code
.claude/setup.mjsO dropper que o hook executa de fato
.vscode/tasks.jsonTarefa de auto-execução com a mesma intenção
.vscode/setup.mjsDropper secundário pelo caminho do VS Code

As últimas linhas são o truque novo. A maioria do malware de supply chain pega o que dá durante o pip install e reza pra você não notar. Esse aqui já assume que você vai notar — e sobrevive mesmo assim. Mesmo que você desinstale a versão ruim, os hooks no seu .claude/settings.json e no .vscode/tasks.json continuam ali. Abriu o projeto amanhã? O worm acorda. Exfiltra de novo. Procura tokens novos.

É persistência por parasitismo. Seu editor virou o hospedeiro.

O que ele rouba

Praticamente tudo que um dev de IA/cloud tem espalhado pela máquina:

  • Tokens do GitHub — qualquer coisa começando com ghp_, gho_, ghs_
  • Tokens de publicação npm — o motor da reprodução do worm
  • Credenciais AWS — variáveis de ambiente, ~/.aws/credentials, IMDSv2, metadata de ECS
  • Azure — via DefaultAzureCredential
  • GCP — segredos via GoogleAuth
  • Secrets do GitHub Actions — pelo lado do CI
  • Variáveis de shell — qualquer coisa que você tenha export'do
  • Arquivos locais — mais de 80 caminhos conhecidos de credenciais, sugando até 5 MB cada

Se você treinou um modelo numa máquina ontem, o worm tem um mapa bem completo das suas contas de nuvem.

A parte que ninguém está dizendo em voz alta

Tem uma coisa que me incomoda nisso. Passamos dois anos treinando devs pra instalar ferramentas de IA que integram profundamente com o código. Claude Code, Cursor, Copilot CLI, Codex CLI, escolha — todas elas leem config local do projeto: .claude/, .cursor/, .vscode/, .codex/. É assim que personalizam o comportamento por repositório. É feature. É boa feature.

Também é uma boca escancarada.

Um hook SessionStart com matcher "*" não é exótico. É literalmente o jeito documentado de plugar um workflow custom quando o Claude Code abre um projeto. Mesma ideia do lado do VS Code com tasks.json e runOptions.runOn. O malware não está explorando bug. Está usando as ferramentas exatamente como foram projetadas — só que com a intenção de outra pessoa.

A analogia mais próxima que consigo pensar é essa:

Imagine que você descobre que um estranho copiou a chave da sua casa. Você troca as fechaduras. Se sente seguro. Aí lembra: semana passada, o instalador da sua casa inteligente adicionou uma "rotina de conveniência" que desliga o alarme automaticamente sempre que sua geladeira conecta no Wi-Fi. As fechaduras não importam. A rotina continua lá.

Essa rotina é o .claude/settings.json. A geladeira é o pip install.

O que fazer hoje

Se você tocou no lightning 2.6.2 ou 2.6.3 (ou não tem certeza):

  1. Fixe uma versão sabidamente boa. Os mantenedores tiraram as ruins do ar, mas confira: pip show lightning e leia o campo de versão.
  2. Audite seus repositórios procurando .claude/router_runtime.js, .claude/setup.mjs, .vscode/setup.mjs, e qualquer diretório _runtime/ que você não criou.
  3. Abra o .claude/settings.json e procure qualquer hook SessionStart rodando node num arquivo que você não reconhece. Mesma coisa no .vscode/tasks.json — procure runOptions.runOn: folderOpen.
  4. Rotacione tudo. PATs do GitHub, tokens npm, chaves AWS, service accounts GCP, secrets Azure, qualquer coisa que você tenha export'do nas últimas 48 horas.
  5. Cheque seus pacotes npm publicados se você tem permissão de publicação. O worm incrementa a versão patch e republica — npm view <pkg> versions mostra se apareceu algo que não foi você que subiu.

Se a sua resposta no passo 1 for "nunca toquei nesse pacote, nem uso PyTorch": escaneie mesmo assim. A propagação cross-ecosystem significa que um token npm comprometido de um colega pode chegar nos seus repositórios via uma dependência compartilhada.

Minha opinião

Não estou surpreso que tenha acontecido. Estou surpreso que demorou tanto.

O ecossistema de agentes de código com IA cresceu em uns 18 meses. Foi do "GitHub Copilot sugere uma linha" pro "Claude Code modifica seu repositório seguindo um arquivo JSON de config que você mal leu" sem muita pausa pra pensar no meio do caminho. Normalizamos a ideia de que abrir uma pasta no editor já é suficiente pra rodar código daquela pasta. As ferramentas têm UX ótima. O threat model… existe, mas mais na cabeça de quem escreve as ferramentas do que de quem as usa.

Shai-Hulud está mostrando a conta. A boa notícia é que o conserto é majoritariamente higiene. Trate o config nível-projeto do seu editor do mesmo jeito que você já trata scripts no package.json. Leia antes de abrir a pasta. Não confie em hook que você não escreveu. Desconfie de tudo que auto-executa.

A má notícia é que isso vai continuar acontecendo. A superfície de ataque é boa demais. Um único token npm roubado agora compromete todo pacote downstream e toda sessão de Claude Code que algum dia abrir um repositório infectado. É um worm com duas dentições.

Enfim — vai checar seu .claude/settings.json. Eu espero.

Fontes