O verme virou código aberto
Em algum momento da semana de 11 de maio de 2026, as pessoas por trás do Shai-Hulud — o verme autorreplicante de cadeia de suprimentos do npm que vem devorando contas de mantenedores desde setembro de 2025 — vazaram o próprio código-fonte. No fim de semana, a OX Security havia encontrado quatro pacotes npm de typosquat sob uma única conta, sendo um deles uma cópia quase literal do verme vazado, outro um bot de DDoS em Golang, e os outros dois infostealers simples enviando chaves SSH e carteiras de cripto para C2s de quinta categoria. O piso para bifurcar ataques de cadeia de suprimentos acaba de cair muito, e as pessoas com maior probabilidade de instalar um desses pacotes já não são humanas.
Em algum momento da semana passada, as pessoas por trás do Shai-Hulud — o verme do npm
que vem mastigando contas de mantenedores desde setembro
de 2025 — vazaram o próprio código-fonte. No fim de semana, uma conta npm
chamada deadcode09284814 havia publicado quatro typosquats
reaproveitando esse código. Um era o verme, quase literalmente.
Outro era um bot de DDoS em Golang. Dois eram infostealers
simples que fazem POST das suas chaves SSH,
do seu ~/.aws/credentials e do seu cofre do MetaMask para
um IP alugado. Duas mil seiscentas e setenta e oito instalações
depois, a pergunta deixou de ser "alguém vai transformar
o vazamento em arma". A pergunta é qual dos agentes digitando
npm install no seu terminal nesta tarde vai apanhar um deles.
Há uma coisa que acontece, periodicamente, em software ofensivo,
em que uma ferramenta fechada é despejada em um fórum e a população de
pessoas capazes de executá-la salta de "a única equipe que a escreveu" para "qualquer
adolescente com uma VPS". Mimikatz. EternalBlue. O código-fonte do Conti. A cada
vez, a capacidade não mudou — ela apenas deixou de ser escassa.
A manchete da semana passada, divulgada pelo
BleepingComputer
com base em pesquisa da
OX Security,
é que o Shai-Hulud — o verme sobre o qual
escrevemos quando ele devorou quarenta e dois
pacotes @tanstack em seis minutos — entrou para essa lista.
A bifurcação não é teórica. Até domingo, a OX já havia documentado quatro
pacotes maliciosos publicados a partir de uma única conta npm chamada
deadcode09284814:
chalk-tempalte— um typosquat dechalk-template, carregando uma cópia quase literal do código-fonte vazado do Shai-Hulud, incluindo o comportamento assinatura do Shai-Hulud de fazer upload de credenciais roubadas como repositórios GitHub públicos gerados automaticamente. A leitura da OX: "o código do malware Shai-Hulud é uma cópia quase exata do código-fonte vazado, sem técnicas de ofuscação", sugerindo um novo ator de ameaça que nem sequer se deu ao trabalho de lixar os números de série.axois-utils— um typosquat deaxios-utils, entregando um payload em Golang que a OX chama de Phantom Bot: floods HTTP, TCP, UDP e reset, persistência via pasta Startup do Windows e tarefas agendadas, e um C2 emb94b6bcfa27554.lhr.life. Sua máquina de dev, alistada.@deadcode09284814/axios-util— um typosquat diferente, um payload diferente: chaves SSH, variáveis de ambiente, credenciais de AWS/GCP/Azure, enviadas para80.200.28.28:2222. "Bem direto ao ponto", nas palavras da OX.color-style-utils— um infostealer puro que captura seu IP, geolocalização e dados de carteira de cripto, e faz POST paraedcf8b03c84634.lhr.life.
Downloads semanais combinados no momento do relatório da OX: 2.678.
Há duas histórias entrelaçadas aqui que merecem ser
desembaraçadas. A entediante é que o npm tem typosquats. Tem;
sempre teve; sempre terá, pelo mesmo motivo pelo qual existem cachorros
chamados Bench no parquinho — nomes são baratos e o namespace é
plano. A interessante é que a barreira para rodar um
verme da classe Shai-Hulud acabou de cair. Até a semana passada, era preciso
ter a ferramenta da equipe original, a infraestrutura da equipe original,
a disciplina da equipe original para não ser pego. Hoje,
basta ter um clone GitHub de um repositório público, um túnel lhr.life e
a paciência para digitar npm publish. Os quatro pacotes que a OX encontrou são,
coletivamente, a prova.
Por que a contagem de atores importa mais que a contagem de pacotes.
Um único verme sofisticado é, em certo sentido, um adversário
tratável. Ele tem sinais. Tem infraestrutura. Tem hábitos.
Assinaturas de detecção podem ser escritas contra ele. Aikido, Socket,
Snyk, Wiz — as empresas de monitoramento de cadeia de suprimentos npm que pularam
em cima do incidente @tanstack de 11 de maio — pegaram-no em questão de horas,
especificamente porque vinham observando a mesma família havia
oito meses.
Uma família de vermes derivados escritos por pessoas que baixaram
o código-fonte de um site de paste é uma forma diferente. Cada um vai
exfiltrar para um C2 diferente, embutir uma chave RSA diferente, escolher uma
combinação diferente de arquivos para ler e escolher um
espaço de typosquat diferente para morar. Alguns serão cuidadosos; a maioria será
desleixada de um jeito que os pega rapidamente; um deles, na
próxima vez que escrevermos um post como este, será sofisticado
o suficiente para que não consigamos pegá-lo em uma semana. O problema de detecção
dos defensores se amplia de "encontrar Shai-Hulud" para "encontrar qualquer coisa
que queira ler ~/.ssh/id_ed25519 de dentro de um script prepare".
Essa é uma superfície muito, muito maior.
A forma do caminho de instalação também mudou, e essa é a
parte que deveria preocupar quem está rodando um agente de codificação. Um desenvolvedor
humano que quisesse chalk-template teria, em 2024, lido o nome
do pacote em um tutorial, digitado, e percebido o
erro quando chalk-tempalte aparecesse com duzentos downloads
e um estranho como publicador. Um
agente de codificação
em 2026, ao receber a tarefa de "adicionar cor à saída do meu CLI", vai instalar
o que quer que o gerenciador de pacotes entregar de volta. O agente não vê
o campo do publicador. O agente não percebe que o README
tem três linhas. O agente está rodando trinta npm installs
nesta hora porque o usuário está fazendo o trabalho de uma equipe pequena e
o agente é pago por tarefa, não por instalação.
O que um clone não-ofuscado do Shai-Hulud realmente faz.
O pacote chalk-tempalte é, escreve a OX, "quase sem qualquer
alteração" em relação ao código-fonte vazado. Isso significa que as mesmas
mecânicas que percorremos em
O verme que se escreve dentro de .claude
se aplicam, com uma nova ruga significativa: o canal de exfiltração
é o próprio GitHub.
O truque assinatura do Shai-Hulud — preservado pelo imitador porque
copiar é mais fácil que reescrever — é que as credenciais roubadas
não vão para um servidor de despejo escondido. Vão para um repositório
GitHub público recém-criado, publicado usando um token GitHub que
o malware acabou de roubar da vítima. Os segredos da vítima ficam num
repositório público, pertencente à própria conta GitHub da vítima, para qualquer pessoa
no mundo varrer, até que alguém perceba e o repositório seja
detonado. O playbook padrão do defensor — bloquear o domínio de exfiltração,
procurar DNS de saída incomum — não pega isso, porque o
DNS de saída é api.github.com, com o qual a máquina do seu desenvolvedor
fala duzentas vezes por hora de qualquer jeito. O pacote de credenciais
deixa o seu laptop disfarçado de git push.
Uma vez que os segredos estão públicos, qualquer pessoa que monitore o firehose do GitHub — e várias pessoas monitoram o firehose do GitHub exatamente por essa razão — pode pegá-los. O verme não precisa manter seu C2 vivo. O GitHub é o C2.
Há um detalhezinho naquela figura que merece atenção
demorada. O clone do Shai-Hulud, chalk-tempalte, nem sequer depende da
própria infraestrutura para receber o butim. Ele usa a identidade GitHub
da própria vítima para publicar os próprios segredos roubados da vítima
em um repositório público no GitHub. O C2 em lhr.life é
backup. O canal principal é o git push. Para um defensor observando
a saída, isso é indistinguível do CI normal do desenvolvedor.
Para o GitHub, é — até que alguém sinalize o repositório — um projeto público
legítimo, propriedade de um usuário real. A exfiltração é lavada através
da identidade da vítima.
O que o agente está fazendo enquanto você rola a tela.
Se você tem um agente de codificação no seu terminal — Claude Code, CLI do Cursor,
Codex CLI, Aider, escolha o seu — há uma chance não-nula de que,
no tempo que você levou para ler o último parágrafo, o agente
tenha rodado um npm install por você. Talvez dois. Agentes de codificação não
fazem pausa para admirar árvores de dependências. A razão inteira pela qual você comprou
um é porque ele não faz pausa.
Os pacotes que a OX pegou são typosquats, que é uma categoria de
erro muito especificamente adequada à velocidade de máquina. Um humano que
tem que digitar chalk-template vai colocar as letras na ordem certa
porque já fez isso cem vezes. Um modelo que
ingeriu cada post do Stack Overflow do planeta viu
chalk-template e chalk-tempalte no mesmo corpus de treinamento —
o último tipicamente dentro de uma captura de tela do erro de outra pessoa —
e, dado um prompt como "adicione saída colorida ao meu CLI", às vezes
vai emitir o erro de digitação ipsis litteris. O agente não pestaneja. O
gerenciador de pacotes não pestaneja. O script prepare roda.
Esse não é um modo de falha hipotético. É o modo de falha para o qual a família Shai-Hulud foi projetada. O verme original espalhou-se roubando tokens de mantenedores e usando-os para republicar mais versões comprometidas de pacotes legitimamente populares. Os imitadores ainda não têm os tokens dos mantenedores; o que eles têm é o namespace dos typosquats, no qual agentes são unicamente bons em cair.
Onde o Bromure se encaixa nessa história.
Bromure Agentic Coding é a configuração na
qual o agente de codificação roda dentro de uma VM Linux descartável por tarefa,
com a pasta do projeto montada, a saída intermediada e
as credenciais mantidas do lado do host macOS do hipervisor.
Percorremos a arquitetura em detalhes no
texto sobre o Bitwarden CLI
e no
texto sobre @tanstack. O
que segue é o que especificamente acontece com cada um desses quatro
pacotes dentro dessa fronteira.
Passe isso pelos quatro pacotes, um de cada vez, porque os detalhes específicos são onde a arquitetura justifica seu pão.
chalk-tempalte tenta pegar $GH_TOKEN.
O movimento assinatura do clone do Shai-Hulud é pegar o token GitHub
do desenvolvedor e usá-lo para criar um repositório público na
conta do próprio desenvolvedor. Dentro de uma VM Bromure, o
$GH_TOKEN que ele lê é um stub — uma string sintaticamente válida que
começa com ghp_ e existe exatamente por essa razão. A
primeira ação do executor é POST /user/repos contra
api.github.com. O proxy de saída do lado do host reconhece
api.github.com como um endpoint da lista de permissões, mas apenas para as
operações que a tarefa atual de fato pediu — git push para o
repositório em que a tarefa está trabalhando, gh pr create contra esse mesmo
repositório, gh api repos/that/repo/issues. "Criar um novo repositório
público na conta do usuário" não está nessa lista, porque
o usuário não pediu isso. O proxy se recusa a substituir o
token real, e o stub sai como stub. O GitHub retorna 401.
O canal de exfiltração do verme — o canal engenhoso, aquele projetado
para burlar a filtragem de saída DNS — nunca abre.
O canal de backup, o túnel lhr.life em
87e0bbc636999b.lhr.life, também não está na lista de permissões. O trace
registra a tentativa. Os bytes não saem.
axois-utils instala o Phantom Bot para persistência.
O bot Golang tenta escrever a si mesmo na pasta Startup do
Windows e criar uma tarefa agendada. A VM Bromure é um convidado Linux,
então a persistência específica do Windows é, de graça, um no-op.
Em uma variante Linux do mesmo payload — que alguém vai lançar
em breve — o bot escreveria a si mesmo em /etc/cron.d/ ou
~/.config/systemd/user/. Ambos esses caminhos ficam dentro do disco
descartável copy-on-write do convidado. O próximo bromure reset,
ou o fim natural da tarefa atual, descarta o disco. A
persistência some sem qualquer caça.
Enquanto isso, a conexão de saída do bot para
b94b6bcfa27554.lhr.life não está na lista de permissões de saída da tarefa,
porque nenhuma tarefa legítima de codificação fala com um túnel
lhr.life recém-registrado. O bot liga para casa num soquete fechado. O
trace da sessão registra a tentativa — útil amanhã de manhã quando uma
lista de IOC for publicada.
@deadcode09284814/axios-util faz POST de credenciais brutas.
O mais simples dos quatro payloads é também o que tem menos
para pegar. O executor lê o ~/.ssh, ~/.aws do convidado, variáveis
de ambiente, e faz POST para 80.200.28.28:2222. O diretório SSH está
vazio. O arquivo AWS é um stub. As variáveis de ambiente são
ou stubs ou inexistentes. O IP de destino não está na lista de permissões.
Ou a conexão é bloqueada no proxy, ou ela deixa o
host carregando um payload de placeholders. Qualquer resultado serve.
color-style-utils procura carteiras que não estão lá.
O ladrão de cripto é o pacote cujo modelo de ameaça mais claramente
assume que o próprio navegador do desenvolvedor está na mesma máquina. Ele
lê caminhos como
~/Library/Application Support/Google/Chrome/Default/Local Extension Settings/<MetaMask-id>/
e os equivalentes para Phantom e Keplr. Nenhum desses caminhos
existe na VM Bromure. A VM não tem o seu perfil do Chrome
dentro dela. A VM não tem uma extensão de carteira instalada. A VM
é, por design, o navegador principal de ninguém. O executor encontra um
diretório vazio e segue em frente.
Essa é a parte que não é uma história sobre credenciais residentes no host. As carteiras vivem no host porque, em um laptop normal, o navegador do desenvolvedor e o agente de codificação do desenvolvedor compartilham um filesystem. O Bromure não torna a carteira mais forte; ele a torna inalcançável a partir do lugar em que o verme está rodando. O verme não consegue ler o que não está no disco dele.
O que ainda dói.
Há cantos dessa história onde a VM por tarefa do Bromure não é uma solução, e eles merecem ser nomeados em voz alta.
A pasta do projeto está montada.
Arquivos que o verme escreve na pasta do projeto — incluindo a
persistência tipo .claude/router_runtime.js que cobrimos no
post sobre @tanstack — são duráveis entre resets de tarefas, porque essa
é a razão de existir da montagem da pasta do projeto. A defesa
aí não é a VM. É git status e uma olhada de cinco segundos
no diff antes de você dar push. O trace facilita identificar
quais sessões adicionaram arquivos inesperados.
A lista de permissões de saída é estreita de propósito.
O corretor de credenciais do Bromure funciona porque a lista de permissões
é estreita. Se você permite npm publish para o seu próprio escopo
porque está publicando um release hoje, e por acaso
instala um desses quatro pacotes hoje, o verme vai
publicar sob o seu escopo. Permita o que a tarefa precisa. Nem
um byte a mais.
Um token bom para esta tarefa ainda é um token real.
O token GitHub stub é trocado por um real no fio para
operações que a tarefa permitiu. Se chalk-tempalte conseguisse
convencer o agente a fazer um git push para o próprio
repositório do projeto, esse push iria com um token real. A fronteira
protege as credenciais. Ela não revisa o diff. Leia
o diff.
Detecção é a jusante do trace.
O trace da sessão registra cada comando shell, escrita de arquivo e
requisição de saída. Por conta própria, ele não classifica
87e0bbc636999b.lhr.life como ruim. Registra que a requisição
foi feita. Quando a OX publicar uma lista de IOC fresca amanhã de manhã,
sua busca leva dois segundos. Esse é o valor que o trace adiciona —
não mágica, apenas recibos.
Uma última coisa.
O vazamento não é a notícia. A notícia é o que o vazamento torna provável
ao longo do próximo ano, que são muitos forks pequenos e meio competentes
de um verme que, mesmo na sua forma competente, o ecossistema
npm mal conseguiu pegar a tempo. Alguns dos forks serão barulhentos
o bastante para virar matéria. A maioria vai ficar no registry por uma
semana, juntar alguns milhares de npm installs, e sumir
quando alguém finalmente registrar um abuso. As duas mil
seiscentas e setenta e oito instalações que a OX cronometrou nos
pacotes deadcode09284814 não são um caso fora da curva. São a
média.
A pergunta honesta não é "minha equipe vai evitar cada typosquat envenenado no npm". Os agentes digitam rápido. Os nomes são baratos. A pergunta honesta é: quando o agente instalar um — e ao longo do próximo ano, em uma equipe que usa agentes, ele vai — o script pós-instalação encontra as mãos do desenvolvedor, ou encontra uma caixa Linux descartável com stubs nos arquivos de credenciais e um proxy que não sabe quem ele é.
Bromure Agentic Coding é o segundo. É gratuito, código aberto e entregue hoje. A árvore de forks vai piorar antes de melhorar.