Skip to main content

Entenda as vulnerabilidades de tomada do sistema de arquivos no gerenciador de pacotes JavaScript npm

Escrito por
prioritize vulns header

7 de janeiro de 2020

0 minutos de leitura

Em 11 de dezembro de 2019, uma vulnerabilidade de segurança que afeta os principais gerenciadores de pacotes JavaScript (npm, yarn e pnpm) foi divulgada publicamente. Descoberta pelo pesquisador de segurança Daniel Ruf, ela permite que agentes mal-intencionados usem diferentes técnicas para sobrescrever arquivos arbitrariamente.

Neste artigo:

Como funcionam os pacotes de linha de comando do Node.js?

Os pacotes npm costumam especificar uma chave bin no arquivo de manifesto do pacote — package.json. O cliente do gerenciador de pacotes — por exemplo, npm ou outras alternativas — usa essa chave bin para declarar executáveis disponíveis no prompt de comando por meio da variável de ambiente global $PATH.

Você provavelmente já viu esse recurso ao instalar pacotes de linha de comando globalmente — como o Angular CLI por meio de @angular/cli, typescript ou o Snyk CLI por meio de snyk —. Depois da instalação, eles ficam disponíveis em qualquer lugar do seu PATH, independentemente do diretório de um projeto npm específico. Para entender como a chave bin é usada, vamos usar o unpkg para ver como funciona o projeto Angular CLI, acessando o package.json no site do unpkg.

