La récente vulnérabilité du pool Curve est différente des incidents de piratage de crypto-monnaie que nous avons vus auparavant, car cette fois, le problème n'est pas directement lié à la vulnérabilité du contrat intelligent lui-même, mais au compilateur sous-jacent du langage de programmation utilisé par Vyper.
Vyper est un langage de programmation orienté contrat intelligent qui adopte un style de type Python et est spécifiquement conçu pour interagir avec la machine virtuelle Ethereum (EVM).
L’impact de cette vulnérabilité est très grave et d’énormes pertes sont signalées chaque jour dans l’actualité. La situation semble sous contrôle, mais pas avant que des pirates informatiques aient volé plus de 70 millions de dollars. Selon l'évaluation postérieure de LlamaRisk, certains pools de projets DeFi ont également été attaqués par des pirates informatiques, notamment le pool pETH/ETH de PEGD qui a perdu 11 millions de dollars, le pool msETH/ETH de Metronome qui a perdu 3,4 millions de dollars et le pool alETH/ETH d'Alchemix qui a perdu 11 millions de dollars. 22,6 millions de dollars américains, tandis que le pool Curve DAO a perdu environ 24,7 millions de dollars américains.
Cette vulnérabilité est appelée erreur de réentrance et apparaît principalement dans certaines versions du langage de programmation Vyper, notamment v0.2.15, v0.2.16 et v0.3.0. Par conséquent, les projets utilisant ces versions spécifiques de Vyper peuvent devenir la cible d'attaques.
Qu’est-ce que la réentrée ?
Pour comprendre pourquoi cette vulnérabilité s'est produite, comprenons d'abord ce qu'est la réentrance et comment elle fonctionne.
Ce qu'on appelle la réentrée signifie qu'une fonction peut être interrompue pendant l'exécution et rappelée en toute sécurité avant la fin de l'appel précédent. Ce mécanisme est souvent utilisé dans des applications telles que la gestion des interruptions matérielles et la récursivité.
Pour qu’une fonction soit réentrante, elle doit remplir quelques conditions :
Premièrement, il ne peut pas utiliser de données globales et statiques. Il s'agit d'une convention qui signifie que lorsque la fonction est exécutée, ne vous appuyez pas sur des données globales ou des variables statiques. Car si une fonction est interrompue pendant l'exécution puis appelée à nouveau, des données globales et statiques peuvent être corrompues ou des informations peuvent être perdues.
Deuxièmement, il ne peut pas modifier son propre code. Peu importe le moment où une fonction est interrompue, elle doit pouvoir poursuivre son exécution de la même manière. Si une fonction modifie son propre code lors de l'exécution, une erreur peut se produire lors de son nouvel appel.
Enfin, il ne peut pas appeler d'autres fonctions qui ne sont pas réentrantes. Autrement dit, au sein d’une fonction réentrante, vous ne devez pas appeler d’autres fonctions qui ne sont peut-être pas réentrantes. Parce que si vous faites cela, cela peut entraîner des résultats imprévisibles, provoquant un crash du programme ou des erreurs.
C’est trop difficile à comprendre, nous n’entrerons donc pas dans les détails.
Comment a-t-il été exploité ?
Expliquons comment les attaques de réentrée ont conduit au vol de fonds et à la perte de 70 millions de dollars lors de l'attaque Curve.
Tout d’abord, nous savons qu’une attaque par réentrée fait référence à une méthode dans laquelle un contrat malveillant appelle à plusieurs reprises une fonction dans un contrat intelligent. Dans l'attaque Curve, cette fonction est utilisée pour retirer les liquidités de l'utilisateur dans le pool. Cette fonction présente un défaut dans la mesure où elle ne fait pas suffisamment de vérifications avant de mettre à jour le montant.
Examinons le processus spécifique de l'attaque :
Disons qu’un contrat intelligent vulnérable contient 10 éthers.
L’attaquant appelle la fonction de dépôt et dépose 1 Ethereum.
L’attaquant appelle alors la fonction de retrait et se prépare à retirer 1 Ethereum. Dans cette fonction, il vérifie si l’attaquant a 1 Ethereum sur son compte.
Cependant, cette fonction ne met pas à jour le solde du contrat avant de transférer 1 Ether sur le compte de l'attaquant. Cela signifie que le contrat pense toujours qu'il y a 10 éthers à l'intérieur.
L’attaquant appelle à nouveau la fonction de retrait (réentrée) et se prépare à retirer à nouveau 1 Ethereum.
Parce que le contrat pense toujours qu'il y a 10 éthers à l'intérieur, il transférera à nouveau 1 éther à l'attaquant.
Ce processus se répète jusqu'à ce qu'il n'y ait plus de liquidité dans le contrat.
De cette façon, l'attaquant peut appeler à plusieurs reprises la fonction de retrait et transférer presque toutes les liquidités du contrat sur son propre compte, provoquant ainsi le vol des fonds du contrat.
Cette vulnérabilité est très bénéfique pour l'attaquant car le montant du contrat n'est pas mis à jour en temps réel, ce qui permet à l'attaquant de répéter l'attaque jusqu'à ce qu'il n'y ait plus de fonds disponibles pour le retrait dans le contrat. #Crv