PhantomSync: Pacotes criptográficos do npm ocultam ladrão de carteiras

PhantomSync: oito pacotes npm para desenvolvedores de criptografia ocultam um dropper com atraso e autopersistência.

TL, DR

Um único publicador do npm enviou oito pequenos pacotes cujos nomes soam como blocos de construção comuns para desenvolvimento de blockchain e carteiras digitais. base58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers e crypto-validate-libCada um deles contém uma função utilitária funcional. No entanto, após essa função utilitária, é anexado um bloco de código que se executa automaticamente assim que o módulo é importado.

Aproximadamente 37 segundos após a importação.Esse bloco decodifica uma carga útil que vem dentro do pacote disfarçada de teste, grava-a em um arquivo oculto no diretório pessoal do usuário e se registra para reiniciar a cada Conecte-se em Windows, macOS e Linux, e executa o script decodificado como um processo separado. Nada disso acontece durante o `npm install`; ele aguarda a importação e execução do código, que é um momento mais tranquilo do que a instalação.

A carga útil não é opaca. Ela é enviada em base64 simples, portanto, decodificar o "conjunto de teste" recupera o segundo estágio por completo: um carteira de criptomoedas e ladrão de segredos que aguarda a máquina ficar ociosa, coleta chaves privadas e frases-semente, criptografa cada descoberta com uma chave RSA-4096 codificada e a exfiltra fixando-a em um armazenamento IPFS público, enviando um sinal de retorno a cada 12 horas.

Acompanhamos o cluster como PhantomSyncTodos os oito pacotes estavam ativos no registro npm no momento da análise, publicados sob uma única conta.

Ecossistemanpm
PACOTESbase58-utils, abi-encode, eth-dev, arb-kit, layer2-sdk, solana-key-utils, eth-wallet-helpers, crypto-validate-lib
Plataformas visadasWindows, MacOS, Linux
Comportamento centralInstalador de dependências com atraso, executado no momento da importação, oculto como um recurso de teste; instala persistência multiplataforma.
carga pagaRouba-carteiras e segredos de criptomoedas, criptografia RSA-4096, exfiltração via pinning IPFS público

Anatomia do ataque

À primeira vista, cada embalagem parece comum. utilitários base58, por exemplo, é um pequeno trecho de código auxiliar Base58 / Bitcoin-WIF sem dependências — exatamente o que o nome promete. O código relevante está localizado em depois de do módulo módulo.exports, onde um leitor que apenas folheia o início do arquivo provavelmente não encontrará: uma função que se invoca automaticamente e que se agenda com um temporizador.

A sequência de operações, reconstruída a partir do código-fonte do pacote, funciona da seguinte forma:

1. Package is required() by the host project 2. A self-invoking function schedules a callback ~37,000 ms later (setTimeout) 3. On fire, the callback reads test/fixtures/keypairs.dat (a base64 blob) 4. It base64-decodes that blob into a Node.js script 5. It writes the script to ~/.cache-db/.node-sync/syncd.js (mode 0o700) 6. It installs Conecte-se-persistence for that script (see below) 7. It spawns "node syncd.js" as a detached process

Duas opções de design se destacam.

A carga útil viaja como um dispositivo de teste. A segunda etapa não está escrita como um código óbvio. Ela reside em test/fixtures/keypairs.dat, um arquivo base64 cujo nome se mistura a um pacote que alega lidar com pares de chaves. Para um humano que apenas lê o arquivo tarball rapidamente, ele parece dados de amostra; para o código anexado, é um script para decodificar e executar. O próprio dropper não contém endereço de rede — esses estão no segundo estágio — mas não há ofuscação adicional: o fixture é uma única camada de base64, então decodificá-lo (base64 -d) recupera o total syncd.js e seu comportamento na rede. A próxima seção descreve o que esse estágio de recuperação faz.