O arquivo package.json do pacote @angular/cli destaca a chave bin, que associa o comando ng a ./bin/ng.
Campo package.json do ng-cli mostrando o uso da chave JSON bin para definir um executável quando o npm instala este pacote (fonte: unpkg https://unpkg.com/browse/@angular/cli)

Como você pode ver acima, nesta versão do pacote, o arquivo de manifesto package.json declara um arquivo executável chamado ng, que aponta para um arquivo incluído no pacote e disponível em ./bin/ng.

Esse arquivo JavaScript ./bin/ng é tornado executável para o usuário pela adição de uma instrução Unix Shebang no início do arquivo. Essa instrução informa ao shell qual interpretador usar para executar o arquivo — em aplicações baseadas em Node.js, o binário do Node.js é instalado no sistema operacional do usuário.

No caso do Angular CLI, o conteúdo do arquivo executável aparece na imagem a seguir:

Código-fonte do executável ng da Angular CLI mostrando uma verificação da versão do Node.js e uma mensagem de erro para versões não compatíveis.
(fonte: https://unpkg.com/browse/@angular/cli@6.0.4/package.json)

Como essa vulnerabilidade de segurança afeta o cliente do gerenciador de pacotes npm?

Para entender as implicações dessa vulnerabilidade, precisamos nos aprofundar um pouco no npm, em especial nos mecanismos que controlam a disponibilidade dos executáveis instalados para o usuário.

O cliente npm gerencia os executáveis (“bins”) declarados pelos pacotes instalados globalmente em um diretório específico. Nele, mantém links simbólicos entre os executáveis desses pacotes e os links no próprio diretório.

Para verificar qual diretório é usado para armazenar esses executáveis, usamos o cliente npm da seguinte forma:

$ npm bin --global
/Users/lirantal/.nvm/versions/node/v10.16.3/bin

Ao inspecionar a variável de ambiente $PATH disponível no seu shell, você encontra uma entrada que corresponde ao diretório do seu sistema operacional. Assim, é possível acessar, de qualquer diretório do sistema operacional, pacotes instalados globalmente que declaram bins.

Com essas informações, é natural se perguntar se um pacote poderia declarar um arquivo executável bin fornecido por outro pacote. Foi exatamente isso que Daniel quis entender e que o levou a descobrir vulnerabilidades de sobrescrita arbitrária de arquivos nos principais gerenciadores de pacotes: npm, yarn e pnpm.

Como essa vulnerabilidade de segurança do npm ocorre na prática?

Na Snyk, reproduzimos essa vulnerabilidade de gravação arbitrária de arquivos e demonstramos como a exploração ocorre, com base nas provas de conceito de Daniel, compartilhadas com a equipe de segurança da Snyk.

O primeiro pacote define um conjunto global de executáveis da seguinte forma:

{
  "name": "binary-replacer",
  "version": "1.0.0",
  "main": "index.js",
  "author": "Daniel Ruf <mac1@daniel-ruf.de>",
  "license": "MIT",
  "bin": {
    "curl-test": "./bin/index.js",
  }
}

O executável curl-test simplesmente imprime um texto no console:

#!/usr/bin/env node
console.log('hi')

Se instalarmos esse pacote globalmente com npm install -g binary-replace e depois executarmos o comando curl-test no shell, ele deverá exibir hi na tela.

Agora, imagine que temos outro pacote, binary-replacer-2, que também define um executável, com a mesma chave bin do pacote anterior: curl-test

{
  "name": "binary-replacer-2",
  "version": "1.0.0",
  "main": "index.js",
  "author": "Daniel Ruf <mac1@daniel-ruf.de>",
  "license": "MIT",
  "bin": {
    "curl-test": "./bin/index.js",
  }
}

Quando o usuário instala esse pacote globalmente, o npm substitui o link simbólico anterior pelo executável fornecido pelo novo pacote. Neste exemplo, ele é inofensivo:

#!/usr/bin/env node
console.log('hi 2')

Mas e se um pacote malicioso tivesse como alvo alguns dos pacotes globais mais conhecidos? As possibilidades são muitas — desde o pacote now da Zeit até o CLI fkill de Sindre Sorhous.

Além disso, Daniel detalhou várias formas de explorar essa vulnerabilidade no npm, no yarn e no pnpm. Por exemplo, e se, em vez de usar curl-test como chave, substituíssemos esse valor por um caminho relativo?

 "bin": {
    "../../aaa": "/bin/date"
  }

Ou até mesmo definíssemos a chave como o caminho absoluto de uma ferramenta de linha de comando conhecida, usada com frequência pelo usuário em um prompt do shell, como:

"bin": {
  "/usr/bin/date": "./fake-date"
}

É admirável que o pnpm, mantido principalmente pelo desenvolvedor Zoltan Kochan, na maioria dos casos tenha o comportamento esperado e só seja vulnerável quando um caminho relativo é fornecido. Ainda assim, isso é um problema. Também há uma opção de atualização para versões corrigidas do npm.

Por que essa vulnerabilidade de segurança do npm é tão grave?

A gravidade dessa vulnerabilidade aumenta porque a sobrescrita arbitrária de arquivos também ocorre com pacotes instalados usando a opção de linha de comando do npm —ignore-scripts, que, em teoria, impediria que binários maliciosos executassem essa mesma ação.

Além disso, embora tenhamos falado sobre pacotes instalados globalmente e seus executáveis, o problema também se aplica às dependências transitivas do seu projeto. Quando as dependências são instaladas com um npm install comum, um pacote pode sobrescrever arquivos arbitrariamente dentro e fora do diretório do projeto, o que às vezes resulta em injeção de malware, ataques que alteram arquivos de lock ou outras formas de comprometimento do sistema de arquivos.

Você está em risco? O que deve fazer? 

Você corre risco em dois casos:

  1. Você instala um pacote malicioso, seja por manipulação social ou porque um invasor registra um pacote com nome parecido com o de um pacote popular (typosquatting) que você acaba instalando.

  2. Um pacote legítimo é comprometido de forma que as chaves bin do package.json são modificadas para sobrescrever arquivos e incluir um arquivo malicioso a ser executado. É muito improvável, mas já aconteceu duas vezes: [1], [2]

O impacto dessa vulnerabilidade também levou a lançamentos de segurança do Node.js, pois o cliente do gerenciador de pacotes npm vem incluído no próprio Node.js e foi necessário atualizar o npm para a versão corrigida 6.13.4.

Daniel comunicou primeiro as descobertas da vulnerabilidade à equipe de pesquisa de segurança da Snyk para ajudar a coordenar a divulgação pública responsável com todos os projetos e mantenedores dos gerenciadores de pacotes envolvidos. Ele também publicou mais informações sobre os tipos de ataque em seu blog.

Se você usa alguma das versões a seguir do npm, yarn ou pnpm, está vulnerável e precisa tomar medidas imediatas para atualizar:

Não é comum incluir npm, yarn ou pnpm como dependência de um projeto. Ainda assim, se a Snyk monitora seu projeto e encontra versões vulneráveis de algum deles, você recebe alertas automáticos da Snyk. Além disso, se você conectar seu repositório do GitHub (ou do Bitbucket), criamos automaticamente um pull request para atualizar esses gerenciadores de pacotes para versões corrigidas.

Também recomendamos seguir práticas seguras de desenvolvimento ao trabalhar com gerenciadores de pacotes como o npm. Para facilitar, compilamos convenções e boas práticas de segurança em 10 práticas recomendadas de segurança para npm.

Acha que encontrou uma vulnerabilidade em um pacote de código aberto? Na Snyk, valorizamos a comunidade de segurança e acreditamos que a divulgação responsável de vulnerabilidades em pacotes de código aberto ajuda a proteger a segurança e a privacidade dos usuários. Se você acha que encontrou uma vulnerabilidade em um pacote de código aberto, envie sua descoberta ao programa Snyk Open Source Package Vulnerability Disclosure, e cuidaremos do resto! https://snyk.io/vulnerability-disclosure/

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.

Leia mais

AI Background min crop
Blog

A IA está acionando todos os riscos que você aceitou: sete insights do painel de segurança de IA sem filtros

A IA está tornando os ataques mais baratos e rápidos, expondo riscos que as organizações já carregam. Quatro líderes de segurança explicam por que a defesa na velocidade das máquinas e a correção mais rápida são essenciais.

blog feature ai violet
Blog

VulnBump: agentes de IA conseguem corrigir dependências vulneráveis sem quebrar o build?

O VulnBump testa agentes de IA em 307 atualizações reais de dependências. Uma correção só é considerada válida se eliminar a vulnerabilidade e mantiver o build e os testes passando.

blog feature snyk container custom base recommendations
Blog

Conheça o Evo Agentic AppSec: segurança que acompanha o ritmo do desenvolvimento com IA

O desenvolvimento impulsionado por IA está ultrapassando os fluxos de trabalho tradicionais de segurança. O Agentic AppSec da Snyk usa agentes de segurança para encontrar, corrigir e verificar vulnerabilidades continuamente, enquanto as pessoas definem os critérios.