Notícias

15 jul, 2026

AsyncAPI comprometido: quatro pacotes npm que carregavam a botnet

Publicidade

AsyncAPI virou endereço de um ataque à cadeia de suprimentos de software. A Socket identificou pacotes do namespace @asyncapi publicados com um implante ofuscado. Além disso, esse implante baixava um carregador da botnet Miasma. Portanto, qualquer código Node.js que importasse os módulos executava o payload na hora.

AsyncAPI no alvo: o que foi publicado em 14 de julho

A equipe de pesquisa de ameaças da Socket encontrou versões envenenadas dentro do namespace oficial. São elas:

  • @asyncapi/generator-helpers@1.1.1
  • @asyncapi/generator-components@0.7.1
  • @asyncapi/generator@3.3.1
  • @asyncapi/specs@6.11.2 e a versão alpha correspondente

O scanner de IA da Socket classificou o generator-helpers como malware confirmado. Já as versões imediatamente anteriores estão limpas. Ou seja, a janela de contaminação é estreita e bem delimitada.

Os uploads aconteceram em duas ondas. Primeiro, os três pacotes do generator subiram com segundos de diferença, por volta das 07:10 UTC. Depois, uma hora mais tarde, saíram os dois lançamentos do @asyncapi/specs.

O truque sujo: malware no código fonte, não no script de instalação

Aqui mora a parte interessante para quem defende pipelines. O código malicioso não vive em hooks de ciclo de vida do pacote. Em vez disso, ele foi costurado dentro de arquivos de código fonte legítimos.

Por causa disso, bloqueadores de script de instalação não protegem nada. Assim que um require alcança o módulo infectado, o implante acorda durante o carregamento. Logo, máquina de desenvolvedor, runner de CI, suíte de testes e caminho de aplicação viram ponto de execução.

Na sequência, o módulo dispara um processo em segundo plano com node -e. Esse processo então busca a etapa seguinte.

Publicação confiável virou confiança cega

Os metadados do npm atribuem cada publish à identidade do GitHub Actions usada para trusted publishing. Contudo, é justamente aí que a coisa incomoda.

Os três pacotes do generator compartilham metadados idênticos. Mesma versão de Node.js, mesma versão de npm, mesmo commit de origem, mesmo repositório, mesmo branch next e mesmo identificador de execução. Enquanto isso, chamadas públicas à API do GitHub para o workflow e para os logs retornaram 404 durante a análise. Ainda assim, a proveniência confirma origem em um runner hospedado pelo GitHub.

Os arquivos de origem já estavam envenenados em um commit público. Contudo o workflow apenas construiu e publicou o que estava lá. Aliás, o commit lista autor e committer como “Your Name”, com email placeholder e associação inválida no GitHub.

A lição é direta. Publicação confiável prova de onde o artefato veio. Porém, ela não prova que o conteúdo é confiável.

Três estágios até o payload

A cadeia de infecção se desdobra em camadas altamente ofuscadas.

Estágio 1: o implante inicial

Trata se de uma única instrução ofuscada, jogada no código do pacote. Após a desofuscação, aparecem strings de um spawn de processo filho. As flags escolhidas são detached, unref, ignore e windowsHide. Dessa forma, o processo se mantém discreto. O filho recebe o downloader do segundo estágio como argumento de node -e.

Estágio 2: o downloader

A URL aponta para um gateway IPFS que serve um arquivo chamado sync.js. Além do mais, os diretórios de destino imitam locais comuns de runtime:

  • Windows: LocalAppData\NodeJS
  • macOS: Application Support/NodeJS
  • Linux: .local/share/NodeJS ou .config/node

Depois da gravação, outro processo Node destacado inicia o arquivo de 8.25 MB.

Estágio 3: decriptação do Miasma

O sync.js atua como wrapper de decriptação. Ele usa HKDF com SHA256, AES de 256 bits no modo GCM e uma etapa de rotação de caracteres. Em seguida, avalia 3.09 MB de JavaScript.

Dentro da Miasma: um framework de tarefas com C2 próprio

O payload decriptado se apresenta como framework de tasking da família Miasma. Inclusive, ele traz configurações reveladoras. Com isso o target se chama miasma-train-p1, os diretórios de estado ficam em .config/.miasma e o serviço responde como miasma-monitor. Para se esconder, os caminhos de camuflagem reaproveitam pastas mesa_shader_cache.

A configuração marca o npm como ecossistema alvo ativo. Por outro lado, os módulos de propagação para PyPI, Ruby e Cargo estão desativados por enquanto.

A infraestrutura de comando e controle gira em torno de um único IPv4. Ele expõe endpoints REST nas portas 8080, 8081 e 8091. Ainda existem módulos dormentes para relays Nostr, IPFS, consultas RPC Ethereum, libp2p, nós DHT do BitTorrent e descoberta mDNS local.

Os testes dinâmicos confirmaram handlers funcionais. Listagem, leitura e escrita de arquivos, execução de shell, atualização de payload e ajuste de beacon respondem normalmente. Além disso, a blacklist de shell é rasa e barra apenas o killall. No Linux, por fim, a persistência vem de uma unidade de usuário do systemd que sobe o processo como miasma-monitor.

Você foi exposto? O critério é um só

Primeiro, respire. Ter o pacote afetado em um lockfile não compromete o host automaticamente.

A exposição exige algo específico. Aplicação, código de teste, CLI ou código de build precisa ter importado o arquivo de origem infectado. Entretanto, se você não consegue descartar isso, trate o host como exposto.

Limpeza real: apagar node_modules não resolve

Remover a pasta não reverte nada. Afinal, o segundo e o terceiro estágios deixam rastros próprios:

  • Um processo Node em segundo plano
  • O arquivo sync.js instalado
  • Um lockfile .miasma
  • Arquivos de cache camuflados
  • Dados residuais da cadeia de spawn
  • Um serviço de usuário do systemd ativo

Checklist para times que dependem de AsyncAPI

Comece pela árvore de dependências. Audite as versões listadas e fixe versões limpas. Assim que houver patch oficial, aplique.

Depois, vá aos logs de auditoria da organização no GitHub. Procure pushes que resultem no commit 3eab3ec9304aa26081358330491d3cfeb55cc245 no branch next.

Por fim, trate runners de CI que importaram os pacotes como caso forense. Os indicadores publicados pela Socket incluem tráfego para 85.137.53.71, tráfego RPC inesperado de IPFS, Nostr, DHT ou Ethereum saindo de agentes de build e unidades systemd de usuário com nomes suspeitos.

AsyncAPI e o que sobra para o ecossistema npm

Automação encontra indicadores. Contudo, ela não decide nada por você. Limites de confiança, rotação de credenciais e estratégia de remediação seguem nas mãos dos times de segurança e engenharia.

O recado é incômodo justamente por ser simples. Um commit com email placeholder atravessou um workflow legítimo e virou release assinada pela reputação do projeto. Portanto, a pergunta que vale para o seu pipeline hoje é outra.

Acompanhe nosso perfil no Instagram!