Yearn被盜$9M美金攻擊分析
- 核心观点:Yearn协议遭多阶段组合攻击损失900万美元。
- 关键要素:
- 利用闪电贷撬动初始攻击资金。
- 组合利用精度丢失等三个核心漏洞。
- 最终实现无限铸造LP代币并掏空资金池。
- 市场影响:凸显DeFi协议组合漏洞风险,促进行业安全升级。
- 时效性标注:中期影响
前言
2025 年12 月1 日,Yearn 協議遭遇了一起精心設計的多階段複雜駭客攻擊,最終導致約900 萬美元資產損失。攻擊並非單一漏洞利用,而是攻擊者透過閃電貸撬動資金、利用協議多重邏輯缺陷逐步操控資金池狀態,最終實現近乎無限鑄造yETH 相關LP 代幣並耗盡資金池的惡性事件。
本次攻擊以閃電貸為初始資金槓桿,透過多步驟操作層層突破協議防護,核心攻擊流程可分為資金籌備、狀態操控、無限鑄幣及獲利了結四個關鍵階段,各環節環環相扣,精準利用了協議設計中的邏輯漏洞。
攻擊tx : https://etherscan.io/tx/0x53fe7ef190c34d810c50fb66f0fc65a1ceedc10309cf4b4013d64042a0331156
技術分析
首先透過2 個閃電貸分別從Balancer 和Aave 中藉出wstETH, rETH, WETH 和0xa35b_ETHx, rETH, wstETH, cbETH。


在閃電貸回呼函數中將借出的ETH 用1100 ETH 存入Tornado.Cash: 100 ETH 混幣,隨後藉助Tornado.Cash: 100 ETHwithdraw 函數取款100 ETH 到惡意合約0x3e8e7533dcf69c698Cf並觸發其fallback 函數。


在fallack 函數中,可以看到下圖最後一筆exchange 與下一步remove_liquidity 數值一致,可以推斷前面的步驟都是為了將閃電貸得到的資產都透過exchange 等操作兌換成大量的yETH weighted stableswap pool 的LP 代幣,為後續攻擊做準備。

至此,核心的攻擊流程正式開始。
1.首先將上述exchange 得到的LP 代幣透過yETH weighted stableswap pool 的remove_liquidity 函數全部銷毀轉化成按份額分配的8 種該pool 中的底層資產。
這個remove_liquidity 函數的邏輯可以理解為假設池子總共有10,000 個LP Token,你銷毀了416.37 個LP Token,你的比例就是:416.37 / 10,000 = 4.16%。
然後對於每一種資產,假設池子裡有1,000 個wstETH(虛擬餘額prev_vb)。你能取走的虛擬餘額:dvb = 1,000 * 416.37 / 10,000 = 41.637 wstETH,然後再除以匯率rate 轉換為實際代幣數量。
2.其次透過重複呼叫add_liquidity 類似注入單邊池子,一共有8 個_amounts 參數分別對應要注入的不同資產的數量。觀察到前面的幾個循環index3[rETH 代幣]、index6[wOETH 代幣]和index7[mETH 代幣]都輸入0,即每次加流動性都不加這3 個代幣。

透過如上方式註入單邊資產並提取池中代幣的方式人為地擴大池子中rETH、w0 ETH 和mETH 和其他代幣的數量差距。
3.緊接著單邊注入巨量的rETH 代幣,而後關鍵的一步remove_liquidity,但是輸入_amount=0。

為什麼可以提取0 個代幣?透過remove_liquidity的內部實作。

remove_liquidity 函數沒有對0⾦額進⾏短路處理,依然執⾏了完整的vb_prod 計算循環,並且是基於上述人為製造的池子中代幣數量差距計算並更新全局packed_pool_vb 狀態。
4.而後呼叫update_rates 函數只更新index6[wOETH]的池子比例,最後再呼叫remove_liquidity 提取池中代幣,此時該pool 中的W0 ETH 數量已經所剩無幾。

5.同樣的透過類似的方式榨乾pool 中index6[w0 ETH]和index7[mETH]的份額,注意這裡前兩次更新index6 後馬上remove_liquidity 提取代幣,而最後一個index7[mETH]目前只做了更新還未提取。

至此透過上述加巨量單邊池並不斷的提取所有池子代幣的方式,pool 中W0 ETH 和mETH 的比例已經近乎為0。
此時創建新的惡意合約0xADbE952eBB9b3e247261d2E3b96835f00f721f8E ,並將所有的代幣轉到該合約。注意在這之前的上一步驟中單邊加rETH 獲得的LP 代幣並沒有兌換成底層代幣,而是也轉移到了新的惡意合約。