A detonação é retardada e está ligada à importação, não à instalação. Porque o gatilho é requer () Além disso, em vez de um gancho de instalação, utiliza um temporizador de aproximadamente 37 segundos, o que evita verificações que apenas monitoram o npm install etapa, e o atraso persiste por mais tempo do que muitas execuções de sandbox e CI de curta duração. Quando finalmente algo é executado, a instalação que puxou o pacote já terminou há muito tempo.

Assim que o script decodificado estiver no disco em ~/.cache-db/.node-sync/syncd.js — um caminho escolhido para se assemelhar a um diretório de cache comum — o dropper garante sua sobrevivência após reinicializações nas três principais plataformas:

  • Linux: Uma entrada cron que reinicia o script. A entrada é instalada filtrando o crontab existente através de grep -v sincronizado, o que tem como efeito colateral omitir a nova entrada de uma listagem simples que busca pelo mesmo nome.
  • Windows: uma tarefa agendada chamada WinNodeSync, configurado para ser executado novamente em um intervalo de 12 minutos.
  • MacOS: um trabalho lançado rotulado com.apple.syncd, presente em várias das embalagens — um rótulo que imita um serviço legítimo do sistema Apple.

O script é então iniciado imediatamente como um processo separado, de forma que continue em execução mesmo após o término do programa de importação.

O que faz a segunda etapa

Como o arquivo de configuração é uma única camada base64, o segundo estágio é decodificado corretamente e pode ser lido na íntegra. Todos os oito pacotes contêm uma das três variantes do mesmo script, que se identifica em um comentário de cabeçalho como phantom syncd v3 — topo durmiente (“toupeira adormecida”). Sua função é roubar dados de carteiras de criptomoedas e segredos de desenvolvedores, enviando-os para fora da máquina. Seu funcionamento se dá nos seguintes passos:

  • 1. Aguarde até que a máquina esteja ociosa. Antes de fazer qualquer coisa, syncd.js Verifica por quanto tempo o usuário está inativo e só prossegue após um limite (em torno de 15 minutos) — xprintidle no Linux, ioreg HIDIdleTime no macOS e uma consulta de tempo ocioso do PowerShell no Windows. A coleta de dados, portanto, tende a ocorrer quando ninguém está usando o teclado.
  • 2. Obtenha um interruptor de ativação controlado remotamente.O script extrai uma pequena configuração de um arquivo morto antes de executar a ação. A fonte principal é um URL bruto de um gist do GitHub (gist.githubusercontent.com/juang55/…/cfg.txt); o script só é ativado se essa configuração estiver definida como ativo=1Se o resumo não estiver disponível, o sistema utiliza três identificadores de conteúdo IPFS fixos, obtidos através dos gateways públicos. gateway.pinata.cloud, ipfs.io e cloudflare-ipfs.comIsso proporciona ao operador um controle de armar/desarmar posterior ao fato e um mecanismo de segurança resistente a derrubadas. (A variante menor, enviada em biblioteca de validação criptográfica, auxiliares de carteira eth e solana-key-utils, tem a camada de resumo removida e depende apenas dos identificadores IPFS.)
  • 3. Colete material para a carteira e segredos. O script percorre o diretório inicial do usuário — ~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~ / .ssh e Desktop/Documentos/Downloads (incluindo nomes de pastas em espanhol), mais ~/.env e arquivos rc do shell, e o equivalente AppData locais no Windows. Ele tem como alvo chaves privadas do Ethereum, chaves WIF do Bitcoin, frases-semente BIP-39, pares de chaves Solana, JSON do keystore do Ethereum, chaves SSH e variáveis ​​de ambiente que contêm segredos. A variante maior (em abi-codificar, utilitários base58, eth-dev) contém o dicionário BIP-39 completo de 2048 palavras e usa expressões regulares para extrato chaves individuais e frases-semente validadas de qualquer texto que leia; as duas variantes menores, em vez disso, correspondem a arquivos por palavra-chave (semente, mnemônico, wallet, metamask, fantasma, livro-razão, trezor, …) e carregar arquivos inteiros.
  • 4. Criptografe e exfiltre os dados através de um serviço público de fixação de dados. Cada descoberta é agrupada com uma impressão digital do hospedeiro (nome de usuário@nome do host, plataforma, carimbo de data/hora) e criptografado com uma chave pública RSA-4096 codificada no script. O registro criptografado é então carregado, fixando-o no IPFS via api.pinata.cloud/pinning/pinJSONToIPFS, autenticado com credenciais da API Pinata embutidas no código. Não há servidor C2 específico para ser apreendido: os dados roubados são armazenados em um armazenamento público descentralizado, recuperáveis ​​pelo operador usando os hashes de conteúdo resultantes. Os uploads são espaçados com alguns segundos de jitter aleatório e um registro local de carimbo de data/hora | tipo de chave | hash retornado é mantido em ~/.cache-db/.node-sync/.sl.
  • 5. Persistir e emitir um sinal em um ciclo de 12 horas. A segunda etapa restabelece sua própria persistência — uma entrada cron no Linux, um com.apple.syncd tarefa launchd no macOS e uma tarefa agendada chamada Sincronização de nós do Windows No Windows — configurado para executar novamente a cada 12 horas. Observe que isso é um diferente Nome da tarefa do Windows a partir daquela que o dropper instala (WinNodeSync); ambos valem a pena serem procurados.

