Yearn 900万ドルの盗難攻撃分析
- 核心观点:Yearn协议遭多阶段组合攻击损失900万美元。
- 关键要素:
- 利用闪电贷撬动初始攻击资金。
- 组合利用精度丢失等三个核心漏洞。
- 最终实现无限铸造LP代币并掏空资金池。
- 市场影响:凸显DeFi协议组合漏洞风险,促进行业安全升级。
- 时效性标注:中期影响
序文
2025年12月1日、Yearnプロトコルは高度な多段階ハッキングを受け、約900万ドルの資産が失われました。この攻撃は単一のエクスプロイトによるものではなく、攻撃者はフラッシュローンを通じて資金を活用し、プロトコルの複数の論理的欠陥を悪用して流動性プールを徐々に操作し、最終的にyETH関連のLPトークンをほぼ無限に発行し、流動性プールを枯渇させました。
この攻撃は、フラッシュローンを初期の資金レバレッジとして利用し、複数のステップを経てプロトコルの防御を段階的に突破しました。攻撃の核となるプロセスは、資金準備、状態操作、無制限の発行、利益確定という4つの主要な段階に分けられます。各段階は相互に関連しており、プロトコル設計の論理的な脆弱性を的確に悪用しています。
テンセントへの攻撃:https://etherscan.io/tx/0x53fe7ef190c34d810c50fb66f0fc65a1ceedc10309cf4b4013d64042a0331156
テクニカル分析
まず、2つのフラッシュローンを通じて、BalancerとAaveからそれぞれwstETH、rETH、WETH、0xa35b_ETHx、rETH、wstETH、cbETHを借ります。


フラッシュローンのコールバック関数では、借りたETHがTornado.Cash: 100 ETHに預け入れられ、1100 ETHと混合されます。その後、Tornado.Cash: 100 ETHwithdraw関数を使用して、悪意のあるコントラクト0x3e8e7533dcf69c698Cf806C3DB22f7f10B9B0b97に100 ETHが引き出され、フォールバック関数が起動されます。


fallack関数では、下図の最後の交換が次のステップのremove_liquidityの値と同じであることがわかります。これまでのステップはすべて、フラッシュローンで取得した資産を、交換などの操作を通じて、大量のyETH加重ステーブルスワッププールLPトークンに交換し、その後の攻撃に備えるためのものだったと推測できます。

この時点で、コア攻撃プロセスが正式に開始されます。
1. まず、上記の取引所から取得したすべてのLPトークンは、yETH加重ステーブルスワッププールのremove_liquidity関数を通じて、シェア割り当てに従ってプール内の8つの原資産に変換され、破棄されます。
remove_liquidity 関数のロジックは次のように理解できます。プールに合計 10,000 個の LP トークンがあり、416.37 個の LP トークンを破棄すると、割合は 416.37 / 10,000 = 4.16% になります。
次に、各資産について、プールに 1,000 wstETH (仮想残高 prev_vb) があると仮定すると、引き出すことができる仮想残高は、dvb = 1,000 * 416.37 / 10,000 = 41.637 wstETH となり、これを為替レートで割って実際のトークン数に変換します。
2. 次に、`add_liquidity` を繰り返し呼び出すことで、片側プールに資金が注入されます。`_amounts` パラメータは合計8つあり、それぞれが注入される異なる資産の量に対応しています。前のループでは、index3[rETHトークン]、index6[wOETHトークン]、index7[mETHトークン]がすべて0と入力されていました。これは、これらの3つのトークンが流動性が追加されるたびに追加されなかったことを意味します。

上記の方法で一方的な資産を注入し、プールからトークンを引き出すことで、プール内の rETH、w0 ETH、mETH と他のトークン間の数量格差が人為的に広がります。
3. 次に、大量の rETH トークンが一方的に注入され、その後、_amount=0 で、removing_liquidity という重要なステップが実行されます。

なぜ0トークンしか出金できないのでしょうか?これは、 remove_liquidityの内部実装によるものです。

remove_liquidity 関数は、0 値のトランザクションに対してショートサーキット処理を実行せず、完全な vb_prod 計算ループを実行し、上記の人工的に作成されたプール内のトークン数量の差に基づいてグローバルの packed_pool_vb 状態を計算して更新しました。
4. 次に、update_rates関数を呼び出して、index6[wOETH]のプール比率のみを更新し、最後にremove_liquidity関数を呼び出してプールからトークンを抽出します。この時点で、プール内のW0 ETHの量はほぼなくなりました。

5. 同様に、プール内のindex6[w0 ETH]とindex7[mETH]のシェアを同様の方法で絞り込みます。index6の最初の2回の更新後、remove_liquidityを使用してトークンを引き出しますが、index7[mETH]は更新されただけで、まだ引き出されていないことに注意してください。

大量のトークンを片側プールに追加し、プールからすべてのトークンを継続的に抽出することで、プール内の W0 ETH と mETH の比率はほぼゼロになりました。
この時点で、新たな悪意あるコントラクト0xADbE952eBB9b3e247261d2E3b96835f00f721f8Eが作成され、すべてのトークンがこのコントラクトに転送されます。前のステップで一方的にrETHを追加することで得られたLPトークンは、基礎となるトークンに変換されず、新たな悪意あるコントラクトにも転送されていることに注意してください。

