900 万美元被盗:Yearn yETH 池漏洞分析
背景
2025 年 12 月 1 日,老牌去中心化收益聚合协议 Yearn 遭到攻击,损失约 900 万美元。以下是慢雾安全团队针对此次攻击事件的具体分析:

根本原因
在 Yearn 的 yETH Weighted Stableswap Pool 合约中,计算供应量的函数逻辑(_calc_supply) 由于采用了不安全的数学运算方式,允许计算时出现溢出和舍入的情况,导致在计算新的供应量和虚拟余额乘积时出现严重偏差,最终造成攻击者可以将流动性操控到特定的值后铸造出超出预期数量的 LP 代币获利。
前置知识
yETH 是一个由各种以太坊流动性质押衍生品(LST) 组成的自动做市商池(AMM),用户可以将 LST 存入池中提供流动性并获得 yETH 代币。每种 LST 资产都有一个对应的汇率提供商,资金池中的资产余额乘以利率被称为“虚拟余额”(vb),其计算公式如下:

合约会跟踪一个变量 D,该变量代表资金池在完全平衡状态下的总 LP 供应量。任何对 D 值的增减操作都会相应地增发或销毁等量的 LP 代币(yETH)。这种机制确保了 1:1 的锚定,其计算方式如下:

其中 σ 为各资产虚拟余额的总和(vb_sum), π 为虚拟余额的总乘积(vb_prod)。虚拟余额的总乘积(vb_prod) 会在计算新的供应量时同步更新,计算方式如下:

攻击分析
攻击交易:0x53fe7ef190c34d810c50fb66f0fc65a1ceedc10309cf4b4013d64042a0331156
1. 攻击者首先通过闪电贷借出大量 LST 资产,包括(wstETH、rETH、WETH、0xa35b_ETHx、rETH、wstETH 和 cbETH)。

注意其中一部分的 WETH 被换成 ETH 后存入 Tornado Cash,之后再从 Tornado Cash 提款触发攻击合约的 fallback 函数执行攻击:


2. 在攻击合约中,先通过调用 yETH pool 合约的 update_rates 函数为其中六种 LST 资产更新对应的利率并平衡流动性,紧接着用 800 枚 WETH 兑换出 LP 代币,即 yETH。

3. 之后攻击者进行了连续 5 次的移除后再添加流动性的操作,其中移除流动性时会为燃烧 yETH 并按权重为攻击者赎回池中的 8 种 LST 资产;而在添加流动性时却只会添加其中几个资产的流动性,并不会添加cbETH、 wOETH 和 mETH 的流动性。

而在第五次添加流动性后,虚拟余额的总乘积(vb_prod) 会被更新为 0,供应量 D 值会被更新为与虚拟余额总和(vb_sum) 接近相等的值,比正常预期的要偏大。
那么这是为什么呢?让我们跟进到 _calc_supply 函数中进行分析:

该函数用循环的方式迭代计算新的供应量,而在每次循环中又会再循环 8 次进行迭代计算出新的乘积用于下一次循环计算供应量,其中每次循环中计算供应量的算法可以简化为一个式子:s’ = (l - s * r) / d,计算乘积的算法可以简化为:r’ = r * (s’ / s)^8。
通过用 foundry 模拟攻击交易步骤后的结果,我们可以得知当前供应量(_supply) 约为 2.51e21, 虚拟余额总和(_vb_sum) 约为 1.0903e22,虚拟余额乘积(_vb_prod) 约为 3.5e15, _amplification 为定值 4.5e20。
在第一次循环时,s’ = ((4.5e20 * 1.0903e22) - 2.51e21 * 3.5e15) / (4.5e20 - 1e18) ≈ 1.0927e22,r’ = 3.5e15 * (1.09e22 / 2.5e21)^8 ≈ 4.57e20。在第二次循环时,s’ = ((4.5e20 * 1.0903e22) - 1.0927e22 * 4.57e20) / (4.5e20 - 1e18) ≈ 1.94e18。此处触发本次攻击的核心漏洞,由于在计算新的供应量时分子采用的是 unsafe_sub 函数将 l 和 s * r 的值进行相减,而该函数并不会检查是否溢出,从而导致最终的计算出来的值被下溢出成 1.94e18,远远小于之前的供应量。
而在计算新的乘积 r’ 时由于此时的 s’ 远小于 s,导致计算出来新的虚拟余额总乘积(vb_prod) 会因为向下舍入等于 0。
因为新的乘积 r’ 被舍入到 0,所以后续所有循环中供应量计算出来的值都会恒等于一个定值:s’ = ((4.5e20 * 1.0903e22) - 0) / (4.5e20 - 1e18) ≈ 1.0927e22。最终计算出来的 LP 总供应量(约等于 1.0927e22 )以及虚拟余额乘积(等于 0)会被更新到合约存储中,并为攻击者铸造偏差后的 yETH。
紧接着攻击者在虚拟余额乘积和总供应量被操控的情况下,再次用 cbETH 添加单边流动性,使得这两次添加流动性为攻击者铸造出了超出预期数量的 yETH。

4. 随后攻击者通过移除流动性(数量为 0)的方式来恢复虚拟余额的总乘积(vb_prod) 为非零值,首先让我们跟进到 remove_liquidity 函数中:

我们可以看到在移除流动性时即使传入的 LP 数量为 0,也会重新计算虚拟余额的总乘积并进行更新,计算公式为:

