Na madrugada de ontem, duas versões do Aaxios chegaram ao npm carregando um trojan de acesso remoto multiplataforma. As versões afetadas ficaram no ar por aproximadamente três horas antes de serem removidas pelo npm.
Tempo suficiente, dado o volume de downloads, para infectar um número potencialmente grande de máquinas de desenvolvedores e pipelines de CI/CD. O TecMundo entrou em contato com a npm mas não obteve resposta até a publicação da reportagem.
O axios é uma biblioteca JavaScript usada para fazer requisições HTTP. Basicamente, é o que permite que aplicações web e mobile se comuniquem com servidores e APIs. Com cerca de 100 milhões de downloads semanais, é uma das dependências mais onipresentes de todo o ecossistema JavaScript.
O vetor de entrada
O ponto de partida foi o comprometimento da conta npm de jasonsaayman, o mantenedor principal do projeto. Os atacantes trocaram o e-mail registrado da conta por ifstap@proton.me, um endereço ProtonMail sob controle deles.

Com isso, obtiveram acesso a um token de acesso npm de longa duração vinculado à conta. Foi com esse token que publicaram os pacotes maliciosos diretamente via linha de comando, completamente fora do pipeline oficial do projeto.
Esse detalhe revela uma falha de processo. O axios usa o GitHub Actions com o mecanismo OIDC Trusted Publisher do npm para publicar releases legítimas. Esse mecanismo gera um token efêmero e criptograficamente vinculado a um workflow específico do GitHub.
Ele não pode ser roubado porque expira em minutos, mas aparentemente a conta ainda tinha um token clássico de longa duração ativo em paralelo. E foi esse token que os atacantes usaram.

A prova está nos metadados do próprio npm. Toda release legítima do axios 1.x tem um campo trustedPublisher com ID de workflow do GitHub. A versão maliciosa 1.14.1 não tem nada disso, foi publicada manualmente por jasonsaayman com e-mail ifstap@proton.me.
Além disso, não existe nenhum commit ou tag correspondente no repositório GitHub do axios. O pacote existe só no npm.
2 branches comprometidos em 39 minutos
Às 00h21 (UTC), axios@1.14.1 chegou ao npm sob a tag latest, que é a versão que qualquer instalação sem número específico vai buscar por padrão. Às 01h00 (UTC), axios@0.30.4 seguiu sob a tag legacy, cobrindo quem ainda usa a linha mais antiga da biblioteca.

Os dois branches ativos do projeto foram envenenados em menos de 40 minutos, maximizando o número de projetos expostos. As versões maliciosas ficaram no ar por aproximadamente 2h53min e 2h15min antes de o npm removê-las.
Zero linhas de código malicioso no axios
Os atacantes não modificaram o código-fonte do axios. Uma comparação entre 1.14.0, a versão limpa, e 1.14.1, a maliciosa, mostra que exatamente um arquivo mudou. Todos os outros 85 arquivos da biblioteca são bit a bit idênticos entre as duas versões.
O arquivo alterado foi o package.json, que é o manifesto de um pacote npm. É nele que ficam registradas as informações do projeto e a lista de dependências que ele precisa para funcionar. A única mudança funcional foi a adição de plain-crypto-js@^4.2.1 nessa lista.

Uma varredura em todos os arquivos do axios confirma que essa dependência não é usada em nenhum lugar do código da biblioteca. Ela não tem função nenhuma no axios.
Está ali exclusivamente para acionar o mecanismo de infecção. Isso porque quando o npm instala um pacote, ele também instala automaticamente tudo que está na lista de dependências desse pacote. Não é necessária nenhuma confirmação adicional do usuário.
O pacote isca e 18 horas de preparação
O plain-crypto-js foi criado pelo atacante sob a conta nrwise@proton.me. Para não levantar suspeitas, o pacote foi construído como uma cópia quase perfeita do crypto-js, uma biblioteca criptográfica legítima bastante usada.

Os 56 arquivos de código-fonte são idênticos aos do original, a descrição é a mesma. O link do repositório aponta para o projeto real, e o nome do autor verdadeiro foi usado de forma fraudulenta.
A versão 4.2.0 foi publicada 18 horas antes do ataque, sem nenhum payload malicioso. O objetivo era fazer a conta nrwise parecer um mantenedor legítimo com histórico de publicação. O intuito era evitar alertas automáticos que monitoram o npm em busca de pacotes recém-criados.
A versão armada, 4.2.1, chegou às 23h59 UTC do dia 30 com três adições. O package.json ganhou uma instrução para rodar automaticamente um script chamado setup.js durante a instalação do pacote.

O setup.js em si foi incluído com o código do dropper ofuscado. E um arquivo chamado package.md foi adicionado com um manifesto limpo pré-preparado, para ser usado na limpeza de evidências depois da execução.
O dropper detecta o sistema e age diferente
O setup.js esconde seu conteúdo real usando duas camadas de codificação, dificultando que ferramentas automáticas de análise identifiquem o que ele faz sem executá-lo. Quando roda, o script identifica o sistema operacional da máquina e executa um caminho diferente para cada plataforma.
No macOS, o script escreve um arquivo temporário e usa uma ferramenta nativa do sistema para executá-lo. Esse arquivo baixa o malware do servidor dos atacantes e o salva em uma pasta do sistema com um nome que imita processos legítimos do macOS, tornando-o difícil de identificar em uma inspeção visual da máquina.

