Crypto : un bug a inventé 49 millions de jetons qui n'existaient pas

Crypto : un bug a inventé 49 millions de jetons qui n'existaient pas

Le 18 août, vers 17 h 30 heure de Greenwich, quelqu'un a envoyé une transaction à un réseau qui s'appelle MAYAChain. Une seule. Elle contenait 23 instructions empilées les unes derrière les autres, et elle a enchaîné six bugs différents comme on enfile des perles.

Résultat : le protocole a écrit dans ses comptes qu'il devait 49 millions de jetons à quelqu'un. Des jetons qui n'existaient nulle part. Il n'en avait que 168 000 en caisse.

Le reste s'est joué en quelques secondes.

Un registre comptable où une machine a inscrit 49 000 000 de jetons émis, à côté d'un coffre presque vide contenant 168 000

La machine à tamponner s'est emballée. Le coffre, lui, n'a pas suivi.

D'abord, c'est quoi ce truc

Maya est ce qu'on appelle un protocole d'échange entre chaînes. L'idée de départ est plutôt sympathique : te permettre d'échanger directement des bitcoins contre autre chose, sans passer par une plateforme d'échange, donc sans confier tes fonds à une société qui pourrait fermer ou se faire pirater.

Pour que ça marche, il faut de la matière première. Des gens déposent leurs jetons dans des réserves communes, appelées bassins de liquidité, et touchent une part des frais au passage. Quand tu fais un échange, tu puises dans le bassin et tu y déposes ce que tu donnes. C'est le principe.

Soyons honnêtes tout de suite sur la taille : Maya est un petit acteur. On parle d'un protocole dont l'ensemble des bassins pesait une dizaine de millions de dollars, pas d'un géant. Si tu n'en avais jamais entendu parler, c'est normal. Ce qui rend l'affaire intéressante n'est pas le montant, c'est la mécanique. Elle est d'une bêtise magnifique et elle peut arriver à n'importe qui, y compris dans du code qui n'a rien à voir avec les cryptos.

Le garde-fou qui s'est retourné

Voilà le déroulé, tel que l'équipe l'a publié elle-même dans son rapport d'incident.

Schéma en quatre étapes, un capteur qui se déclenche à tort, un calcul sans limite haute, un virement échoué dont l'écriture reste, et un petit dépôt qui prend tout le bassin

Quatre étapes, une seule transaction. Chaque case toute seule est inoffensive.

Un. La fausse alerte. Maya surveille ses propres mouvements de fonds. Quand des sorties d'argent lui paraissent anormales, il considère qu'il y a eu vol. L'attaquant a réussi à faire passer des transferts parfaitement légitimes pour des fonds disparus.

Deux. Le dédommagement sans plafond. Quand le protocole croit s'être fait voler, il dédommage automatiquement ceux qui avaient déposé dans le bassin concerné. C'est un mécanisme de protection, écrit pour être gentil avec les utilisateurs. Sauf que le calcul du dédommagement n'avait pas de limite haute. Pas de « au-delà de tant, on arrête et on appelle un humain ». L'équipe a crédité un bassin de 49,45 millions de jetons CACAO, sa monnaie maison. Le bassin choisi n'était pas là par hasard : c'était l'un des plus petits de la maison.

Trois. Le virement échoue, mais la ligne reste. Le protocole essaie alors de transférer réellement ces 49 millions depuis sa réserve. Il n'en a que 168 000. Le transfert échoue, évidemment. Mais une erreur d'écriture comptable fait que le solde gonflé, lui, reste inscrit. Le protocole croit désormais dur comme fer qu'il y a 49 millions de jetons dans ce bassin.

C'est cette troisième étape qui me fascine. Le virement a échoué. Le système a bien vu qu'il n'avait pas l'argent. Et il a quand même gardé le chiffre.

Cent jetons, et la moitié du stock

Le reste est presque une formalité.

Le bassin affiche maintenant une fortune. L'attaquant y dépose 100 jetons. Comme le bassin est minuscule en vraie valeur mais énorme sur le papier, ce dépôt lui donne 99,93 % de la propriété du bassin.

Une balance très déséquilibrée, une seule pièce d'un côté, une montagne de pièces de l'autre

Cent jetons d'un côté. Presque la moitié de la monnaie en circulation de l'autre. On a connu des retours sur investissement plus modestes.

Il retire alors sa part : 48,87 millions de jetons CACAO. Presque la moitié de tout ce qui existe de cette monnaie.

Le tout, du premier au dernier geste, dans la même transaction.

Ce que ça a coûté, en vrai

Les chiffres viennent du rapport de l'équipe, publié le 19 août, donc ce sont leurs chiffres.

L'attaquant est reparti avec environ 1,65 million de dollars d'actifs. Dans le lot, 1,36 million est sorti vers d'autres chaînes, dont 20,83 bitcoins envoyés vers une seule adresse, et il reste à peu près 291 000 dollars immobilisés.

Le CACAO, lui, s'est effondré. Il valait autour de 0,115 dollar avant l'attaque, il est descendu jusqu'à 0,013, avant de remonter vers 0,03. Ce n'est pas un mouvement de marché mystérieux à interpréter : la moitié de la monnaie a été créée à partir de rien puis vendue, on connaît la cause exacte et l'heure exacte.