其中 D 为总供应量的值,wi 为每个资产各自的权重,vbi 为每个资产减去赎回数量后新的虚拟余额,由于传入的 LP 数量为 0 所以此处依旧等于之前的虚拟余额,最终计算出来后的虚拟余额总乘积(vb_prod) 的值约为 9.09e19,虚拟余额总和与总供应量没变,依旧等于上一次单边添加流动性后的值(vb_sum ≈ 1.0926e22, supply ≈ 1.095e22)。
紧接着调用 update_rates 函数更新 wOETH 资产的兑换利率,跟进到 update_rates 函数中:

首先会先从利率提供者地址获取 wOETH 最新的兑换利率,如果利率有变化的话会用这个新的兑换利率更新虚拟余额总乘积(vb_prod)、资产对应的虚拟余额、虚拟余额总和,之后就会调用 _update_supply 函数更新 LP 总供应量。这也就说明了为什么先前循环移除/添加流动性时不为 wOETH 添加流动性,因为如果利率有变化的话会在在添加流动性时就调用 _update_rates 函数将利率进行更新。而只有在这一步中才是需要利用一个利率差去将操控的 vb_prod 和供应量恢复。


在其中依旧会调用 _calc _supply 函数计算新的供应量和虚拟余额总乘积,由于虚拟余额已经经过上面的操作恢复为一个非零的值,所以最终计算出来的供应量要小于先前的供应量,并为合约燃烧掉这部分的 yETH。

至此,虚拟余额总乘积(vb_prod)、虚拟余额总和(vb_sum) 和总供应量的值被更新为以下值:vb_prod ≈ 4.34e19、vb_sum ≈ 1.0926e22、supply ≈ 9.98e21。
5. 之后攻击者将上面两次添加流动性铸造的 LP 代币通过 remove_liquidity 函数全部赎回,由于这部分 LP 是在第三步放大操控供应量时所铸造的,而赎回时是在第四步恢复并减少供应量时赎回的,所以攻击者可以赎回比正常预期更多的代币,以此来削减池子中的资产余额和供应量。

攻击者用相同的手法再次进行了循环操控,以此逐渐削减 LP 的供应量和虚拟余额。注意其中调用了 OETHVaultCore 合约的 rebase 函数,其目的是为了让 wOETH 的兑换利率进行更新,以此让 update_rates 函数能再次获取到新的利率并恢复虚拟总和乘积和供应量的值。
在最后一次操控完后,攻击者在移除流动性之后能直接将池子中的所有资产给清空,使得虚拟余额总乘积(vb_prod)、虚拟余额总和(vb_sum) 以及总供应量的值全部为 0。

6. 在池子被完全清空的情况下,攻击者开始向空池中添加粉尘流动性:

由于底层 8 种 LSD 资产的利率都接近 1e18,那么在添加粉尘数量的代币后,每个资产的虚拟余额会直接等于代币的数量。在供应量为 0 的情况下,添加流动性时会调用内部的 _calc_vb_prod_sum 函数重新计算虚拟余额总乘积(vb_prod)、虚拟余额总和以及当前总供应量的值:


紧接着会将这些值传入 _calc_supply 函数中计算新的供应量,即给攻击者铸造的 LP 数量,在 _calc_supply 函数中某次迭代循环计算 supply 时同样会因为 unsafe_mul 溢出导致计算出来后的 supply 为一个数量级巨大的值(2.354e56),并为攻击者铸造对应数量的 yETH 代币。

7. 最后攻击者通过 AMM 将 yETH 直接卖出兑换,并归还闪电贷获利。
MistTrack 分析
据链上追踪 & 反洗钱工具 MistTrack 分析,攻击者在本次事件中获利约 900 万美元,初始资金来自 Railgun 转入的少量 ETH。

攻击者发起攻击后,先是将 1,100 ETH 转入了 Tornado Cash,其中 100 ETH 被提取用于进一步利用:

剩余约 600 万美元的获利资金(包括 128 ETH、48.96 cbETH、203.55 rETH、742.63 frxETH、857.48 pxETH、167.67 stETH)则被集中转移到地址 0xa80d3f2022f6bfd0b260bf16d72cad025440c822:

(https://etherscan.io/address/0xa80d3f2022f6bfd0b260bf16d72cad025440c822)
值得注意的是,后续 Yearn 通过销毁黑客持有的 pxETH,成功追回了 240 万美元资金, 857.48 pxETH 已重新铸造并返还至 Redacted Cartel 多签钱包。

(https://etherscan.io/tx/0x0e83bb95bb9d05fb81213b2fad11c01ea671796752e8770b09935f7052691c35)
MistTrack 已对相关地址进行标记,并将持续监控资金异动。
总结
本次攻击的核心在于攻击者利用了 Yearn 协议的 yETH Weighted Stableswap Pool 合约在添加流动性时计算 LP 供应量的实现逻辑中存在的不安全的数学运算方式所导致的溢出和舍入缺陷。通过精心构造特定的虚拟余额和供应量数值放大这个缺陷带来的误差,使得攻击者可以铸造出巨额的 LP 代币来获利。慢雾安全团队建议项目方/审计人员在面对类似场景时应当加强对极端场景和边界条件的覆盖测试,在计算核心变量时采用安全的数学运算方式并进行检查,以防止溢出等严重漏洞影响协议安全。
| 感动 | 同情 | 无聊 | 愤怒 | 搞笑 | 难过 | 高兴 | 路过 |
- 上一篇:加密支付公司 Truther 宣布推出非托管 US…
- 下一篇:没有了!
相关文章
-
没有相关内容

会员登录