No Windows, o script localiza o PowerShell, o ambiente de linha de comando da Microsoft, e cria uma cópia dele com um nome diferente em uma pasta do sistema. Essa cópia persiste mesmo que o pacote malicioso seja desinstalado depois.
Em seguida, um script auxiliar busca o payload do servidor dos atacantes e o executa em uma janela completamente oculta.
No Linux, e em qualquer sistema que não seja macOS nem Windows, o script usa o curl, uma ferramenta de linha de comando presente na maioria dos sistemas Unix, para baixar um script Python do servidor dos atacantes e executá-lo em background.

Em todos os casos, se qualquer etapa falhar por qualquer motivo, o erro é ignorado completamente e a instalação do npm termina com sucesso aparente. O desenvolvedor não vê nenhum aviso.
Malware apaga as próprias evidências
Após lançar o payload, o setup.js apaga a si mesmo, apaga o package.json malicioso e substitui esse arquivo pelo stub limpo que foi pré-preparado no package.md.
Esse stub reporta versão 4.2.0, não 4.2.1. Após a infecção, quem rodar o comando para listar os pacotes instalados vai ver plain-crypto-js@4.2.0. Isso porque o npm lê o número de versão do arquivo que está na pasta do pacote, e esse arquivo agora diz 4.2.0.

Um investigador buscando especificamente pela versão 4.2.1, que é a que consta nos alertas de segurança, veria um número diferente e poderia concluir que o sistema estava limpo.
O único indicador confiável que sobra é a existência da pasta node_modules/plain-crypto-js/ no projeto. Esse pacote nunca foi dependência de nenhuma versão legítima do axios.
C2 contatado em menos de dois segundos
A StepSecurity, empresa de segurança que detectou o ataque, instalou axios@1.14.1 em um ambiente instrumentado. O teste era capaz de registrar todas as conexões de rede, processos e escrita de arquivos durante a execução, para confirmar o comportamento real do malware.

O resultado foi que 1,1 segundo após o início da instalação, o dropper já estava fazendo uma conexão ao servidor dos atacantes em sfrclak.com, na porta 8000. A conexão foi estabelecida antes mesmo de o npm ter terminado de resolver todas as dependências do pacote.
Trinta e seis segundos depois, em uma etapa diferente do workflow, uma segunda conexão ao mesmo servidor foi registrada. Era o payload de segunda etapa já rodando de forma completamente independente, desvinculado de toda a árvore de processos do npm. O malware havia persistido além da etapa que o instalou.
Planejamento avançado sem atribuição pública
Até o momento desta publicação, nenhuma atribuição foi feita. O npm e autoridades policiais estão investigando.

O nível de planejamento documentado inclui staging de 18 horas para burlar detecção automática, infraestrutura pronta para três sistemas operacionais antes do disparo. Além de comprometimento simultâneo dos dois branches da biblioteca, mecanismo de autolimpeza com deception ativa de versão e persistência específica no Windows que sobrevive a desinstalações.
A StepSecurity classifica o ataque como um dos mais sofisticados já documentados contra um pacote top-10 do npm.
O que fazer se você instalou as versões afetadas
O primeiro passo é buscar por axios@1.14.1, axios@0.30.4 ou plain-crypto-js@4.2.1 nos arquivos de lockfile do projeto. Esses arquivos registram exatamente quais versões foram instaladas em cada momento e costumam ter nomes como package-lock.json, pnpm-lock.yaml ou yarn.lock.

Se qualquer uma dessas strings aparecer, o ambiente que instalou as dependências foi exposto.
Para sistemas potencialmente comprometidos, os arquivos a buscar são /Library/Caches/com.apple.act.mond no macOS, wt.exe dentro da pasta ProgramData no Windows e /tmp/ld.py no Linux.Conexões ou resoluções de DNS para sfrclak.com ou o IP 142.11.206.73 nos logs de rede desde 30 de março também são indicadores de comprometimento.
Se qualquer um desses artefatos for encontrado, a orientação dos pesquisadores é reconstruir a máquina a partir de um estado limpo e rotacionar imediatamente todas as credenciais acessíveis no momento da instalação.Isso inclui tokens npm, chaves de cloud, secrets de CI/CD, chaves SSH e qualquer valor presente em arquivos de configuração de ambiente.
Para quem não foi afetado, a mitigação imediata é fixar o axios em 1.14.0 ou 0.30.3 e adicionar o flag --ignore-scripts em pipelines de CI/CD, desativando a execução automática de scripts durante instalações.
Acompanhe o TecMundo nas redes sociais. Inscreva-se em nossa newsletter e canal do YouTube para receber mais notícias de tecnologia e segurança.