
Yearn Finance 攻击事件分析
概述
2023 年 4 月 13 日,Yearn Finance 遭到黑客攻击,导致损失了大约 1000 万美元。本文将分析攻击过程以及漏洞产生的原因。
攻击分析
这是一笔攻击交易:
https://etherscan.io/tx/0xd55e43c1602b28d4fd4667ee445d570c8f298f5401cf04e62ec329759ecda95d
攻击者从 Balancer 发起了闪电贷,借了 500 万 DAI、500 万 USDC 和 200 万 USDT:

然后,在 Curve 上,攻击者将 500 万 DAI 兑换成了 695,000 USDT,并将 350 万 USDC 兑换成 151 USDT:

攻击者调用 IEarnAPRWithPool 的 recommend 函数来检查当前的 APR。此时,只有 Aave 的 APR 不等于 0:

接下来,攻击者将 800,000 USDT 转移到了攻击合约 0x9fcc1409b56cf235d9cdbbb86b6ad5089fa0eb0f 中。在该合约中,攻击者多次调用了 Aave:Lending Pool V1 的 repay 函数,帮助其他人偿还债务,以使 Aave 的 APR 等于 0:

攻击者调用了 yUSDT 的 deposit 函数,抵押了 900,000 USDT,并获得了 820,000 yUSDT:

接下来,攻击者调用了 bZx iUSDC 的 mint 函数,使用 156,000 USDC 铸造了 152,000 bZx iUSDC,并将其转移到了 Yearn yUSDT:

攻击者调用 Yearn:yUSDT 的 withdraw 函数,将 820,000 yUSDT 兑换成 1,030,000 USDT。此时,合约中只剩下攻击者转移的 bZx iUSDC:

接下来,攻击者调用 Yearn:yUSDT 的 rebalance 函数,销毁 bZx iUSDC:

然后,攻击者向 yUSDT 合约转移了 1/e6 个 USDT,并调用了 deposit 函数,抵押了 10,000 USDT,获得了 1,252,660,242,850,000 yUSDT:

然后,在 Curve 上,攻击者将 70,000 yUSDT 兑换成 5,990,000 yDAI,将 4 亿 yUSDT 兑换成 4,490,000 yUSDC,将 1,240,133,244,352,200 yUSDT 兑换成 1,360,000 yTUSD:

然后在 yearn: yDAI 和 yearn: yUSDC 中分别调用 withdraw ,提取 678 万 个 DAI 和 562w 万个 USDC,并归还闪电贷:

漏洞分析
这次攻击中最关键的一点,是攻击者使用 100,000 USDT 铸造了 1,252,660,242,850,000 个 yUSDT。查看 deposit 函数的实现:

可以看到 share 的数量和变量 pool 相关,pool 越小,share 越大,而 pool 的值由 _calcPoolValueInToken 获得:

攻击者在调用 rebalance 函数 后,合约中只存在了 USDC,但是 _balance() 获取的是 USDT 的余额,USDC 的余额并不计入其中,因此此时的 pool 为 1(攻击者转入的) :



这里显然是项目方的配置错误,yUSDT 合约中 应当都是 USDT 类的代币,但是其 fulcrum 变量却是 USDC 相关的 bZx IUSDC 代币,因此 yUSDT 中的 USDC 不计入 balance 中:


攻击者为什么能调用 rebalance 函数来 burn 掉 bZx iUSDC 代币呢?查看 rebalance 函数的实现:



可以看到在 _withdrawFulcrum() 中会 存在 redeem 和 burn 操作,因此我们需要让 "newProvider != provider" 成立 , 其中 recommend() 的实现:

攻击者通过控制 IIEarnManager(apr).recommend(token) 的返回值,使其为都为 0 来操控 newProvider:

如何让其都为 0 呢,该函数的返回值和计算出的各个 DeFi 中的 APR 相关,由于 Compound,bZx,dydx 中没有池子,因此只需要控制 Aave ( Aave: Lending Pool Core V1) 即可:

要使其值返回为 0 ,需要让 apr.calculateInterestRates 函数的第一个返回值为 0 :

即让 currentLiquidityRate 为 0 ,该值和 _totalBorrowsStable、_totalBorrowsVariable 相关,当这两个个值都为 0 时,currentLiquidityRate 为 0:


_totalBorrowsVariable 为 0 ,即 Aave: Lending Pool Core V1 此时没有人存在债务,为了达成这个条件,攻击者将池中所有人的债务进行了 repay :

最后,攻击者让 _totalBorrowsVariable 变为 0,所以它能够调用 rebalance 函数 burn 掉 bZx iUSDC 代币:

总结
此次 Yearn Finance 攻击事件的根本原因是项目方合约的配置错误。攻击者通过一系列精妙的手法利用了该漏洞,最终获利大约 1000 万美元。
TechFlow 공식 커뮤니티에 오신 것을 환영합니다
Telegram 구독 그룹:https://t.me/TechFlowDaily
트위터 공식 계정:https://x.com/TechFlowPost
트위터 영어 계정:https://x.com/BlockFlow_News