Timeline

Os oito pacotes foram publicados em um curto período de tempo, entre 13 e 14 de julho de 2026. Vários deles possuem mais de uma versão; o dropper é idêntico em todas as versões, com apenas os deslocamentos de linha variando conforme o tamanho do código utilitário inócuo acima dele se altera.

Data Evento
2026-07-13 Os primeiros pacotes do cluster aparecem sob um editor (solana-key-utils, eth-wallet-helpers, crypto-validate-lib e versões iniciais do restante)
2026-07-13 → 07-14 Os nomes restantes e as versões subsequentes foram publicados; os mesmos navios dropper anexados em cada um.
2026-07-14 Todos os oito foram sinalizados e analisados; todos os pacotes ainda podem ser instalados a partir do registro.

Ao longo do surto, a reputação do editor no registro passou de praticamente neutra no início do cluster para fortemente negativa à medida que as detecções se acumulavam — um efeito colateral observável dos pacotes sinalizados, e não um recurso projetado.

Indicadores de compromisso

Todos os indicadores abaixo foram confirmados como presentes no momento da análise — aqueles nas tabelas da “Segunda Etapa” por meio da decodificação. test/fixtures/keypairs.dat e lendo o recuperado syncd.js.

Arquivos e caminhos

Indicador Tipo
~/.cache-db/.node-sync/syncd.js Segundo estágio decodificado, escrito com o modo 0o700
~/.cache-db/.node-sync/.sl Registro de exfiltração local (carimbo de data/hora | tipo de chave | hash IPFS)
test/fixtures/keypairs.dat Carga útil codificada em Base64 armazenada dentro do arquivo tar como um "arquivo de teste".

Infraestrutura de rede de segundo estágio (recuperada de syncd.js)

Indicador Tipo
gist.githubusercontent.com/juang55/b298754cb72942b1cdcf02ccd45cde2f/raw/cfg.txt Ativação de ponto morto; o script só é executado se a configuração estiver correta. active=1
Qmcqz3w8j4qFQXDAXAxnrdc2oSX3nzBT4NqtpTqL8mr1ga Configuração de fallback do IPFS (CID)
QmdTXoqVmTHY1i4ZWLdLkoQ9YChp5TXPh5cWXwnAYZt5iF Configuração de fallback do IPFS (CID)
QmfJkLU5gdCpqbbqEjWYC2anXW9FmuEeSLLeLiHVJKYUjp Configuração de fallback do IPFS (CID)
gateway.pinata.cloud, ipfs.io, cloudflare-ipfs.com Os gateways IPFS eram usados ​​para buscar a configuração de fallback.
api.pinata.cloud/pinning/pinJSONToIPFS Ponto de extremidade de exfiltração, dados roubados fixados no IPFS público
Chave da API Piñata 13c766575b9270a9825d, credencial de exfiltração codificada

