← Retour à tous les articles
Publié le · par Renaud Deraison

Le portable était la porte d'entrée

Binance affirme que le plus gros vol de cryptomonnaies jamais enregistré, environ 6,3 milliards de dollars, a pu commencer sur l'ordinateur portable compromis d'un ingénieur DevOps. Le groupe accusé a une habitude : faire exécuter un projet à un développeur, puis se servir de tout ce que le portable peut atteindre. Les agents de code exécutent désormais des projets toute la journée. Dans Bromure Agentic Coding, ce code tourne dans une VM qui ne détient que des identifiants de substitution, et chaque usage d'une clé de production passe par le Mac.

L'attaquant n'avait pas besoin des clés privées de Binance. Il lui fallait le portable d'un ingénieur et les systèmes que celui-ci pouvait atteindre. La machine d'un développeur est un chemin vers la production, et les agents de code exécutent désormais tout le code qui y atterrit.

Le 30 octobre, Binance a révélé un « incident de sécurité » portant sur environ 6,3 milliards de dollars d'actifs. The Hacker News rapporte que Chainalysis a évalué le vol à 5,912 milliards de dollars au moment des faits, près de quatre fois le plus gros vol de cryptomonnaies enregistré jusque-là. Près de 3 milliards de dollars ont été gelés on-chain, pour l'essentiel des stablecoins gelés par Tether et Circle. Binance dit qu'elle couvrira toutes les pertes.

Le récit que fait Binance du point de départ tient en une phrase : « L'enquête initiale suggère que l'incident de sécurité pourrait impliquer l'ordinateur portable compromis d'un ingénieur DevOps », ce qui a permis à un attaquant d'atteindre un conteneur de service et de soumettre des retraits non autorisés. Binance dit n'avoir trouvé aucune preuve de la fuite d'une clé privée de portefeuille. Elle n'a pas dit comment le portable a été compromis.

Cinquante signatures et trois minutes

Les données on-chain montrent ce que valait cet accès. À 20 h 20 UTC, l'attaquant a soumis une transaction portant 50 autorisations, chacune signée par le hot wallet de Binance, une pour chacun de 50 numéros de transaction consécutifs, afin que l'une d'elles corresponde au suivant du portefeuille. Chacune pointait vers un contrat de balayage qui n'obéit qu'à l'attaquant. L'une a correspondu. Environ trois minutes et 24 secondes plus tard, le hot wallet a envoyé la totalité de son solde à l'attaquant : 167 464 ETH et 63 tokens, dont 1,615 milliard d'USDT.

Le directeur général de Blockaid, Ido Ben-Natan, a déclaré à Cointelegraph que les premiers éléments pointent vers un « détournement de clé » : on a fait signer au système de signature les transactions de l'attaquant, et aucune clé n'a eu besoin d'être volée. Les clés sont restées où elles étaient. Quelqu'un disposant de l'accès de l'ingénieur a demandé au système de s'en servir.

TRM Labs attribue l'attaque « avec un haut degré de confiance » à TraderTraitor, le groupe nord-coréen que le FBI a tenu pour responsable du vol de 1,5 milliard de dollars chez Bybit en février 2025. Chainalysis désigne également la Corée du Nord. Binance n'a nommé personne.

Comment ce groupe s'installe sur les portables

L'intrusion la mieux documentée de TraderTraitor montre la méthode. Dans l'affaire Bybit, le rapport préliminaire de Safe lui-même et les conclusions de Mandiant la décrivent dans l'ordre :

  1. Le 4 février 2025, un développeur de Safe a exécuté sur son Mac un projet Docker nommé MC-Based-Stock-Invest-Simulator-main. Mandiant pense qu'on l'a convaincu de le faire.
  2. Le projet a contacté getstockprice[.]com, un domaine enregistré deux jours plus tôt, et le poste de travail macOS du développeur a été compromis.
  3. Les attaquants ont pris les jetons de session AWS du développeur, ce qui leur a permis d'utiliser son accès AWS sans passer par l'authentification multifacteur.
  4. Avec cet accès, ils ont modifié l'interface Safe qu'utilisaient les signataires de Bybit, et ceux-ci ont approuvé une transaction qui a vidé un cold wallet.

Mandiant a noté que le groupe avait utilisé des projets Docker similaires, sur le thème de la Bourse, lors de vols précédents. La méthode ne change pas : donner un projet à un développeur, le laisser tourner sous son identité, et prendre ce que sa machine peut atteindre. Un portable DevOps peut atteindre beaucoup de choses. Il contient en général ~/.aws avec des jetons de session valides, un ~/.kube/config pour les clusters de production, des clés SSH pour le bastion et une session gh, le tout lisible par n'importe quel processus que l'ingénieur lance.

