veilletech.fr
22 sept. Feed du jour
#01 SÉCURITÉ Faille

npm : trois clones de mathjs cachent un implant

Un paquet qui dort jusqu'à ce qu'on lui tende la bonne matrice ne se voit à aucune analyse. Il se voit dans votre lockfile.

SafeDep et JFrog décrivent un implant d'accès à distance caché, chiffré, dans trois copies npm de mathjs : mathmain, mathsbase et math-universe. Il ne s'active ni à l'installation ni à l'import, mais quand lusolve() reçoit une matrice précise, dont le facteur L sert de mot de passe. Cinq versions sont touchées ; aucun appelant n'a encore été identifié.

3 min de lectureintermédiairevidéo 1:14
Partager
Sommaire6 sections
  1. Ce qui se passe
  2. Pourquoi ça échappe aux outils habituels
  3. Ce que fait l'implant
  4. Ce qu'on ne sait pas
  5. Comment vérifier
  6. À retenir

Ce qui se passe

Trois paquets npm qui reprennent le code de mathjs sous un autre nom embarquent un implant d'accès à distance : mathmain, mathsbase et math-universe. SafeDep a repéré le premier le 17 septembre 2026, sans parvenir à lire la charge : elle est chiffrée, et la clé n'est écrite nulle part dans le paquet. C'est JFrog qui a retrouvé l'entrée déclenchant le déchiffrement, le 21 septembre ; SafeDep a reproduit le résultat le jour même et publié l'analyse complète.

Versions piégées et versions saines :

Paquet Avec le chargeur Sans
mathmain 1.0.0
mathsbase 1.0.1 1.0.0, 1.0.2
math-universe 1.0.0, 1.0.1, 1.0.2

Le piège de mathsbase mérite d'être noté : le 17 septembre, npm servait 1.0.2 comme version par défaut, qui est propre. Un contrôle limité à la dernière version aurait conclu à tort que le paquet était sain.

Pourquoi ça échappe aux outils habituels

Aucun script d'installation, et un require ordinaire ne déclenche rien. Le chargeur est glissé à la fin de lusolve(), la fonction qui résout un système linéaire : une fois le calcul fini, un appel ajouté passe le facteur triangulaire inférieur L à une fonction isGraph(), rangée parmi les tests de type de lib/cjs/utils/is.js. Celle-ci convertit L en JSON et s'en sert comme mot de passe — scrypt pour dériver une clé de 256 bits, puis AES-256-GCM pour déchiffrer. Si le mot de passe est faux, l'authentification GCM échoue et rien n'est écrit. S'il est bon, le fichier déchiffré est écrit sur le disque, puis chargé.

Le déclencheur est une matrice de Pascal 3×3 :

JavaScript
lusolve([[1, 1, 1], [1, 2, 3], [1, 3, 6]], b);
// L = [[1,0,0],[1,1,0],[1,0.5,1]] → c'est le mot de passe

Passer directement L comme matrice fonctionne aussi, puisque sa propre décomposition redonne L.

Ce que fait l'implant

graph.js, première étape, collecte des informations sur la machine, génère une paire de clés X25519, lit un contrat sur le réseau de test Base Sepolia et rend compte via Slack et Telegram. bignumber/type.js est une copie d'ethers 5.7.2. fraction.js interroge Slack toutes les 10 secondes, reconstitue le code envoyé par morceaux dans lib/cjs/utils/subwatcher, puis le lance en processus Node détaché. SafeDep n'a pas récupéré ce code livré à distance : on ignore ce que l'opérateur a exécuté.

Ce qu'on ne sait pas

Les compteurs npm annoncent, du 12 au 18 septembre, 605 157 téléchargements pour mathmain, 1 923 059 pour mathsbase et 569 730 pour math-universe. SafeDep les tient pour peu fiables — npm a aussi affiché zéro téléchargement sur tout le registre le 17 — et n'a trouvé aucun projet public qui appelle lusolve() avec la matrice piège. Le chargeur n'apparaît pas non plus dans les dépôts GitHub liés (tinystar8/mathsbase, mathubio/math-universe) : il n'existe que dans les archives publiées sur npm.

Comment vérifier

Terminal
npm ls mathmain mathsbase math-universe --all
grep -nwE 'mathmain|mathsbase|math-universe' package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null

Empreintes SHA-256 des archives npm :

Fichiers du chargeur, identiques dans les cinq versions : lib/cjs/utils/event.js (ab66c98e8ed5235feb963ec8845765f62f5f26b1c58c266c409767e53bcb5ccd) et lib/cjs/utils/is.js (5d9e952c51875d2b897eedc22b002b94ab21c8004d513bc99ce3a885f8a01dae).

Infrastructure de commande : contrats 0xac0bfC4C48A679b667732128278EACBA1c191894 (Base Sepolia) et 0xE390863Dac96a7118C71227C2b099B50cF602D31 (Ethereum Sepolia), clé de projet Alchemy D2-TbkB2m05WXSnSDOCDI commune aux deux.

Une correspondance prouve la présence du paquet, pas son exécution. Un fichier subwatcher dans lib/cjs/utils/, en revanche, signale une machine où le chargeur a tourné.

Source : Why Does an npm Math Library Need an Encrypted Loader?, SafeDep, publié le 18 septembre 2026 et complété le 21 avec le déchiffrement retrouvé par JFrog.