Alvos de coleta da segunda etapa (recuperados de syncd.js)

Indicador Tipo
~/.config/solana, ~/.ethereum/keystore, ~/.foundry, ~/.hardhat, ~/.ssh, ~/.env, arquivos rc do shell Diretórios/arquivos pesquisados ​​em busca de chaves e segredos
AppData\Roaming\Solana, AppData\Local\ethereum\keystore Pesquisa por equivalentes do Windows
Tipos de artefatos coletados Chaves privadas ETH, Bitcoin WIF, frases-semente BIP-39, pares de chaves Solana, JSON do keystore Ethereum, chaves SSH, variáveis ​​de ambiente secretas

Artefatos de persistência

Plataforma Indicador
Linux Iniciando entrada cron syncd.js; instalado via crontab filtrado por grep -v syncd
Windows tarefa agendada WinNodeSync (conta-gotas) e WindowsNodeSync (segunda etapa)
MacOS rótulo launchd com.apple.syncd
Todas as A segunda fase persiste em um ciclo de 12 horas.

Comportamentais

  • Função autoinvocável anexada após módulo.exports, agendando um setTimeout de aproximadamente 37,000 ms na importação.
  • processo_filho spawn(“nó”, ) com a opção de desacoplamento definida.
  • Ativação controlada por inatividade no segundo estágio (xprintidle / ioreg HIDIdleTime / Tempo ocioso do PowerShell; limite de aproximadamente 15 minutos).
  • Após encontrar a criptografia RSA-4096, siga para HTTPS. POST para uma API pública de fixação de IPFS.

Pacotes e versões (npm, editor solbuilder_io)

Pacote versões
base58-utils 1.0.0, 1.0.1, 1.0.3
abi-encode 1.0.0, 1.0.1, 1.0.2
eth-dev 1.0.0, 1.0.1, 1.0.2
arb-kit 1.0.0, 1.0.1
layer2-sdk 1.0.0, 1.0.1
solana-key-utils 1.0.0
eth-wallet-helpers 1.0.0
crypto-validate-lib 1.0.0

Publisher

  • solbuilder_io - angel_lopez89[@]proton[.]meE-mail não verificado, sem conta de controle de versão verificada, sem repositório vinculado.

Atribuição e comportamento observado

Os oito pacotes compartilham uma conta de publicação e uma carga útil. Cada um é um pacote trivial — alguns kilobytes de código utilitário real com o mesmo dropper anexado — publicado sem um repositório vinculado e sob um e-mail descartável não verificado. Essa uniformidade, o caminho de drop compartilhado, os rótulos de persistência compartilhados e o compartilhamento keypairs.dat Os arquivos de preparação são o que dão coesão ao cluster.

As embalagens são excepcionalmente francas sobre seu próprio comportamento. solana-key-utils Contém comentários embutidos que descrevem o bloco anexado em termos simples — um deles o rotula. PHANTOM: persistência invisível, outro (em espanhol) diz Ejecutar topo en background, “executar o programa em segundo plano”. Essas são as anotações do próprio código sobre o que ele faz; o nome da campanha nesta postagem é derivado desse primeiro rótulo, juntamente com o nó falso “daemon de sincronização” (sincronizado) que o mecanismo de persistência imita.

