En juin 2016, un attaquant a utilisé une faille de reentrancy pour drainer 3,6 millions d'ETH du DAO (Decentralized Autonomous Organization), soit environ 60 millions de dollars à l'époque. C'était 14% de tout l'ETH en circulation. L'événement a été tellement grave qu'Ethereum a dû faire un hard fork pour annuler le vol, créant Ethereum Classic (ETC) pour ceux qui refusaient de modifier la blockchain. C'est l'attaque fondatrice de la sécurité DeFi.

Comment la reentrancy fonctionne

Le concept est plus simple qu'il n'y paraît. Un smart contract envoie de l'ETH à une adresse. Si cette adresse est un autre smart contract (et pas un wallet classique), ce contrat peut contenir une fonction fallback qui s'exécute automatiquement quand il reçoit de l'ETH. Cette fonction fallback peut rappeler le contrat d'origine pour demander un nouveau retrait avant que le premier ne soit terminé.

Imaginez un distributeur automatique. Vous demandez un retrait de 100 euros. Le distributeur vous donne les billets, mais avant de débiter votre compte, vous appuyez sur le bouton de retrait une deuxième fois. Le distributeur vérifie votre solde (pas encore débité), vous donne 100 euros de plus, puis débite une seule fois. Vous avez retiré 200 euros avec 100 euros sur le compte.

En Solidity, le problème vient de l'ordre des opérations. Si le contrat envoie les fonds (call) avant de mettre à jour le solde (state change), l'attaquant peut "re-entrer" dans la fonction de retrait pendant que l'envoi de fonds est en cours.

Le hack du DAO en détail

Le DAO était un fonds d'investissement décentralisé sur Ethereum. Les participants déposaient de l'ETH et votaient pour financer des projets. La fonction splitDAO permettait aux participants de retirer leur part.

Le bug : splitDAO envoyait d'abord l'ETH au participant, puis mettait à jour son solde à zéro. L'attaquant a créé un contrat malveillant dont la fonction fallback rappelait splitDAO. À chaque appel, le DAO envoyait de l'ETH et l'attaquant rappelait splitDAO avant que le solde ne soit mis à jour. En boucle, jusqu'à avoir drainé 3,6 millions d'ETH.

La communauté Ethereum a voté un hard fork pour annuler les transactions de l'attaquant et restituer les fonds. C'est un moment fondateur qui soulève une question qui divise encore : est-ce que modifier une blockchain "immutable" pour corriger un vol est acceptable ? Ethereum Classic (la chaîne non modifiée) dit non. Ethereum dit oui.

La reentrancy après le DAO

On pourrait croire que tout le monde a appris la leçon. Pas vraiment. En 2020, le protocole Lendf.Me a perdu 25 millions de dollars à cause d'une reentrancy. En 2021, Cream Finance a été exploité pour 130 millions via une attaque incluant de la reentrancy. En 2022, Fei Protocol a perdu 80 millions. Les développeurs continuent de faire la même erreur.

La reentrancy est devenue un classique des CTF (Capture The Flag) en sécurité blockchain. C'est la première vulnérabilité qu'on apprend à détecter dans les formations d'audit de smart contracts. Et pourtant, des protocoles déployés en production avec des millions de TVL passent à travers.

C'est pour ça que les audits de smart contracts sont indispensables avant d'investir dans un protocole DeFi. Un audit ne garantit pas l'absence de bugs, mais il réduit considérablement le risque de failles aussi connues que la reentrancy.

Comment les développeurs se protègent

Le pattern Checks-Effects-Interactions est la solution standard. Au lieu d'envoyer les fonds puis mettre à jour le solde, le contrat vérifie les conditions (checks), met à jour l'état (effects), puis envoie les fonds (interactions). Si l'attaquant rappelle le contrat pendant l'envoi de fonds, le solde est déjà à zéro et le retrait échoue.

Les mutex (verrous d'exclusion mutuelle) sont une autre approche. Le contrat pose un verrou au début de la fonction et le libère à la fin. Si la fonction est rappelée pendant son exécution, le verrou bloque la ré-entrée. OpenZeppelin fournit un modifier ReentrancyGuard prêt à l'emploi que tout développeur Solidity devrait utiliser.

Le langage Move (utilisé par Aptos et Sui) élimine la reentrancy par construction. Le modèle de propriété des ressources en Move empêche un contrat de rappeler un autre contrat qui détient encore des ressources en cours de modification. C'est un avantage architectural que Solidity n'a pas.

Comment évaluer le risque en tant qu'investisseur

Avant de mettre des fonds dans un protocole DeFi, vérifiez s'il a été audité par une firme reconnue (Trail of Bits, OpenZeppelin, Consensys Diligence, Halborn). Lisez le rapport d'audit s'il est public. Cherchez spécifiquement les mentions de reentrancy dans les findings.

Vérifiez que le code est open source. Un protocole qui refuse de publier son code sur GitHub devrait vous alerter. Si le code est vérifié sur Etherscan, c'est un bon signe, mais ça ne remplace pas un audit.

Regardez la TVL (Total Value Locked) et depuis combien de temps le protocole est en production. Un protocole avec 500 millions de TVL qui tourne depuis 2 ans sans incident est statistiquement plus sûr qu'un protocole lancé la semaine dernière, même avec un audit. Le temps est le meilleur audit.

Et comme toujours, ne mettez jamais plus que ce que vous pouvez perdre. Les flash loans et les reentrancy peuvent vider un protocole en une seule transaction. Les fonds déposés dans un smart contract vulnérable peuvent disparaître en quelques secondes.

À lire aussi