TL, RD
Un único editor de npm, deadcode09284814, levou a cabo unha campaña de typosquat contra o lexítimo axios chalk-template paquetes desde mediados de maio de 2026.
A campaña comezou como un roubo convencional de credenciais e carteiras, exfiltrando datos a un Túnel HTTPS de Serveo.
On 2026-05-17, Con axois-utils:1.0.9, o operador cambiou a carga útil por completo: a mesma estrutura de gancho de instalación triplo, o mesmo editor, o mesmo nome de paquete.
Pero o script de instalación agora é un recrutamento de botnets DDoS multiplataforma.
A carga útil fala a un localhost.run túnel e acepta comandos de inundación, convertendo os entornos infectados en parte dunha botnet con capacidade para DDoS.
O operador incluso enviou o Binario do servidor C2 dentro do ficheiro tar, mostrando unha clara escalada desde o roubo de credenciais ata a infraestrutura activa de botnet.
Dous paquetes, catro versións aínda activas no rexistro e un operador que agora executa ambos módulos en paralelo.
Gravidade: alta.
O ataque: como funciona
A campaña está construída sobre unha estrutura de entrega deliberadamente aburrida: dous nomes de paquetes typosquat, cunha letra diferente dos paquetes con centos de millóns de descargas semanais (eixes, modelo de xiz) e instalación tripla hooks que garanten a execución. O interesante é o que fai o andamio leva — e o feito de que o operador cambiou a carga sen modificar nada máis.
O andamio compartido
Cada versión publicada de ambos paquetes declara os mesmos tres ciclos de vida hooks in paquete.json:
Listar a carga útil nos tres hooks é esaxerado — postinstalación só funcionaría por defecto npm instalarA escolla importa: instalación de npm –ignore-scripts omite scripts, pero varios fluxos de traballo comúns (restauracións da caché de CI que volven executar o ciclo de vida hooks selectivamente ou desenvolvedores que só auditan postinstalación) fan que unha declaración con tripla redundancia sexa a aposta máis segura para o atacante.
plantilla de xiz engade un segundo mecanismo: declara axois-utils:^1.0.7 como tempo de execución dependenciaInstalación de typosquatted modelo de xiz polo tanto, tamén extrae o typosquat irmán e ambas as cargas execútanse. Os dous paquetes son un par coordinado, non listaxes independentes.
Fase A — ladrón de credenciais / carteiras (15-05-2026 → presente)
A fase A é a carga útil orixinal. O cargador é un pequeno postinstalación.js que apunta a un túnel inverso HTTPS de Serveo:
O subdominio Serveo codifica o enderezo IP de destino (80.200.28.28) directamente no nome do host (unha convención recoñecible para ese servizo). O operador rota o prefixo aleatorio do subdominio entre versións para evitar as listas de bloqueo de dominios inxenuas, pero o enderezo IP subxacente nunca se move. (modelo de chalk:1.0.14 usado 8a3e818ea8f11186-80-200-28-28…; 1.0.16 / 1.0.19 / 1.0.20 uso f04a273bd84c0622-….)
postinstalación.js mans fóra a phantom.js, un módulo "coleccionista" integrado de 7,667 liñas. phantom.js pasea ao anfitrión por:
Claves privadas SSH (~/.ssh/*) |
.npmrc contidos e calquera npm_* tokens de entorno |
| Ficheiros de credenciais de AWS, GCP e Azure |
expresión regular de token de acceso persoal de GitHub (ghp_*, gho_*, ghu_*, ghs_*) |
| Artefactos de criptomoedas para Bitcoin, Exodus, Electrum, Monero, Litecoin, Dogecoin, Zcash, Dash |
Un recursivo .env* camiñar por ~/projects, ~/dev, ~/code, ~/workspacee a instalación cwd |
Historias de Shell e as completas process.env |
O paquete envíase por correo ao túnel Serveo en /recollidaNon hai ofuscación, nin lóxica anti-VM, nin estadificación: a aposta do operador está no volume e na velocidade.
Fase B: recrutamento de PhantomBot DDoS (17-05-2026, só axois-utils:1.0.9)
A Fase B substitúe phantom.js + postinstall.js por un único ficheiro chamado distrube.js (o erro tipográfico é do operador). A estrutura de triplo gancho en package.json non cambia; o paquete mantén o mesmo nome, alcance e patrón de cambio de versión. Desde fóra, axois-utils:1.0.9 semella unha continuación menor de 1.0.6. Dentro, é unha familia de software malicioso diferente.
distrube.js ábrese cun bloque de configuración que nomea a propia marca do operador:
// axois-utils-1.0.9/distrube.js:11-15 |
// Use your localhost.run HTTPS URL (the tunnel handles HTTP internally) |
const C2_HOST = 'b94b6bcfa27554.lhr.life'; // Your localhost.run tunnel |
const C2_PORT = 443; // HTTPS port - tunnel handles SSL |
const BOT_ID = `${os.hostname()}_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`; |
Os comentarios están dirixidos a un futuro operador que execute o kit (“Use o seu URL HTTPS localhost.run”). Iso, ademais do que se inclúe xunto ao cliente do bot, indica que non se trata só dunha carga útil para vítimas, senón do kit.
O fluxo:
- Persistencia execútase primeiro. O script instálase de tres xeitos diferentes por sistema operativo (rexistro Correr + Cartafol de inicio + tarefas en Windows; crontab + servizo de bots fantasma unidade systemd + .bashrc/.zshrc engadir + /etc/rc.local en Linux; LaunchAgent com.phantom.bot.plist en macOS). Tanto o nome do servizo de persistencia como a etiqueta LaunchAgent están robot pantasma / com.phantom.bot, que é tamén de onde vén o nome da campaña.
- Expulsar información do sistema execútase unha vez: unha única pegada dixital POST a b94b6bcfa27554.lhr.life:443/collect que leva o nome do host, a plataforma, a arquitectura, o nome de usuario, a CPU/RAM, as interfaces de rede, o ficheiro process.env, o cwd, o directorio de inicio, a versión do nodo e o enderezo IP público. Isto é moito menos agresivo que a Fase A: sen SSH, sen carteiras, sen recursión .env. O traballo do bot é rexistrarse, non roubar.
- Bucle de latexo abre un POST HTTPS cada 30 segundos a b94b6bcfa27554.lhr.life:443/. O axente de usuario é PhantomBot/1.0. A verificación TLS está explicitamente desactivada (rejectUnauthorized: false). A resposta C2 analízase como JSON do formato {tipo, obxectivo, porto, duración, fíos, método} e envíase.
- Arsenal de inundacións está conectado a seis comandos:
| Tipo de comando | Módulos | Notas |
|---|---|---|
| pingar | Resposta de Actividade | O bot identifícase a si mesmo |
| http | Inundación HTTP | Inundación de solicitudes de capa 7 |
| https | Inundación HTTPS | Igual que http, envolto en TLS |
| tcp | Inundación de TCP | correo lixo de carga aleatoria de 65 KB |
| pud | Inundación UDP | Paquetes UDP de 65,507 bytes |
| rápido | Reinicio rápido de DoS por HTTP/2 | A técnica CVE-2023-44487 de 2023 |
Os cinco módulos de inundación teñen unha obxectivo, porto, duracióne temas argumento: o bot é un *flooder* xenérico de alugueiro, non dirixido a ningunha vítima en particular. A lista de vítimas do operador está no C2, non no paquete.
Novidades: o binario C2 incluído
A fase A ten unha forma familiar: roubo de credenciais, gancho de instalación, C2 tunelizado polo provedor. A fase B é Tamén unha forma familiar: as redes de bots DDoS levan anos sendo un elemento habitual dos paquetes npm maliciosos. O inusual é que o operador enviou o lado do servidor do kit dentro do ficheiro tar de npm:
- c2 — un binario Go ELF compilado de 4 megabytes
- c2.go — o código fonte de Go de 426 liñas para ese binario
Ningún dos dous ficheiros é invocado por ningún gancho de instalación. Non forman parte da carga útil da vítima. Están no directorio do paquete do mesmo xeito que o faría un ficheiro de documentación. A lectura máis plausible é que o operador enviou a súa árbore de traballo de desenvolvemento a npm sen seleccionar a lista de ficheiros (un verdadeiro erro de OpSec) e o que se incluíu é o kit completo de dúas caras: o cliente que executa unha vítima e o servidor que executa o operador.
Esta é a parte da historia que xustifica a formulación do "kit de botnet chave en man". Calquera que descargara axois-utils:1.0.9 antes do derribo non só ten unha mostra de vítima, senón que tamén teñen un servidor C2 que funciona.
O pivote, nunha frase
Mesmo editor, mesmos nomes de paquetes, mesmo andamiaxe de triplo gancho, mesma marca "fantasma", pero A carga útil cambiou de modelo de monetización da noite para a mañá: desde "segredos de colleita que podo revender" ata "capacidade de inundación de aluguer que podo arrendar". Os TTP en tempo de instalación que os defensores deberían estar vixiando son case idénticos en ambas as fases; defensas baseadas en sinaturas claveadas nas cadeas de ruta da carteira da Fase A ou en phantom.jsO coleccionista de 7,667 liñas tería perdido a Fase B por completo.
| Data (UTC) | evento |
|---|---|
| 2026-05-15 | Primeira publicación: axois-utils:1.0.4 (Proba de compilación LAN, plataforma de desenvolvemento en 192.168.129.19) |
| 2026-05-15 | axois-utils:1.0.6 publicado — A Fase A C2 trocouse a 80.200.28.28 vía túnel Serveo; o comentario da fonte // Change to '80.200.28.28' for public confirma o intercambio de desenvolvemento a produción |
| 2026-05-16 | chalk-tempalte:1.0.14 1.0.16 publicado — cargador encadeado declarando axois-utils:^1.0.7 como dependencia de tempo de execución; o subdominio de Serveo rotado |
| 2026-05-16 | Campaña de fase A descuberta durante a análise de npm de rutina; aplicáronse veredictos de confirmación maliciosa |
| 2026-05-17 | axois-utils:1.0.9 publicado — pivote da carga útil: recrutamento de DDoS de PhantomBot a través do túnel localhost.run; do lado do operador c2 binario e c2.go código fonte incluído no ficheiro tar |
| 2026-05-17 | chalk-tempalte:1.0.19 1.0.20 publicado — aínda Fase A (roubo); o operador agora executa ambos módulos en paralelo |
| 2026-05-17 | Descubrimento da Fase B durante a exploración de rutina; os veredictos aplicáronse en todos 2026-05-17 versións |
| (en curso) | Informes de retirada pendentes; os catro paquetes publicados seguen dispoñibles no rexistro no momento da redacción. |
Indicadores de compromiso
Paquetes
| Ecosystem | Paquete | versións |
|---|---|---|
| npm | axois-utils (typosquat de axios) | 1.0.4, 1.0.6, 1.0.9 |
| npm | chalk-tempalte (typosquat de modelo de xiz) | 1.0.14, 1.0.16, 1.0.19, 1.0.20 |
Identidade do autor
| Campo | Valor |
|---|---|
| editor de npm | deadcode09284814 |
| Correo electrónico do editor | phantomdeadcode@tutamail.com |
| SCM comprobación | ningún |
Comando e control
| Fase | Endpoint | transporte |
|---|---|---|
| A | f04a273bd84c0622-80-200-28-28.serveousercontent.com:443/collect | Túnel HTTPS de Serveo |
| A | 8a3e818ea8f11186-80-200-28-28.serveousercontent.com:443/collect | Túnel HTTPS de Serveo (versión anterior) |
| A | 80.200.28.28:443/collect | Punto final do VPS subxacente |
| B | b94b6bcfa27554.lhr.life:443/ | localhost.run túnel inverso HTTPS |
Resumos de ficheiros (SHA-256)
| Arquivo | SHA-256 | Notas |
|---|---|---|
c2 (Ver ELF, 4 MB) | 165fa92d237fd017c227d00da06ab788212a62be94bf61e95df2d22d00377ef2 | Servidor C2 do lado do operador, enviado dentro axois-utils:1.0.9 |
c2.go (426 liñas) | 7d0ae79fdb1e9968f3323a3712b624643a782ba3efb2cf3a2cb9c4c5513cea30 | Ir ao código fonte para o binario C2 |
distrube.js | 308b15c023088a7188dea4ef609010ac2493eb4c365b103053d7621a9ca5b935 | Cliente de bot de fase B |
phantom.js (chalk-tempalte:1.0.19) | 9e380ec88d3ccf3929e1a104e3b868d4d7b59ca189a8a431a54e9f3357dfdd81 | Recollidor de credenciais/carteiras de fase A |
phantom.js (chalk-tempalte:1.0.20) | d1c9e3f296ee9f7d5032f73f9c504cede50334bc14c394055fd5cb9c3a6e08b3 | Colector de fase A (variante 1.0.20) |
postinstall.js (chalk-tempalte:1.0.19 = 1.0.20) | ffba9bdd6793edd5b38e12900252c1813a693f59c25af51c3b658cf3f27b6162 | Cargador de fase A, idéntico en bytes en ambas versións |
Atribución e motivación
Facemos o seguimento desta campaña como PhantomBot, tomando a propia marca do operador do Axente de usuario: PhantomBot/1.0 cabeceira do latido do corazón, o servizo de bots fantasma unidade de sistema, a com.phantom.bot Etiqueta LaunchAgent e a código mortofantasma@ identificador de correo electrónico. O operador é coherente sobre este nome en polo menos catro artefactos.
Catálogo de sinais
- Editor - código morto09284814 en npm. Conta de usuario único, correo electrónico desbotable de Tutamail, non SCM verificación. Sen historial de publicacións previas máis alá desta campaña.
- Reutilización da marca — aparece «fantasma» no correo electrónico do editor, no nome do ficheiro do coleccionista da Fase A (phantom.js), no nome do servizo de persistencia da Fase B (servizo de bots fantasma), na etiqueta LaunchAgent (com.phantom.bot), e no axente de usuario do bot (PhantomBot/1.0).
- Infraestructura — Un único VPS en 80.200.28.28 fronte á Fase A mediante a rotación do subdominio Serveo; a Fase B móvese a un localhost.run túnel inverso (b94b6bcfa27554.lhr.vida). Os servizos de ambos provedores son gratuítos e só requiren SSH de saída por parte do operador, o que é coherente con configuracións de baixo orzamento e pouca seguridade operacional.
- estilo de código — JavaScript simple en todo; sen ofuscación, sen codificación de cadeas, sen comprobacións anti-VM. Os nomes das variables son descritivos (roubar información do sistema, executarComando, httpFlood). Comentarios en distrube.js dirixirse directamente a un operador futuro (“Use the your localhost.run HTTPS URL”), o que suxire que o ficheiro estaba destinado a ser redistribuído como un kit, non usado por un só atacante.
- Ficha de Seguridade Operacional — O ficheiro tar da Fase B inclúe tanto o cliente do bot como o servidor C2 do lado do operador (c2 ELF + c2.go fonte). Isto é coherente cun operador que pasou a súa árbore de traballo a npm sen auditar a lista de ficheiros. Ou o operador non ten experiencia ou o kit está significaba para ser distribuído publicamente e o binario C2 forma parte do produto.
- Autoconfirmación - axois-utils:1.0.4 contén o comentario da fonte // Cambiar a '80.200.28.28' para público na liña 12, confirmando a progresión do desenvolvemento/posta en escena/produción que os operadores adoitan negar.
Motivación
A carga útil da Fase A é un roubo financeiro directo: as credenciais, as claves na nube, os tokens de GitHub e as carteiras criptográficas teñen mercados de revenda líquidos. A Fase B tamén é financeira, pero indirecta: os servizos DDoS de alugueiro ("booter") alugan capacidade de inundación por minuto, e os bots como o que se envía aquí son o inventario no que se executan eses servizos. O latexado do corazón de 30 segundos, o xenérico obxectivo/porto/duración esquema de comando e o arsenal de inundacións de cinco protocolos son os standard produto dun operador de arranque.
Executando ambos módulos en paralelo — modelo de chalk:1.0.19 1.0.20 continuar a enviar o ladrón mentres axois-utils:1.0.9 envía o bot — suxire que o operador está a cubrir as fontes de ingresos en lugar de abandonar a Fase A. O pivote é un adición, non un substituto.
O que non estamos a afirmar
Non puidemos confirmar unha identidade real detrás do código morto09284814 identificador, e non atribuímos esta campaña a ningún actor de ameazas, grupo ou país con nome. A marca "fantasma" é o suficientemente consistente en todos os artefactos como para suxerir un único operador, pero o envío en estilo kit do binario C2 deixa aberta a posibilidade de que futuros paquetes de identificadores non relacionados reutilicen o mesmo código.
Impacto, tendencias e o que deberían facer os defensores
Impacto
Os obxectivos lexítimos das okupas eixes modelo de xiz son amplamente dependientes no ecosistema de JavaScript; eixes por si só está entre os trinta paquetes npm máis descargados. Os nomes de typosquat están a unha tecla de distancia dos reais (axois ↔ eixes, template ↔ modelo), e ambas son erros ortográficos lingüisticamente plausibles.
Non puidemos obter estatísticas de descarga fiables para as versións maliciosas antes da retirada: npm non conserva as contagens de descarga por versión despois de que un paquete sexa publicado e npms.ioOs proxies de estilo son ruidosos a esta escala. Calquera organización cuxo ficheiro de bloqueo se resolva nun paquete chamado axois-utils or plantilla de xiz do editor código morto09284814 debería tratarse como comprometida: rotar todas as credenciais presentes en proceso.env no host afectado, auditar os vectores de persistencia por sistema operativo (consultar "Que deben facer os defensores" a continuación) e eliminar a dependencia.
Patróns emerxentes
Esta campaña exemplifica un cambio que observamos en varios incidentes recentes de NPM: O andamio de gancho de instalación é o activo duradeiro; a carga útil é intercambiableO mesmo editor, o mesmo paquete, o mesmo preinstalación / instalar / postinstalación triplo, e mesmo a mesma cadencia de subidas de versión, o único que cambiou entre axois-utils:1.0.6 axois-utils:1.0.9 era o ficheiro o hooks executar. Detección baseada en sinaturas clave para a carga útil da Fase A (cadeas de ruta da carteira, phantom.jsO colector de 7,667 liñas de , o servidor Serveo C2) tería marcado as tres primeiras versións e perdido a Fase B por completo.
O mesmo patrón é visible noutras campañas de 2026 que seguimos: un único operador cunha estrutura estable, alternando entre o roubo de credenciais, clientes que trampan nos exames, expulsión de bots de Telegram e agora a contratación de DDoS. Os defensores que dependen das sinaturas de carga útil seguirán perdendo a segunda rolda de cada campaña.
O corolario é que Os TTP en tempo de instalación son o mellor sinalDeclaracións de hook de instalación triplo, scripts de instalación que importan https or proceso_fillo, instalar scripts que escriban nos cartafoles de inicio do usuario e instalar scripts que resolvan nomes de host en servizos de túnel inverso gratuítos (Serveo, localhost.run, ngrok, cloudflared) son raros en paquetes lexítimos e comúns en paquetes maliciosos.
Que deben facer os defensores
- Auditoría de ficheiros de bloqueo. Buscar cada package-lock.json / fío.bloqueo / pnpm-lock.yaml na súa organización para as cadeas exactas axois-utils plantilla de xizTrata calquera partido como un compromiso.
- Rotar segredos nos hóspedes afectados. Fase A: credenciais de SSH, nube, GitHub, npm e carteira recollidas en bloque. Asume todas as credenciais que estean presentes en proceso.env, ~ / .ssh, ~/.aws, ~/.npmrc, ~/.config, ou calquera .env* o ficheiro no host afectado está exposto.
- Eliminar a persistencia (Fase B). En Windows, eliminar HKCU\Software\Microsoft\Windows\Versión actual\Executar\Actualización do sistema, quitar %APPDATA%\Microsoft\Windows\Menú Inicio\Programas\Inicio\actualización do sistema.bate schtasks /delete /tn Actualización do sistema /fEn Linux, crontab -e e elimina @reboot node …, systemctl –desactivación do usuario –now phantom-bot.service logo elimina ~/.config/systemd/user/phantom-bot.servicefregar .bashrc / .zshrc / /etc/rc.localEn macOS, launchctl unload ~/Library/LaunchAgents/com.phantom.bot.plist despois elimina o plist.
- Bloquear os hosts C2. Engade os catro puntos finais C2 da táboa IOC á túa lista de bloqueos de saída. *.serveousercontent.com *.lhr.life pódese bloquear por completo se o teu entorno non ten un uso lexítimo para os servizos de túnel inverso.
- Busca por TTP no momento da instalación, non carga útil. Entradas de ficheiro de bloqueo de inventario cuxas paquete.json declarado preinstalación, instalare postinstalación todos apuntando ao mesmo script. Compare coa antigüidade do paquete e o estado de verificación do editor. Este patrón é pouco común en paquetes lexítimos e é o sinal máis fiable que PhantomBot reutiliza.
- Auditoría para o binario C2. Calquera que descargara axois-utils:1.0.9 ten o servidor C2 do operador (c2 ELF, sha256 165fa92d…) no disco. Analizar as cachés e os almacéns de artefactos de CI. O binario está inactivo por si só, pero a súa presenza nunha estación de traballo é unha proba inequívoca de que o paquete foi instalado.
Liña de peche
O mesmo operador, o mesmo paquete e o mesmo gancho de instalación poden presentar ameazas completamente diferentes en 48 horas. Vixía a estrutura, non a carga útil.