前回の攻撃では、update_ratesでindex7[mETH]を更新しましたが、引き出されなかったトークンはremove_liquidityを呼び出すことでここで引き出されます。この時点では、プールにおけるindex6[w0 ETH]のシェアは非常に小さく、index7[mETH]のシェアはさらに小さくなっています。

この時点で、プール内のトークン比率は著しく不均衡でした。攻撃者は再びadd_liquidityを呼び出して流動性を追加し、[1, 1, 1, 1, 1, 1, 1, 9]という比率で大量のLPトークンを取得しました。

この時点で、攻撃者は大量のLPトークンを入手しており、交換や償還といった手段を用いて利益を上げ、フラッシュローンの手数料を返済します。

攻撃レビュー
この攻撃は、pool.vy 契約における 3 つの主要な脆弱性(精度損失、利回り剥奪、ゼロ供給初期化) を攻撃者が悪用した、複雑な多段階の複合攻撃でした。
第一段階:極端な不均衡を生み出す
- 操作:攻撃者は add_liquidity を繰り返し呼び出しますが、インデックス 3 (rETH)、インデックス 6 (wOETH)、およびインデックス 7 (mETH) を意図的に回避します。
- 目的:プール内の資産比率の不均衡を人為的に作り出すこと。
- 重要なステップ:次に、大量の rETH を一方的に注入します。
- 結果:これにより、rETH と他の資産 (特に wOETH と mETH) 間の量的ギャップが大幅に拡大し、精度が失われる数学的条件が生じます。
フェーズ2: エラーのトリガーとロック
- 操作: remove_liquidity(_amount=0) を呼び出します。
- 原理:
`remove_liquidity` は金額が 0 の場合にショートサーキットを実行しません。
資金を送金しなくても、契約は vb_prod の完全な計算ループを実行します。
極端な重量の不均衡がある場合、_pow_down 関数は重大なフロア エラーを生成します。
コントラクトは、vb_prod の小さい方のエラー値をグローバル状態 packed_pool_vb に書き込みます。
- 本質的には、これは「ゼロコスト」状態攻撃であり、攻撃者はコストをかけずにプールの帳簿価格を変更することに成功します。
第3段階:収益剥奪と市場シェアの搾取
- 操作:
update_rates([6])(wOETH為替レートを更新する)。
remove_liquidity (資産の削除)。
update_rates([7]) (mETHの交換レートを更新する)
- 原理:
`update_rates` は `_update_supply` をトリガーします。`vb_prod` が以前に悪意を持って抑制されていたため、システムはプールの価値が縮小したと誤って判断し、アカウントのバランスを取るためにステーキング契約によって保持されていたLPトークンを破棄しました。
攻撃者は、為替レートの更新の前後にremove_liquidityを悪用して裁定取引を行い、wOETHとmETHのプールを徐々に枯渇させました。
- 結果:多数のステーキング契約が破壊され、攻撃者の LP のシェアが受動的に増加し、プールの総供給量が 0 になりました。
第4段階:供給ゼロ、無制限の鋳造
- 既存の状態:上記の操作後、プールは空になり、総供給量は 0 に近くなり、wOETH と mETH の残高は非常に低くなります。
- 操作: add_liquidity、パラメータは_amounts=[1, 1, 1, 1, 1, 1, 9]。
- 原理:
prev_supply ≈ 0 の場合、_calc_supply の反復式は、非常に小さい値 (1 wei、9 wei) を処理するときに失敗します。
契約では、鋳造される LP トークンの数が天文学的な数値であると誤って計算されました。
- 結果:攻撃者は235,443 ... yETH LP トークンを無から入手しました。
要約
Yearn攻撃は、DeFiプロトコルにおけるエッジシナリオでのロジック検証、数値計算における精度管理、そして複数の脆弱性の組み合わせに対するリスク防止に関して、複数の欠陥を露呈しました。フラッシュローンをツールとして、脆弱性の組み合わせの悪用を中核として、資金難読化を隠れ蓑として用いる攻撃者の攻撃パターンは、DeFi攻撃の専門化と複雑化という現在の傾向を浮き彫りにしています。この攻撃から得られた重要な教訓は次のとおりです。第一に、プロトコルは「ゼロ額」や「極端な不均衡」といったエッジシナリオにおけるロジック検証を強化し、短絡処理の欠如による状態改ざんリスクを回避する必要があります。第二に、数値計算における極端な比率での精度低下に対処する必要があり、`_pow_down`などの主要関数の計算ロジックを最適化する必要があります。Balancerプロトコルは以前、精度低下によるセキュリティインシデントを経験しており、これは教訓となっています。第三に、高頻度の一方的な流動性注入や異常な為替レート更新といった疑わしい操作に対して警告を発するための多次元リスク監視システムを構築する必要があります。 DeFi業界全体にとって、今回の事件は、プロトコルのセキュリティには、個々の脆弱性を修正するだけでなく、プロセス全体の観点から複数の脆弱性を組み合わせた攻撃を防ぐことが必要であり、攻撃者の資金の流れの追跡と傍受を強化して、業界全体のセキュリティ保護能力を向上させる必要があることを改めて証明しました。


