
Analyse de l'incident de sécurité de Yearn Finance
TechFlow SélectionTechFlow Sélection

Analyse de l'incident de sécurité de Yearn Finance
Cet article analysera le déroulement de l'attaque contre Yearn Finance ainsi que les causes de la vulnérabilité exploitée.
Résumé
Le 13 avril 2023, Yearn Finance a été victime d'une attaque entraînant une perte d'environ 10 millions de dollars. Cet article analyse le processus de l'attaque ainsi que les causes du vulnérabilité.
Analyse de l'attaque
Voici une transaction liée à l'attaque :
https://etherscan.io/tx/0xd55e43c1602b28d4fd4667ee445d570c8f298f5401cf04e62ec329759ecda95d
L'attaquant a initié un prêt flash depuis Balancer, empruntant 5 millions de DAI, 5 millions de USDC et 2 millions de USDT :

Ensuite, sur Curve, l'attaquant a échangé 5 millions de DAI contre 695 000 USDT, et 3,5 millions de USDC contre 151 USDT :

L'attaquant a appelé la fonction recommend du contrat IEarnAPRWithPool pour vérifier le taux annuel en cours (APR). À ce moment-là, seul l'APR d'Aave était différent de zéro :

Ensuite, l'attaquant a transféré 800 000 USDT vers son contrat malveillant 0x9fcc1409b56cf235d9cdbbb86b6ad5089fa0eb0f. Dans ce contrat, il a appelé plusieurs fois la fonction repay du contrat Aave : Lending Pool V1 afin de rembourser les dettes d'autres utilisateurs, ce qui a permis de ramener l'APR d'Aave à zéro :

L'attaquant a appelé la fonction deposit de yUSDT, déposant 900 000 USDT et recevant 820 000 yUSDT en retour :

Ensuite, l'attaquant a appelé la fonction mint du contrat bZx iUSDC, utilisant 156 000 USDC pour frapper 152 000 bZx iUSDC, qu'il a ensuite transférés vers le contrat Yearn yUSDT :

L'attaquant a appelé la fonction withdraw du contrat Yearn : yUSDT, échangeant 820 000 yUSDT contre 1 030 000 USDT. À ce stade, le contrat ne contenait plus que les jetons bZx iUSDC transférés par l'attaquant :

Ensuite, l'attaquant a appelé la fonction rebalance du contrat Yearn : yUSDT, ce qui a provoqué la destruction des jetons bZx iUSDC :

Puis, l'attaquant a transféré 1/10⁶ USDT vers le contrat yUSDT, puis a appelé la fonction deposit, déposant 10 000 USDT et obtenant 1 252 660 242 850 000 yUSDT :

Ensuite, sur Curve, l'attaquant a échangé 70 000 yUSDT contre 5 990 000 yDAI, 400 millions de yUSDT contre 4 490 000 yUSDC, et 1 240 133 244 352 200 yUSDT contre 1 360 000 yTUSD :

Enfin, via les contrats yearn : yDAI et yearn : yUSDC, l'attaquant a appelé la fonction withdraw pour retirer 6,78 millions de DAI et 5,62 millions de USDC, puis a remboursé le prêt flash :

Analyse de la vulnérabilité
Le point clé de cette attaque réside dans le fait que l'attaquant a pu frapper 1 252 660 242 850 000 yUSDT en déposant seulement 100 000 USDT. En examinant l'implémentation de la fonction deposit :

On constate que la quantité de « share » est liée à la variable pool : plus la valeur de pool est faible, plus le nombre de parts reçues est élevé. La valeur de pool provient de la fonction _calcPoolValueInToken :

Après avoir appelé la fonction rebalance, seuls des jetons USDC étaient présents dans le contrat. Toutefois, la fonction _balance() renvoie uniquement le solde en USDT, donc le solde en USDC n'est pas pris en compte. Par conséquent, la valeur de pool devient 1 (correspondant au seul jeton transféré par l'attaquant) :



Il s'agit clairement d'une erreur de configuration du projet : le contrat yUSDT aurait dû contenir uniquement des jetons de type USDT, mais sa variable fulcrum référençait le jeton bZx iUSDC lié au USDC. Ainsi, les soldes en USDC dans yUSDT ne sont pas intégrés au calcul du _balance :


Comment l'attaquant a-t-il pu appeler la fonction rebalance pour brûler les jetons bZx iUSDC ? Examinons l'implémentation de cette fonction :



Dans _withdrawFulcrum(), on observe des opérations de redeem et burn. Pour que cela se produise, il faut que la condition « newProvider != provider » soit vraie. Or, la fonction recommend() est implémentée comme suit :

L'attaquant a manipulé la valeur de retour de IIEarnManager(apr).recommend(token), en la forçant à zéro, afin de contrôler la valeur de newProvider :

Comment obtenir un retour nul ? Cette valeur dépend du calcul des APR dans divers protocoles DeFi. Comme Compound, bZx et dydx ne disposent pas de pools actifs, il suffit de contrôler Aave (Aave : Lending Pool Core V1) :

Pour que la fonction renvoie zéro, il faut que la première valeur retournée par apr.calculateInterestRates soit nulle :

Autrement dit, currentLiquidityRate doit être égal à zéro. Ce taux dépend de _totalBorrowsStable et _totalBorrowsVariable. Lorsque ces deux valeurs sont nulles, currentLiquidityRate devient également nul :


_totalBorrowsVariable étant nul signifie qu’aucun utilisateur n’a de dette dans Aave : Lending Pool Core V1. Pour atteindre cet état, l'attaquant a remboursé toutes les dettes existantes dans le pool :

Finalement, avec _totalBorrowsVariable ramené à zéro, l'attaquant a pu invoquer la fonction rebalance pour brûler les jetons bZx iUSDC :

Conclusion
La cause fondamentale de cette attaque contre Yearn Finance réside dans une erreur de configuration des contrats du projet. L'attaquant a exploité cette faille à travers une série de manipulations sophistiquées, réalisant finalement un gain d'environ 10 millions de dollars.
Bienvenue dans la communauté officielle TechFlow
Groupe Telegram :https://t.me/TechFlowDaily
Compte Twitter officiel :https://x.com/TechFlowPost
Compte Twitter anglais :https://x.com/BlockFlow_News