La valeur totale des bassins de Maya a reculé d'environ 10,9 millions de dollars, soit bien plus que ce que l'attaquant a emporté, parce qu'à la panique s'ajoutent ceux qui retirent leurs billes et ceux qui viennent profiter de l'écart de prix.

Réaction de l'équipe : arrêt du réseau. Ils ont coupé les échanges pour contenir les dégâts, en annonçant qu'ils corrigeraient la faille avant de rouvrir. C'est la bonne décision et elle mérite d'être notée, parce qu'elle coûte cher en réputation et qu'elle est prise dans l'urgence.

La leçon, et elle n'est pas réservée aux cryptos

Voilà pourquoi cette histoire m'intéresse bien plus que son montant.

Le code qui a tout fait sauter n'était pas du code d'attaque. C'était un mécanisme de protection. Un truc écrit avec de bonnes intentions, pour rembourser les utilisateurs si le protocole se faisait voler. La rustine était le trou.

Et la faute technique tient en trois mots : pas de plafond. Une valeur calculée automatiquement, versée automatiquement, sans que personne n'ait écrit « et si ce nombre dépasse une valeur absurde, on s'arrête ».

Franchement, qui n'a jamais écrit ça ? Un compteur qui s'incrémente sans borne, une boucle de remboursement, un calcul de remise qui pourrait devenir négatif, un champ de quantité sans maximum. On teste tous le cas normal. On teste beaucoup moins souvent le cas où le chiffre part en vrille, parce qu'on se dit qu'il ne partira pas en vrille.

Le deuxième enseignement est encore plus universel : une écriture comptable a survécu à l'échec de l'opération qu'elle décrivait. Le transfert a raté, la ligne est restée. C'est le genre de bug qu'on trouve dans n'importe quelle application de gestion, et qui s'appelle simplement une transaction mal fermée.

Si tu écris du code qui manipule des soldes, il y a deux questions à se poser aujourd'hui, et elles prennent dix minutes : est-ce que mes calculs automatiques ont une borne haute, et est-ce que mes écritures sont annulées quand l'opération échoue ?

Et si tu détiens des cryptos, ça te concerne comment ?

Directement si tu avais déposé dans un bassin de Maya, sinon pas du tout. Le bitcoin lui-même n'est pas en cause une seule seconde : la blockchain a fonctionné exactement comme prévu, elle a transporté les 20,83 bitcoins de l'attaquant sans broncher, parce que c'est son travail et qu'elle ne juge pas.

Ce qui a lâché, c'est un logiciel construit par-dessus. La distinction compte, parce qu'on lit souvent « le bitcoin a été piraté » alors que ce qui a été piraté est un programme qui s'en sert.

Ce que ça rappelle, en revanche, c'est que l'endroit où tu laisses tes fonds est une décision technique. Un protocole automatique n'est pas plus sûr qu'une plateforme parce qu'il est automatique : il est différemment risqué. La plateforme peut fermer ou se faire cambrioler. Le protocole automatique, lui, fait exactement ce que son code dit, y compris quand son code dit une bêtise. Il n'y a personne pour trouver que 49 millions, quand même, c'est beaucoup.

J'écrivais il y a trois semaines comment 70 millions de dollars étaient partis d'un portefeuille matériel sans que personne ne touche un seul coffre-fort, juste à cause d'un défaut dans la façon de tirer des nombres au hasard. Même famille de leçon : dans ce domaine, l'argent ne se vole presque plus, il se calcule.

Ce que j'en pense

Ce qui me frappe, c'est la disproportion entre l'élégance de l'attaque et la banalité de la faille. Enchaîner six bugs dans une seule transaction, comprendre qu'un mécanisme de dédommagement peut être déclenché à la demande, viser le bassin le plus petit pour que le levier soit maximal : il y a du métier là-dedans, et pas mal d'heures passées à lire du code.

Et en face, il manquait un if.

Le seul point vraiment positif de l'affaire, et je le note parce qu'il est rare, c'est que l'équipe a publié le déroulé complet, bug par bug, au lieu de parler d'un « incident de sécurité » en trois lignes vagues. On sait exactement ce qui s'est passé. C'est comme ça que les autres évitent de refaire la même chose, et c'est précisément ce qu'on n'obtient jamais quand une grosse société se fait avoir.

Alors si tu maintiens du code qui verse de l'argent tout seul, va relire ta fonction de remboursement ce week-end. Elle a un plafond ? Sûr ?


Sources

Rejoignez la conversation

Vous devez avoir un compte pour commenter cet article. La création est gratuite et prend moins d'une minute.

  • Le fichier XMLTV à télécharger gratuitement, chaque jour
  • Commenter les articles et répondre aux autres lecteurs
  • Être averti par e-mail des nouveaux articles que vous suivez

Aucun commentaire pour le moment.

Une erreur s'est produite. Cette application peut ne plus répondre jusqu'à ce qu'elle soit rechargée.Veuillez contacter l'auteur. Reload 🗙