前面的攻擊操作在update_rates 更新了index7[mETH]而未做提取的代幣在這裡通過調用remove_liquidity 提取,此時pool 中index6[w0 ETH]佔比份額很小,index7[mETH]的佔比份額更小。

此時pool 中代幣比例已嚴重失調,攻擊者再次調用add_liquidity 添加流動性,以[1, 1, 1, 1, 1, 1, 1, 9]的比例取得巨量LP 代幣。

到這裡攻擊者已經獲得大額LP 代幣,後續便是透過exchange、redeem 等方式獲利了結並償還閃電貸的費用。

攻擊複盤
這次攻擊是一起複雜的多階段組合攻擊,攻擊者利用了pool.vy 合約中的三個核心漏洞:精度遺失(Precision Loss)、收益剝離(Yield Stripping) 和零供應初始化(Zero Supply Initialization)。
階段一:製造極端失衡
- 操作:攻擊者重複呼叫add_liquidity,但刻意避開index 3 (rETH), index 6 (wOETH), index 7 (mETH)。
- 目的:人為製造池子中資產比例的失衡。
- 關鍵步驟:而後單邊注入巨量rETH。
- 後果:極度拉大rETH 與其他資產(特別是wOETH 和mETH)的數量差距,為精度損失創造數學條件。
階段二:觸發並鎖定誤差
- 操作:呼叫remove_liquidity(_amount=0)。
- 原理:
remove_liquidity 未對0 金額進行短路處理。
即使不轉賬,合約仍執行vb_prod 的完整計算循環。
在極端權重失衡下,_pow_down 函數產生顯著的向下取整誤差。
合約將這個錯誤偏小的vb_prod 寫入全域狀態packed_pool_vb。
- 本質:這是一個「零成本」的狀態攻擊,攻擊者未付出任何代價就成功篡改了池子的帳面價值。
階段三:收益剝離與份額榨取
- 操作:
update_rates([6]) (更新wOETH 匯率)。
remove_liquidity (提取資產)。
update_rates([7]) (更新mETH 匯率)。
- 原理:
update_rates 會觸發_update_supply。由於vb_prod 先前被惡意壓低,系統誤判池子價值縮水,從而銷毀Staking 合約持有的LP 代幣來平帳。
攻擊者利用remove_liquidity 在匯率更新前後進行套利,逐步榨乾池子中wOETH 和mETH 的份額。
- 結果: Staking 合約的份額被大量銷毀,攻擊者手中的LP 份額佔比被動提升,池子Total Supply 被推向0。
階段四:零供應無限鑄幣
- 前置狀態:經過上述操作,池子已被掏空,Total Supply 接近0,且wOETH 和mETH 餘額極低。
- 操作: add_liquidity,參數為_amounts=[1, 1, 1, 1, 1, 1, 1, 9]。
- 原理:
當prev_supply ≈ 0 時,_calc_supply 的迭代公式在處理極小數值(1 wei, 9 wei)時失效。
合約錯誤地計算出了天文數字的LP Token 鑄造量。
- 結果:攻擊者憑空獲得了235,443 ... 張yETH LP Token。
總結
Yearn 本次攻擊事件揭露了DeFi 協定在邊緣情境邏輯校驗、數值運算精確度控制及多漏洞組合風險防治上的多重不足。攻擊者以閃電貸為工具、漏洞組合利用為核心、資金混淆為掩護的攻擊模式,凸顯了當前DeFi 攻擊的專業化、複雜化趨勢。本攻擊的核心教訓包括:一是協議需強化對"零金額""極端失衡"等邊緣場景的邏輯校驗,避免因未短路處理產生狀態篡改風險;二是數值計算中需重視極端比例下的精度損失問題,優化_pow_down 等關鍵函數的計算邏輯,此前Balancer協議就曾因精準度損失出現安全事件,這已是前車之鑑;三是應建立多維度風險監控體系,對高頻單邊流動性注入、異常匯率更新等可疑操作進行預警。對於整個DeFi 產業而言,本次事件再次證明,協議安全不僅需要單一漏洞的修復,更需從全流程視角防範多漏洞組合利用的攻擊,同時加強對攻擊者資金流向的追蹤與攔截,提升產業整體安全防護能力。


