Skip to main content

Pacotes maliciosos de Node.js visivelmente invisíveis: quando configurações de nicho se encontram com caracteres invisíveis

Escrito por
Headshot of Aviad Hahami

Aviad Hahami

blog npmyarn feature

28 de fevereiro de 2022

0 minutos de leitura

Nos últimos anos, vimos um enorme aumento no número de pacotes de código aberto criados e usados na prática. Hoje, cada ecossistema tem seu gerenciador de pacotes, e quase todo gerenciador tem seus recursos e configurações pouco conhecidos.

À medida que desenvolvedores instalam continuamente um número cada vez maior de pacotes, os atacantes passam a se interessar pelas superfícies de ataque desses pacotes. Começa então a jornada para criar o pacote malicioso perfeitamente oculto.

Esta publicação aborda ataques que exploram o ecossistema Node.js e os gerenciadores de pacotes yarn e npm. Mas considere-a um “chamado à ação” para investigar problemas semelhantes em outros ecossistemas, aumentar a conscientização e fortalecer a segurança geral da nossa comunidade.

Dito isso, vamos começar!

Parte 1: Configurações pouco conhecidas

Outro dia, um colega pesquisador me falou sobre algumas opções de configuração interessantes do npm, pouco conhecidas e com sérias implicações de segurança (mais tarde, encontramos opções semelhantes no yarn). Elas permitem especificar ao gerenciador de pacotes qual binário deve ser executado dentro do diretório do projeto. Isso mesmo: qual binário!

Em termos de configuração, essas opções são yarnPath no arquivo .yarnrc e git, shell e script-shell no arquivo .npmrc (entre outras). Para entender melhor o vetor de ataque a seguir, recomendo muito a leitura do artigo de Rotem (da Cider Security) sobre essa descoberta.

Alguém pode perguntar: “por que esses arquivos que acabei de clonar/baixar seriam respeitados?”. A resposta é que é assim que o npm/yarn funciona. Como parte do fluxo de gerenciamento do projeto, o gerenciador procura arquivos .rc em uma hierarquia: primeiro no diretório do projeto atual, depois no diretório do usuário e, por fim, no diretório raiz (saiba mais na documentação de configuração do NPM).

Com as opções acima, podemos criar um pacote malicioso parcialmente oculto (em repositórios/arquivos tarball, já que arquivos `.rc` não são baixados ao executar `npm install` ou equivalente), usando o seguinte arquivo .yarnrc como configuração:

# .yarnrc content

yarnPath: “./evil.sh”

(Observação: este exemplo usa um arquivo .yarnrc, mas um arquivo .npmrc semelhante — com a opção de configuração apropriada definida — terá o mesmo resultado.)

Com isso, qualquer execução de yarn install na raiz do projeto acima fará com que evil.sh seja executado. Agora, só precisamos adicionar o arquivo malicioso.

Dois pontos importantes:

  1. Mesmo que você use --ignore-scripts ou uma instrução equivalente, o que foi descrito acima ainda acontecerá.

  2. Se esse pacote for uma dependência do seu projeto, o arquivo .yarnrc malicioso não será respeitado, pois ele estará dentro de node_modules, e não no mesmo nível. Portanto, nesse caso, não há risco.

Parte 2: Código escondido à vista de todos

Outro tema em alta no último ano foi o código-fonte trojanizado (ou seja, código malicioso resultante do uso de caracteres com valores Unicode diferentes de sua aparência habitual).

A ideia é bem simples: alguns caracteres, por exemplo, (que não sejam espaço, CR, LF etc.), são exibidos como espaços em branco, mas continuam sendo strings válidas.

Para aprofundar um pouco, o caractere aparentemente vazio “…” (0x3164 em hexadecimal) é chamado de “HANGUL FILLER” e, como você pode (ou não) ver, não é um espaço e é uma “letra” válida. Podemos usar isso para ocultar uma lógica específica em trechos de código e passar despercebidos ao introduzir código malicioso.

Como as informações acima são suficientes para nossa demonstração, não vou me aprofundar no assunto. Se quiser mais informações, contexto e exemplos, recomendo muito a leitura dos materiais a seguir:

Parte 3: Binários ocultos + configurações ocultas = RCE

Com tudo isso explicado, os próximos passos do nosso ataque são simples:

  1. Criar um pacote aparentemente inofensivo.

  2. Criar um arquivo malicioso (com um nome que use um caractere oculto).

  3. Criar um arquivo .rc.

  4. Definir o parâmetro de configuração para apontar para o nome do binário com o caractere invisível.