D'un projet sur un portable à la productionUn projet est exécutéProjet Docker, testtechnique, starter kit, parle développeur ou son agentIl tourne en son nomcontacte un domaine toutrécent, dépose un malwaresur le portableIl lit les accèsjetons de session ~/.aws,~/.kube/config, clés SSH,la session ghIl utilise les accèsni demande MFA, ninouvelle connexion :l'identité de l'ingénieurLa production fait ce que demandent les identifiants de l'ingénieurBybit : interface de signature modifiée, 1,5 milliard $. Binance : conteneur de service, 50 autorisations signées, ~6,3 milliards $.
Le schéma documenté de TraderTraitor, d'après l'enquête Bybit. Un développeur exécute un projet, le projet tourne sous l'identité du développeur, et l'attaquant hérite de tout ce que le portable peut atteindre : jetons de session cloud, accès aux clusters, clés SSH. Binance dit que son incident a commencé par un portable DevOps compromis qui a atteint un conteneur de service ; elle n'a pas dit comment le portable a été compromis.

C'est l'agent qui exécute le projet, désormais

Le leurre exigeait autrefois un développeur que l'on pouvait convaincre d'exécuter quelque chose. Cette étape devient plus facile à sauter. Le jour même où Binance a révélé sa brèche, BleepingComputer a rendu compte de l'analyse par Air Security d'un dépôt GitHub nommé SolanaUpdateTools/solana-starter-kit. Il compte 286 étoiles et, selon Air, arrive en tête quand des agents de code IA cherchent un starter kit Solana. Le README est son unique fichier. Il promet un modèle d'application complet et donne une seule étape d'installation : coller une commande qui assemble à partir de fragments une URL sur developer-update[.]app, télécharge un script et l'exécute en mémoire.

La charge utile récupérée par Air installe NetSupport Manager, un outil légitime d'accès à distance, configuré pour se connecter à l'attaquant à chaque ouverture de session. Puis elle affiche une fausse erreur : les fonctions développeur sont « temporairement suspendues en raison de la charge », réessayez dans 72 heures. Un agent qui suit le README exécuterait la commande et signalerait un serveur surchargé. Le développeur ne verrait rien qui mérite un second regard.

Placez cet agent sur le portable d'un ingénieur DevOps, avec le kubeconfig et la session AWS de l'ingénieur dans le même répertoire personnel, et le starter kit devient le projet Docker de l'affaire Bybit, sans l'ingénierie sociale.

Le même projet dans Bromure Agentic Coding

Bromure Agentic Coding fait tourner Claude Code, Codex, Grok, Kimi et omp dans des VM Ubuntu sur votre Mac. Chaque machine est une VM à part entière, et tout ce que l'agent touche vit à l'intérieur : dépôts clonés, caches de paquets, conteneurs Docker, et chaque commande que suggère un README. Votre Mac détient les vrais identifiants, le proxy par lequel passe le trafic de la VM, et le journal de ce qui s'est passé.

Le projet tourne dans la VM, pas sous votre identité

Exécutez le projet Docker du simulateur boursier dans une machine Bromure, ou laissez l'agent coller la commande du starter kit. Le conteneur démarre dans la VM, et la charge utile aussi. Elle obtient un shell dans un invité Ubuntu, sous l'utilisateur de la machine. Elle n'obtient ni votre compte macOS ni votre trousseau. Les ~/.aws, ~/.kube et ~/.ssh de votre Mac n'y sont pas non plus : Bromure lit ces fichiers sur l'hôte quand vous les importez et ne les monte jamais dans une VM.

Les clés qu'elle trouve sont des substituts

La charge utile cherche ensuite des identifiants, et la VM en contient beaucoup qui ont l'air valides. Aucun ne fonctionne ailleurs.

  • AWS. ~/.aws/config pointe vers un utilitaire qui fournit le vrai identifiant de clé d'accès avec une fausse clé secrète, et aucun jeton de session. La VM signe chaque requête avec le faux secret, et le Mac la signe à nouveau avec le vrai en route vers *.amazonaws.com. Copiez les identifiants AWS de la VM sur une autre machine et chaque appel échoue avec InvalidSignatureException. Le jeton de session qu'ont pris les attaquants de Bybit n'existe dans la VM sous aucune forme.
  • Kubernetes. Le ~/.kube/config de la VM est synthétique. kubectl parle au proxy de Bromure, jamais directement au serveur d'API. Un bearer token est un substitut brm-k8s-…, un certificat client est un certificat jetable, et un plugin exec comme aws eks get-token s'exécute sur le Mac et non dans la VM.
  • SSH. Le SSH_AUTH_SOCK de la VM est un pont vers le Mac. L'invité peut demander une signature, et la clé privée ne quitte jamais l'hôte. ls ~/.ssh/id_* ne trouve rien à copier. Le ssh-agent de votre Mac n'est jamais exposé à une VM.
  • Git. Le jeton GitHub de gh et de ~/.git-credentials est un substitut que le proxy échange contre le vrai uniquement sur les requêtes vers github.com.