Descrevemos apenas o que o código faz de forma observável. A nomenclatura dos pacotes — todos termos relacionados a criptomoedas, carteiras e ferramentas de blockchain — indica o público de desenvolvedores com maior probabilidade de baixá-los pelo nome; por si só, não estabelece quem é o editor. O segundo estágio foi totalmente recuperável pela decodificação da sequência base64, e seu comportamento é descrito acima: ele coleta chaves de carteira, frases-semente e segredos e os exfiltra, criptografados com RSA, para armazenamento público no IPFS via Pinata. Uma escolha de design notável é a ausência de um servidor C2 privado — a configuração chega de um gist do GitHub e do IPFS, e os dados roubados são armazenados em um armazenamento público descentralizado indexado por hash de conteúdo, ambos mais difíceis de serem obtidos do que um único host controlado pelo atacante. Nenhum relatório público anterior correspondente a este cluster foi encontrado até o momento da redação deste documento.

Quem está exposto. Qualquer pessoa que adicionou um desses pacotes a um projeto Node e executou um código que o importa. Como a detonação ocorre no momento da importação, e não no momento da instalação, simplesmente ter o pacote presente não é suficiente — mas qualquer uso normal que carregue o módulo alcança a carga útil. Os nomes das iscas visam desenvolvedores que criam soluções para Ethereum, Solana, Arbitrum, Layer-2 e ferramentas gerais de carteira/codificação — o público com maior probabilidade de possuir exatamente os ativos que a segunda etapa busca. Qualquer pessoa que executou um desses módulos em uma máquina que contém chaves de carteira, frases-semente, keystores, chaves SSH ou .env O sistema de segurança deve tratar essas credenciais como comprometidas e rotacioná-las.

Dois padrões que valem a pena internalizar. Primeiro, carga útil como acessório: enviar o segundo estágio como base64 dentro de um arquivo de dados com nome plausível (test/fixtures/keypairs.dat) mantém a origem visível do pacote com aparência limpa e empurra o conteúdo malicioso para um arquivo que ferramentas de revisão e análises humanas geralmente tratam como dados inertes. Segundo, Execução com atraso no momento da importação e persistência entre plataformasRemover o gatilho do gancho de instalação, adicionar um temporizador e, em seguida, persistir no cron, em tarefas agendadas e no launchd é um passo deliberado para se afastar das técnicas de script de instalação mais ruidosas que a varredura automatizada do registro monitora mais de perto.

Orientações para defensores e responsáveis ​​pela manutenção do campo:

  • Tratar o código anexado após módulo.exports Como prioridade de revisão, a lógica do dropper frequentemente se esconde abaixo da superfície "real" do módulo.
  • Não assuma que os arquivos de dados sejam inertes. Um blob base64 sob testes/jogos/ que é lido em tempo de execução e decodificado é adjacente ao executável; sinaliza leituras em tempo de execução de arquivos de teste que alimentam um função/avaliação/escrever-então-ovas corrente.
  • Procure o caminho de queda ~/.cache-db/.node-sync/ e para as unidades de persistência WinNodeSync (tarefa agendada) e com.apple.syncd (launchd) em máquinas de desenvolvedores que obtiveram esses nomes.
  • Atenção para processos Node que criam entradas cron, tarefas agendadas ou trabalhos launchd — bibliotecas legítimas raramente fazem isso durante a importação.
  • Dê preferência a arquivos de bloqueio e versões fixadas e revise as diferenças de qualquer nova dependência "utilitária" pequena, especialmente pacotes sem dependências publicados por contas não verificadas e sem repositório vinculado.

Para os defensores do registro, a conclusão é que o monitoramento do gancho de instalação é necessário, mas não suficiente: um dropper executado no momento da importação, com atraso definido por um temporizador e inserido em um arquivo de dados, passará em uma verificação que considera apenas o momento da instalação, e a etapa de persistência geralmente é o sinal observável mais forte que resta para ser detectado.

sca-tools-software-composição-análise-ferramentas
Priorize, corrija e proteja seus riscos de software
Crie sua conta gratuita.
Nenhum cartão de crédito é necessário.

Proteja seu desenvolvimento e entrega de software

com o Suíte de Produtos da Xygeni