Depois de concluir essas etapas, estamos prontos. Vamos pensar nesse ataque do ponto de vista das vítimas:

Se um desenvolvedor encontrar um pacote desse tipo sem conhecer o vetor de ataque, provavelmente não perceberá os sinais de alerta.

Talvez tenha um pouco de cautela e execute ls -l. Nesse caso, verá:

$ ls -l
total 24
-rw-r--r--  1 root  staff   27 Nov 16 13:48 index.js
-rw-r--r--  1 root  staff  295 Nov 16 17:47 package.json
-rw-r--r--  1 root  staff  354 Nov 16 17:47 yarn.lock

Como a saída não parece inesperada, essa é a primeira armadilha. Nesse momento, a maioria dos alvos executará yarn install e se tornará vítima.

Mas vamos supor que nosso alvo seja um desenvolvedor experiente e adicione a opção “mostrar arquivos invisíveis” ao ls. O resultado será:

$ ls -la
total 32
drwxr-xr-x  7 root  staff  224 Feb 14 16:18 .
drwxr-xr-x  6 root  staff  192 Nov 25 17:48 ..
-rw-r--r--  1 root  staff  109 Nov 16 15:27 .yarnrc
-rw-r--r--  1 root  staff   27 Nov 16 13:48 index.js
-rw-r--r--  1 root  staff  295 Nov 16 17:47 package.json
-rw-r--r--  1 root  staff  354 Nov 16 17:47 yarn.lock
-rwxr-xr-x  1 root  staff  197 Feb 14 16:28

Agora, o alvo pode pensar: “certo, o que tem no arquivo .rc?” (ignorando completamente o arquivo discreto e invisível) e executar cat. O resultado será:

$ cat .yarnrc

yarn-path "./"

Essa é a segunda armadilha.

A maioria dos desenvolvedores não sabe que o yarn (ou o npm) pode executar binários personalizados. Ver algo como o exemplo acima é estranho, sim, mas não diz “evil.sh”, certo?

A terceira armadilha é bem simples: o alvo simplesmente não vê o arquivo. Mesmo que tenha visto o arquivo .rc e consultado a especificação de yarn-path (o npm tem algo equivalente), pode presumir que a propriedade não aponta para nada e acabar executando o gerenciador de pacotes mesmo assim.

Na minha PoC, executar yarn install resulta em:

$ yarn install
Okayyyy lesgo!

Como se proteger e considerações

Você pode se proteger seguindo estas recomendações:

  1. Nunca confie em código (de terceiros). Confira sempre o que você clonou/baixou e fique atento a comportamentos e configurações suspeitos.

  2. Execute em um ambiente isolado. Sempre execute código de terceiros em um ambiente isolado, como um sandbox ou contêiner. Assim, mesmo que o pacote seja malicioso, provavelmente não afetará seu sistema nem comprometerá sua máquina.

    Observação: executar em um sandbox talvez não seja 100% seguro, já que existem vulnerabilidades no kernel, escapes de sandbox etc. Ainda assim, esse tipo de ambiente reduz bastante o risco e bloqueia a maioria dos ataques simples (como um shell reverso básico ou equivalente).

E pronto: mitigamos o ataque.

O fascinante desse vetor de ataque é que ele é completamente visível e, ao mesmo tempo, invisível.

Alguém pode dizer: “Eu sempre executo ls -la, verifico todos os arquivos e conheço todas as opções de configuração do gerenciador de pacotes.” Tudo bem, mas será mesmo? Ampliando essa ideia: isso também vale para todas as pessoas da sua organização?

E, assim como nos filtros de Bloom, a resposta aqui é: “não significa não; sim significa talvez”.

Para encerrar, quero reforçar que este é um chamado à ação (e não à exploração) para que a comunidade aumente a conscientização sobre vetores de ataque como esse. Com tantas pessoas criando aplicações e instalando dependências quase às cegas, é essencial alertar outras pessoas sobre configurações suspeitas.

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

illustration hero ai
Blog

PHP corrige duas vulnerabilidades críticas de RCE sem autenticação (CVE-2026-109618, CVE-2026-109619): atualize agora

Duas falhas críticas no ciclo de requisições do PHP podem permitir execução remota de código sem autenticação. Atualize para uma versão corrigida ou instale um backport do fornecedor para versões mais antigas.

blog feature ai lilac
Blog

Transforme seu backlog de vulnerabilidades em pull requests com remediação agentiva

O Evo Remediation Agent está em Open Preview e traz a resolução automatizada de dependências para o Evo Agentic AppSec. Agora, as equipes de AppSec podem executar campanhas contínuas de remediação

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.