Les substituts servent aussi de pièges. Si la charge utile en envoie un à son propre serveur, le proxy bloque la requête avec un HTTP 451 avant qu'un seul octet ne quitte votre Mac, met la VM en pause et affiche This environment may have been compromised, en nommant l'identifiant et l'hôte visé. La machine ne redémarrera pas tant que son disque et son répertoire personnel n'auront pas été effacés. Vos réglages et vos clés sont conservés.

Les clés de production répondent à vous

Les substituts règlent la question du vol. L'affaire Binance ajoute un second risque : un attaquant qui ne vole pas du tout l'accès, et l'utilise depuis l'endroit où il fonctionne déjà. Une charge utile dans la VM peut envoyer des requêtes via le proxy comme le fait l'agent. Les Guardrails de Bromure décident de ce que ces requêtes ont le droit de faire, et ils sont appliqués sur le Mac, où rien dans la VM ne peut les modifier.

  • Require approval to use, réglé par identifiant : la première fois qu'un contexte Kubernetes, une clé AWS ou une clé SSH est utilisé dans une session, le proxy retient la requête et vous demande sur votre Mac. Vous choisissez 5 minutes, une heure ou la session, ou Don't allow. Pour AWS, la vérification couvre chaque appel de signature. Une charge utile qui cherche à atteindre le cluster de production avant que vous ne l'ayez approuvé fait apparaître une boîte de dialogue sur votre écran, et n'obtient rien tant que personne n'y a répondu.
  • Politique d'écriture, réglée par service. Les nouvelles machines démarrent en Prompt before write pour Kubernetes, AWS, les registres de conteneurs, les forges git et les bases de données : les lectures passent, et chaque écriture s'arrête sur une boîte de dialogue qui montre l'opération exacte. Block destructive refuse les suppressions et les terminaisons, et Read-only refuse toute modification. Une machine qui n'a besoin que de lire des journaux de production peut être réglée en lecture seule, et rien de ce qui y tourne ne peut modifier la production.
  • Outbound connections, le pare-feu propre à chaque machine. Réglez Unmatched traffic sur Deny et n'autorisez que les hôtes dont le projet a besoin. Le téléchargement du starter kit depuis developer-update[.]app, l'appel du projet Bybit à getstockprice[.]com et la connexion de NetSupport vers son serveur tombent tous sous la règle par défaut et sont refusés.

Chaque usage d'identifiant, chaque signature, chaque blocage et chaque autorisation arrive dans la Security Timeline de la machine, les appels de signature AWS et les signatures SSH étant enregistrés un par un. Binance reconstitue aujourd'hui un chemin d'attaque à travers un « ordinateur compromis ». Dans Bromure, la trace de ce que la VM a fait de vos identifiants est conservée sur le Mac, hors de la VM que contrôlait l'attaquant.

Portable : le projet tourne sous votre identitéLe projet ou l'agent exécute une commandedans votre compte macOS, avec votre répertoire personnelDe vrais identifiants sur le disquejetons de session ~/.aws, ~/.kube/config,clés privées ~/.ssh, la session ghLa production, directementcopiés chez l'attaquant, ou utilisés sur place ;sans demande ni MFA, avec l'identité de l'ingénieurBromure : le projet tourne dans une VMVM de la machine (Ubuntu)la charge tourne ici : faux secret AWS, brm-k8s-…, nulle clétout le trafic via le MacProxy sur votre Macsubstitut vers le mauvais hôte : 451, VM en pausehôte inconnu : refusé par le pare-feu sortantusage d'identifiant : approbation, politique d'écritureseulement ce que vous approuvezLa production, avec le vrai identifiantajouté sur le Mac, consigné dans la Security Timeline
Le même projet sur un portable et dans Bromure. Sur un portable, le projet tourne sous l'identité du développeur et lit dans le répertoire personnel des identifiants cloud, cluster et SSH valides. Dans Bromure, il tourne dans la VM de la machine, ne trouve que des substituts, et chaque usage d'un vrai identifiant passe par le proxy du Mac, où s'appliquent les demandes d'approbation, les politiques d'écriture et le pare-feu sortant.

