作者:九九 & Lisa
編輯:77
背景
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) 接近相等的值,比正常預期的要偏大。
那麼這是爲什麼呢?讓我們跟進到 calcsupply 函數中進行分析:

該函數用循環的方式迭代計算新的供應量,而在每次循環中又會再循環 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)、資產對應的虛擬餘額、虛擬餘額總和,之後就會調用 updatesupply 函數更新 LP 總供應量。這也就說明了爲什麼先前循環移除/添加流動性時不爲 wOETH 添加流動性,因爲如果利率有變化的話會在在添加流動性時就調用 updaterates 函數將利率進行更新。而只有在這一步中才是需要利用一個利率差去將操控的 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 的情況下,添加流動性時會調用內部的 calcvb_prod_sum 函數重新計算虛擬餘額總乘積(vb_prod)、虛擬餘額總和以及當前總供應量的值:


緊接着會將這些值傳入 calcsupply 函數中計算新的供應量,即給攻擊者鑄造的 LP 數量,在 calcsupply 函數中某次迭代循環計算 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:

值得注意的是,後續 Yearn 通過銷燬黑客持有的 pxETH,成功追回了 240 萬美元資金, 857.48 pxETH 已重新鑄造並返還至 Redacted Cartel 多籤錢包。

MistTrack 已對相關地址進行標記,並將持續監控資金異動。
總結
本次攻擊的核心在於攻擊者利用了 Yearn 協議的 yETH Weighted Stableswap Pool 合約在添加流動性時計算 LP 供應量的實現邏輯中存在的不安全的數學運算方式所導致的溢出和舍入缺陷。通過精心構造特定的虛擬餘額和供應量數值放大這個缺陷帶來的誤差,使得攻擊者可以鑄造出鉅額的 LP 代幣來獲利。慢霧安全團隊建議項目方/審計人員在面對類似場景時應當加強對極端場景和邊界條件的覆蓋測試,在計算核心變量時採用安全的數學運算方式並進行檢查,以防止溢出等嚴重漏洞影響協議安全。
參考
https://github.com/banteg/yeth-exploit/blob/main/report.pdf