Tenir le starter kit à l'écart de la production

Les machines Bromure ne coûtent pas grand-chose à créer, on peut donc répartir les accès entre elles. Les identifiants se configurent par machine. Ne donnez à la machine qui essaie un nouveau starter kit, un test technique à faire chez soi ou le dépôt d'un inconnu aucun identifiant de production, et un pare-feu qui refuse par défaut. Gardez le kubeconfig et le rôle AWS sur la machine qui fait le travail de déploiement, avec une approbation exigée pour chacun. Pour quelque chose que vous préférez ne pas garder, bromure-cli vm run --rm crée une machine qui est supprimée quand elle s'arrête.

La charge utile peut alors prendre le contrôle de la VM du starter kit sans y trouver quoi que ce soit qui vaille la peine : aucun vrai secret, aucun cluster, et aucune route vers un hôte que vous n'avez pas autorisé. La machine de déploiement n'a jamais exécuté son code.

Le projet tourne dans une VM

Projets Docker, commandes de README et installations de l'agent tournent dans la VM Ubuntu de la machine, loin de votre compte macOS et de votre trousseau.

Des substituts, pas des jetons de session

Les secrets AWS sont faux et les jetons de session absents, les kubeconfigs sont synthétiques, les clés SSH restent sur le Mac. Les copies échouent partout ailleurs.

La production demande d'abord

L'approbation par identifiant et les politiques d'écriture par service s'exécutent dans le proxy sur votre Mac. Rien dans la VM ne peut les désactiver.

Refuser l'hôte inconnu

Avec Unmatched traffic réglé sur Deny, le téléchargement d'une charge utile et son appel vers son serveur sont refusés, et la tentative est enregistrée.

Le voir se produire deux fois

Notre série de tutoriels A Tale of Two Agents s'inspire de cette attaque. Theo, ingénieur DevOps chez Coinvance, une plateforme d'échange fictive, demande à son agent de codage de terminer le test technique d'un recruteur, et le README demande à l'agent d'exécuter d'abord un script d'installation. La même erreur se joue deux fois sur un écran partagé : à gauche, l'agent tourne sur son portable, à droite dans Bromure.

A Tale of Two Agents : l'agent sur le portable de Theo à gauche, l'agent dans Bromure à droite

  1. Le test technique (1:24). Sur le portable, le script envoie ses vraies clés cloud, son kubeconfig et son jeton de déploiement en guise de « télémétrie ». Dans Bromure, il ne trouve que des leurres, et quand ils partent vers le mauvais hôte, Bromure met la VM en pause et lui pose la question.
  2. Latéral (1:18). Quelqu'un modifie le service de retrait avec l'accès Kubernetes de Theo. À droite, la Security Timeline remonte de la requête bloquée jusqu'au raisonnement de l'agent, et l'espace de travail est effacé en un clic.
  3. Vingt minutes (1:02). À gauche, les hot wallets se vident. À droite, il n'y a aucune clé à renouveler.

Theo et Coinvance sont inventés, et les épisodes ne retracent pas ce qui s'est passé chez Binance. Chacun est narré en huit langues.

La machine du développeur fait partie de la production

Le service de retrait de Binance est coupé pour une « refonte de la sécurité » et devrait revenir d'ici 48 heures, et son analyse complète en dira plus sur la façon dont le portable a été atteint. Bybit a déjà appris le schéma à tout le monde. Les attaquants qui veulent les systèmes de production d'une entreprise s'en prennent aux machines des ingénieurs qui les font tourner, et sur la plupart de ces machines l'accès à la production tient dans quelques fichiers du répertoire personnel.

Votre agent de code élargit cette ouverture. Il clone, installe et exécute du code d'inconnus, tâche après tâche, et il collera la commande d'un README plus vite que vous. Dans Bromure Agentic Coding, le code tourne dans une VM qui ne contient que des substituts, et l'accès à la production reste sur le Mac, derrière une boîte de dialogue à laquelle vous devez répondre.

Pour une campagne de vol de jetons qui n'avait besoin d'aucun serveur attaquant, lisez Le jeton volé n'avait besoin d'aucun serveur. Pour un agent qui recevait ses ordres d'un fichier téléchargé, lisez Le téléchargement donnait les ordres.