過去1週間(2026/03/09~2026/03/15)、BlockSecは8件の攻撃インシデントを検出し分析しました。総推定損失額は約166万ドルです。以下の表にこれらのインシデントをまとめ、各ケースの詳細な分析は後述のサブセクションで提供します。
| 日付 | インシデント | タイプ | 推定損失額 |
|---|---|---|---|
| 2026/03/09 | EtherFreakers Incident | 不良なビジネスロジック | ~$25K |
| 2026/03/10 | Alkemi Incident | 不良なビジネスロジック | ~$89K |
| 2026/03/10 | MT Incident | 不良なビジネスロジック | ~$242K |
| 2026/03/11 | AAVE Liquidation Incident | 設定ミス | ~$1.01M |
| 2026/03/11 | Planet Finance Incident | 不良なビジネスロジック | ~$10K |
| 2026/03/12 | AM Incident | 不良なビジネスロジック | ~$131K |
| 2026/03/12 | DBXen Incident | 不良なビジネスロジック | ~$149K |
| 2026/03/15 | Goose Finance Incident | 不良なビジネスロジック | ~$8K |
EtherFreakers Incident
概要
2026年3月9日、Ethereum上のNFTゲームであるEtherFreakersが、不正確な二重計上により悪用され、約25,000ドルの損失が発生しました。ゲーム内の各NFTは引き出し可能なETH残高(「エナジー」と呼ばれる)を保持しています。ゲームのメカニズムとして、プレイヤーはattack()を使用して、あるNFTが別のNFTを捕獲し、ターゲットのエナジーを請求することができます。しかし、コントラクトはターゲットのNFTを決済する前に、ターゲットの残高を支払い、NFTを転送します。その後、転送フックが古い(支払い前の)データを読み取り、その一部をグローバル配当プールにフィードバックすることで、新しいETHの裏付けなしにプールが膨張します。攻撃者はこの捕獲メカニズムをループさせてグローバルインデックスを急増させ、その後、一連のNFTから膨張した残高を引き出しました。
背景
EtherFreakersは、各NFT(「Freaker」と呼ばれる)がenergyと呼ばれる引き出し可能なETH残高を保持する、オンチェーンNFTゲームです。システムは配当プールのように機能します。特定の操作が発生すると、ETHの fraction がすべてのFreakerに比例して配布されます。各Freakerの請求可能なETHは、グローバルアキュムレータfreakerIndexとトークンごとのシェアウェイトfortuneの組み合わせで追跡されます。
具体的には、会計式はenergyOf = basic + (freakerIndex - index) * fortuneとなります。freakerIndexは、_dissipateEnergyIntoPool(amount)が実行され、amountの80%がすべてのFreakerに、20%がクリエイターに配布されるときに増加します。charge()による直接入金は、freakerIndexに影響を与えることなくbasicを増加させるだけです。したがって、freakerIndexの増加は、常に実際のEtherがシステムに入ることで裏付けられるべきです。freakerIndexが対応するETH流入なしに増加した場合、Freakerはコントラクトが実際に保有しているよりも多くのEtherを償還できます。
脆弱性分析
根本原因は、EtherFreakコントラクト(0x3A27...c0f33)における実行順序の誤りです。捕獲が成功すると、attack()関数は次の順序でステップを実行します。
- 237行目:
targetCharge(ターゲットNFTの全エナジー)を直接ETH送金として防御者に支払います。エナジーは消費されました。 - 240行目:
_transfer(defender, capturer, targetId)を呼び出してNFTを移動させます。内部的に、_transfer()はERC-721フック_beforeTokenTransfer()を呼び出し、これはenergyOf(targetId)の0.1%で_dissipateEnergyIntoPool()を呼び出します。これは_dissipateEnergyIntoPool()への最初の呼び出しであり、ステップ5がまだ発生していないため、古い値が読み取られます。 - 241行目:
_dissipateEnergyIntoPool(sourceSpent)を明示的に呼び出します。これは2回目の呼び出しであり、通常のゲームロジックの一部です。 - 244-251行目:
sourceIdとtargetIdの両方についてenergyBalancesを更新します。
バグはステップ2にあります。energyBalances[targetId]がまだ更新されていないため、フックは支払い前の残高を依然として認識し、すでに消費されたエナジーの一部を配当プールに供給します。ステップ1の直接ETH支払いとステップ2のプール入力は、両方とも同じエナジーから引き出され、新しいETHの裏付けなしにfreakerIndexを膨張させます。


_dissipateEnergyIntoPool()が呼び出されるたびにfreakerIndexが増加します。

攻撃分析
以下の分析は、トランザクション0x89e24d...9abd2942に基づいています。
-
ステップ1:
1,700WETHを借入。 -
ステップ2:攻撃者が管理するアドレスの下で、2つの新しいFreaker、トークン
590とトークン591をミント。 -
ステップ3:ゲームの
attack(590, 591)関数を繰り返し呼び出し、成功した捕獲ブランチで実行を維持。 -
ステップ4:各成功後、同じペアが再利用できるようにトークン
591をヘルパーに戻して転送。 -
ステップ5:各成功ループは、システムによって実際に保存されたEtherを超えて
freakerIndexを膨張させる。 -
ステップ6:インデックスが十分高くなったら、以前に管理されていたFreakerのバッチを放出。トークンID
496から520はそれぞれ0.278052246002402082Etherで放出される。 -
ステップ7:排出されたEtherを
WETHにラップし、1,700WETHのフラッシュローンを返済し、約7.498WETHを利益として保持。
結論
根本原因はattack()の成功した捕獲フローにあります。EtherFreakersは、ターゲットトークンのエナジー状態が決済される前にtargetChargeを支払います。その後、_transfer()が_beforeTokenTransfer()をトリガーし、これが古い支払い前のenergyOf(targetId)を読み取り、その一部をプールに分配します。これにより、新しいEtherの裏付けなしにfreakerIndexが増加するため、同じターゲットエナジーが支払いとプール入力の両方としてカウントされます。これはリエンタランシーバグではなく、ビジネスロジックのインフレバグです。
将来、同様のリスクを軽減するために:
-
同じトランザクションがまだ決済中である間に、転送フック内で変更可能な状態から経済的価値を再計算することを避ける。
-
転送フックが状態変数を読み取る場合は、実行順序が結果に影響しないことを確認する(例:フックが実行される前、後ではなく、状態を決済する)。
Alkemi Incident
概要
2026年3月10日、Ethereum上のAlkemiプロトコルが悪用され、約89,000ドルの損失が発生しました。根本原因は会計エラーと不十分なビジネスロジックです。不十分な清算ロジックにより、誰でも同じトランザクション内で自身のポジションを清算して利益を得ることができます。さらに、会計エラーにより、清算中に攻撃者自身の担保の減額が上書きされ、攻撃者は意図されたコストを負担することなく清算報酬を得ることができます。
背景
Alkemiは貸付プロトコルです。借り手のポジションが担保不足になると、誰でもliquidateBorrow()を呼び出して借入金の一部を返済し、割引価格で担保を没収できます。過剰な清算を防ぐため、プロトコルはトランザクションあたりの返済可能額を次の3つの値の最小値に制限します。
- 借り手の現在の借入残高(
currentBorrowBalance_TargetUnderwaterAsset)。 - 清算割引を適用した後の借り手の担保がカバーできる最大返済額(
calculateDiscountedBorrowDenominatedCollateral())。 - アカウントを清算境界に戻すために必要な返済額(
calculateDiscountedRepayToEvenAmount())。これは市場がisSupportedの場合にのみチェックされます。


脆弱性分析
根本原因は、Alkemiプロトコル(0x4822...a888)における不十分なビジネスロジックと会計エラーです。currentBorrowBalance_TargetUnderwaterAssetは、借り手が未払いの負債を抱えている限り、必然的に0より大きくなります。また、calculateDiscountedBorrowDenominatedCollateral()が返す値も、借り手が担保を保有している限り必然的に0より大きくなるため、AlkemiEarnPublicプロトコルは、与えられたローンの清算可能性を判断するために、実質的にcalculateDiscountedRepayToEvenAmount()に依存しています。この関数では、清算する必要がある借入額は、accountShortfall_TargetUserと呼ばれる変数に基づいて計算されるべきです。
しかし、実際の実装では、関数は代わりにグローバル変数closeFactorMantissaを使用して、返済が許可される借入額の上限を計算し、その値を返します。その結果、攻撃者は借入を行い、同じトランザクション内で直ちに自身のポジションを清算することができます。
さらに、liquidateBorrow()関数では、清算者と借り手が同じアドレスである場合、変数supplyBalance_TargetCollateralAssetとsupplyBalance_LiquidatorCollateralAssetは同じストレージスロットを指します。その後、関数は同じ初期残高に基づいて「減少した残高」と「報酬対象残高」を別々に計算し、その後、それらを順番に同じストレージスロットに書き戻します。減少した残高が先に書き込まれ、報酬対象残高が後から書き込まれるため、減少効果は上書きされて失われ、報酬結果のみが残ります。これにより、攻撃者はさらに利益を増幅させることができます。
攻撃分析
以下の分析は、トランザクション0xa170...6d9dに基づいています。
-
ステップ1:攻撃者はBalancerから
51e18WETHのフラッシュローンを借入。 -
ステップ2:攻撃者は
51e18WETHをアンラップしてETHにし、Alkemiプロトコルに供給。 -
ステップ3:攻撃者はAlkemiから
39.5e18ETHを借入。 -
ステップ4:攻撃者は
39.5395e18ETHを使用して自身のポジションを清算。 -
ステップ5:攻撃者はAlkemiから
93.5e18ETHを引き出し。 -
ステップ6:攻撃者はフラッシュローンを返済し、
43.4e18ETHの利益を上げた。

結論
根本原因は、不十分な清算ロジックにより攻撃者が借入を行い、その後同じトランザクション内で自身のポジションを清算して利益を上げることができたこと、そして不正確な会計ロジックが攻撃者の利益をさらに増幅させたことです。
将来、同様のリスクを軽減するために:
- 借り手と清算者の残高更新について、プロトコルは一時的なメモリ変数に残高をコピーして別々に計算・書き戻すのではなく、ストレージ変数に直接操作すべきです。
MT Incident
概要
2026年3月10日、BNB Chain上のデフレショナリートークンであるMT Tokenが悪用され、約242,000ドルの損失が発生しました。根本原因は、不十分な取引制限ロジックと特別な転送条件の不整合な処理です。デフレフェーズ中、コントラクトはプール準備金が固定しきい値を超えると買い操作を制限します。しかし、コントラクトは正確な金額(例:2e17 MT)の転送を紹介者バインディングアクションとして扱い、攻撃者が初期トークンを取得するために買い制限を回避することを可能にします。さらに、制限ロジックは不完全なパス検出(isBuy)に依存しており、ペアからルーターへの間接的なスワップルートをカバーしておらず、ホワイトリストチェックは重要な検証をさらにショートカットします。攻撃者は制限や手数料をトリガーすることなくMTトークンを蓄積し、制御された流動性操作と取引を通じてpendingBurnAmountを操作し、トークン価格が人工的に膨張した異常な状態にプールを強制しました。
背景
MT Tokenは、BNB Chain上のデフレショナリートークンであり、組み込みの取引制限があります。デフレフェーズ中、コントラクトはプール内のMT準備金が21,000e18を超えると買い操作をブロックします。準備金がこのしきい値を下回ると、デフレフェーズが終了し、買いが再開されます。MT Tokenは紹介メカニズムも備えています。2e17 MTまたは1e17 MTの転送は、通常の取引ではなく、紹介者バインディングアクションとして扱われます。
脆弱性分析
根本原因は、MTコントラクト(0x037E...b449)における不十分な買い制限設計でした。通常の状態では、攻撃者は制限フェーズ中にシードキャピタルとしてMTを取得することはできません。しかし、コントラクトは正確に2e17 MTの転送を買いではなく紹介者バインディングアクションとして扱っており、これにより攻撃者は買い制限を回避して2e17 MTを購入できます。

さらに、取引制限はisBuyブランチに依存して購入をブロックしますが、「ペアからルーターへ」のパスはカバーしません。ルーターとペアの両方がホワイトリストアドレスであるため、そのような転送はホワイトリストチェックでショートカットされ、買い制限ロジックに到達しないため、攻撃者はルーターに買いをルーティングし、後に流動性を削除することでトークンを回収することでMTを取得できます。

攻撃分析
以下の分析は、トランザクション0xfb57...fca6に基づいています。
-
ステップ1:攻撃者は
~358,681e18WBNBをフラッシュローン。 -
ステップ2:攻撃者は
2e17MTを購入し、買い制限を回避。 -
ステップ3:攻撃者は
4e12WBNBと2e17MTをペアに供給して流動性を追加。この転送は、上記と同じ理由で手数料徴収ロジックを回避。 -
ステップ4:攻撃者はペアからルーターへの
~10,000,000e18MTトークンを購入し、買い制限と手数料徴収ロジックの両方を回避。 -
ステップ5:攻撃者は流動性ポジションの半分を削除し、その過程でルーターが保有するすべての
MTトークンを回収し、その後回収したMTを売却してWBNBに交換。このステップで、pendingBurnAmountは約9,000,000e18に操作された。
-
ステップ6:攻撃者は再び
~10,000,000e18MTトークンを購入し、プールのMT準備金を~6,756,516e18に引き下げた。これはpendingBurnAmountより低かった。
-
ステップ7:攻撃者は残りの流動性ポジションの半分を削除し、購入した
MTトークンを引き出し、その後distributeDailyRewards()を呼び出してプールからMTをバーンした。その結果、MT準備金は21,000e18に減少した。
-
ステップ8:攻撃者はすべての
MTを~1,198e18WBNBにスワップし、フラッシュローンを返済し、利益を確定した。
結論
このエクスプロイトは、不正確な取引制限により、攻撃者が正確にBINDING_AMOUNTのMTトークンを購入して買い禁止を回避できたことによって引き起こされました。MTトークンを取得した後、攻撃者は流動性を追加し、次にルーターにMTを買い、最後に流動性を削除してトークンを回収することで、手数料徴収ロジックと買い制限の両方をさらに回避できました。攻撃者は次に sell 操作を通じてpendingBurnAmountを蓄積し、バーンを実行してプール準備金を異常な状態にし、膨張した価格でMTを売却して利益を上げることができました。
将来、同様のリスクを軽減するために:
- 転送セマンティクスと取引ロジックの間に厳格な分離を強制する。
AAVE Liquidation Incident
概要
2026年3月11日、AAVEはEthereum上で2100万ドルの不正確な清算に見舞われ、約101万ドルの損失が発生しました。根本原因は、wstETHの不正確なオラクル価格であり、本来健全なポジションが担保不足になったことです。その結果、ユーザーのポジションが清算され、経済的損失が生じました。
背景
AAVEは、wstETHのようなラップされた資産の価格設定のためにオラクルアダプターを使用します。アダプターCAPO(Capped Price Oracle)は、ベースのETH/USD価格に変換比率(getRatio()、つまり1 wstETHがいくらのETHの価値があるか)を掛けてwstETHの価格を導出します。比率の操作を防ぐため、CAPOはスナップショットベースの成長キャップを適用します。
maxRatio = snapshotRatio + maxGrowthPerSecond x (currentTime - snapshotTimestamp)
そして、価格設定中にgetRatio()の出力をクランプします(currentRatio > maxRatioの場合、maxRatioを使用)。このメカニズムは、比率と結果的な価格の最大上方ドリフトを効果的に制限します。
脆弱性分析
根本原因は、CAPOオラクルアンカー構成(0xe1D9...61Ef)における時間-比率の不一致でした。スナップショットタイムスタンプとスナップショット比率は設定されていましたが、スナップショット比率は実際のwstETH/ETH比率を下回るように設定されていました。その結果、アダプターが計算したmaxRatioはライブ比率を下回り、getRatio()を下方にクランプし、wstETH/USDオラクル価格を体系的に低く評価しました。この低く評価された担保評価により、wstETHを担保として使用するポジションのヘルスファクターが低下し、本来健全なアカウントが不適切に不健全と分類され、清算されました。


攻撃分析
以下の分析は、トランザクション0x9064...8a9cに基づいています。
-
ステップ1:清算者は
~6304e18WETHをフラッシュローンし、借り手を清算。 -
ステップ2:清算者はフラッシュローンを返済し、清算を完了。
結論
この清算は、不正確なオラクル価格構成によって引き起こされ、本来健全であるべき借り手を不健全な状態に不適切に押し込み、それによりポジションの清算を引き起こしました。
将来、同様のリスクを軽減するために:
-
重要なパラメータは、更新前に正確性を確認すること。
-
実装に検証チェックを追加し、不正確なパラメータを拒否し、不正確な構成が正常に適用されるのを防ぐこと。
Planet Finance Incident
概要
2026年3月11日、Planet FinanceはBNB Chainで悪用され、推定10,000ドルの損失が発生しました。根本原因は、プロトコルが借り手の格納された借入残高の増加を利息として誤って扱い、攻撃者が繰り返し借入を行い、割引決済をトリガーして記録された負債を過小評価することを可能にしたことです。
背景
Planet Financeは、借り手が利息割引で返済できる貸付プロトコルです。割引は段階的であり、ユーザーのステーキングされたGAMMAと他の資産でのステーキング価値の比率によって決定されます。この比率が高いほど、返済割引も高くなります。割引スケジュールは3つのティアで構成され、0%(最小)から50%(最大)の範囲です。
脆弱性分析
根本原因は、changeUserBorrowDiscount()で借り手の割引を決済する際に、プロトコル(0x4c9E...F467)が、借り手の格納された借入残高の増加を新しい利息として誤って扱ったことです。その結果、利息にのみ適用されるはずの割引が、新しく借入された元本に誤って適用され、借り手の記録された負債が不適切に減少しました。攻撃者は、borrowとchangeUserBorrowDiscountのループを繰り返すことで過剰な割引を蓄積し、オンチェーンで記録された負債を真の借入額よりも一貫して低くさせ、最終的にその差額から利益を得ることができました。


攻撃分析
以下の分析は、トランザクション0x5f45...5ec9に基づいています。
-
ステップ1:攻撃者は
200,000e18USDTをフラッシュローン。 -
ステップ2:攻撃者は
5,000e18USDTを使用してWBNBを購入し、次に取得したWBNBを使用して~8,726,524e18GAMMAを購入。 -
ステップ3:攻撃者はまず、取得したすべての
GAMMAをgGAMMA市場にステーキングし、次に残りのUSDTを担保として供給し、これにより返済割引が5%に増加し、その後の借入が可能になった。 -
ステップ4:攻撃者は
borrowとupdateUserDiscountを繰り返し呼び出し、記録された負債を継続的に削減。
-
ステップ5:攻撃者は最終的に負債を返済し、担保を償還し、利益を実現した。
結論
このインシデントは、Planet FinanceのchangeUserBorrowDiscount()における不十分な割引決済ロジックによって引き起こされました。これは、借り手の格納された借入残高の増加を新しい利息として誤って扱い、その差額に利息割引を適用します。攻撃者は、borrowの後にupdateUserDiscountを繰り返し呼び出すことで、記録された負債を過小評価し、最終的に真の負債よりも少ない額を返済して利益を抽出することができます。
将来、同様のリスクを軽減するために:
- 貸付プロトコルにおいて、利息と新規借入を区別すること。
AM Incident
概要
2026年3月12日、BNB Chain上のデフレショナリートークンであるAM Tokenが悪用され、推定131,000ドルの損失が発生しました。AM Tokenは、各売却が流動性プールからの追加バーンをトリガーし、トークンを永久に削除して総供給量を削減するデフレメカニズムを実装しています。しかし、バーンは即座には実行されず、全売却額がtoBurnAmountとして記録され、実際のバーンは次の売却に延期されます。この遅延により、記録と実行の間にウィンドウが生じ、その間に攻撃者はAMを買い戻してプールからAM準備金をtoBurnAmountまで縮小することができます。次の売却で延期されたバーンがトリガーされると、AM準備金全体が消滅し、価格が極端なレベルまで上昇し、攻撃者はAMを利益のために売却できるようになります。
背景
AM TokenはBNB Chain上のデフレショナリートークンです。各売却時に、コントラクトはスワップに関与したAMの量をtoBurnAmountとして記録し、次の売却時にその記録された量を流動性プールからバーンします。実質的には、売却はプールからAM準備金を縮小する遅延バーンをトリガーします。さらに、バーンを実行する前に、プロトコルは蓄積されたtotalTokenFeeをUSDTにスワップし、その手数料配分ロジックに従って配布します。
脆弱性分析
根本原因は、トークン(0x27f9...213f)の売却ロジックが、スワップに関与した全AM量をtoBurnAmountとして蓄積し、次の売却時にAM/USDTペアからトークンを削除し、pair.sync()を呼び出して準備金を更新することでバーンを実行することでした。この設計により、攻撃者はプールのAM準備金を操作し、オンチェーン価格を歪め、アービトラージを通じて利益を上げることができます。


攻撃分析
以下の分析は、トランザクション0xd0d1...f859に基づいています。
-
ステップ1:攻撃者は
~27,265,119e18USDCと~361,710e18WBNBをフラッシュローンし、それらを~100,423,811e18USDTにスワップ。 -
ステップ2:攻撃者は
~5,062e18AMトークンをUSDTにスワップし、コントラクトの記録されたtoBurnAmountを~4,303e18に操作。
-
ステップ3:攻撃者は
USDTをAM Tokenにスワップし、プールのAM準備金を~4,303e18まで押し下げた。
-
ステップ4:攻撃者は
6 weiAMをプールに転送し、売却パスのバーンロジックをトリガーした。その結果、コントラクトはプールから全AM残高をバーンし、AM準備金を0まで引き下げた。注:プロトコルは、バーンの前にまず蓄積された手数料をUSDTにスワップしようとします。この手数料変換パスも売却ブランチのバーンロジックをトリガーします。バーンが実行され、AM準備金が0に達した後、手数料スワップは失敗します。これはtry/catchでラップされているため、失敗はトランザクションをロールバックしません。代わりに、実行が継続され、手数料アキュムレータは0にリセットされます。
-
ステップ5:攻撃者は
pool.sync()を呼び出し、残りのUSDTと1 weiAMをプールに転送した。両方のトークンが同時に転送されたため、コントラクトはこれをaddLiquidityとして扱い、toBurnAmountは蓄積されなかった。AM準備金は7に更新された。

-
ステップ6:攻撃者は残りの
AMトークンをUSDTにスワップした。このスワップ中、AMをペアに転送したことで売却パスのバーンロジックがトリガーされ、AM準備金は1に減少した。さらに、totalFeeAmountはステップ4で0にリセットされていたため、手数料からUSDTへの変換はもはや実行されず、攻撃者は人工的に膨張した価格でAMを売却することができた。
-
ステップ7:攻撃者はフラッシュローンを返済し、残りの利益を実現した。
結論
このインシデントは、AM Tokenの不十分なバーンメカニズムによって引き起こされました。これは、各売却のスワップに関与したAMをtoBurnAmountとして蓄積し、次の売却時にAM/USDTペアからその量をバーンし、pool.sync()を呼び出すものです。これにより、攻撃者はペアのAM準備金を極端なレベルまで操作し、人工的に膨張した価格でAMを売却してUSDTを吸い上げることができました。
将来、同様のリスクを軽減するために:
- トランザクションあたりの最大バーン額を制限し、バーンがトリガーされる頻度をレート制限することで、攻撃者が短期間でプールのトークン準備金の大部分を消費するのを防ぐ。
DBXen Incident
概要
2026年3月12日、EthereumとBNB Chainのバーン・トゥ・アーン・プロトコルであるDBXenが、合計約149,000ドルの損失で悪用されました。根本原因は、_msgSender()とmsg.sender間の不整合です。burnBatch()がforwarderを介して呼び出されると、バーンされたXEN量は_msgSender()(攻撃者が制御)の下に記録されますが、サイクルレコードはmsg.sender(forwarder)で更新されます。この分割により、攻撃者は古いサイクルレコードに対して報酬と手数料を請求でき、異常に大きな支払いにつながります。
背景
DBXenはバーン・トゥ・アーン・プロトコルです。ユーザーはXENトークンをバーンする代わりに、DXN報酬と蓄積されたプロトコル手数料のシェアを獲得します。主要なメカニズムはサイクルで機能します。ユーザーがburnBatch()を呼び出すと、2つのことが起こります。 (1) バーンされたXEN量が、呼び出し元のアドレス(_msgSender()で識別)の下に記録され、(2) XENコントラクトはonTokenBurned()を介してDBXenにコールバックして、呼び出し元のサイクルレコード(バーンサイクルとlastFeeUpdateCycle)を現在のサイクルに更新します。
報酬と手数料はupdateStats()を介して決済されます。報酬は、バーンサイクルでバーンされた総XENに対するユーザーのシェアに比例します。手数料は、ユーザーの最後の記録サイクル以降に発生した累積プロトコル手数料に基づいています。両方の計算は、ユーザーのサイクルレコードが最新であることに依存します。




脆弱性分析
根本原因は、DBXenプロトコル(0xf5c8...2abd)における不十分なビジネスロジックです。_msgSender()関数は、msg.senderがforwarderであるかどうかをチェックします。もしそうであれば、calldataの最後の20バイトを返しますが、この返された値はforwarderのコンテキストで任意に制御できます。しかし、burnBatch()はmsg.senderが保有するXENを直接バーンします。その結果、攻撃者はforwarderを介してburnBatch()を呼び出し、プロトコルにforwarderが保有するXENをバーンさせ、forwarderのバーンサイクルレコードと手数料更新サイクルレコードを現在のサイクルに更新させることができます。同時に、プロトコルはバーンされたXEN量を_msgSender()に対応するアドレスの下に記録します。
その後、攻撃者はclaimFees()を呼び出し、updateStats()を呼び出します。_msgSender()アドレスのサイクルレコードは一度も更新されなかったため(バーンサイクルとlastFeeUpdateCycleの両方が0のまま)、updateStats()は現在のサイクルで報酬を計算し、サイクル0以降に発生した手数料(プロトコルの全手数料履歴にわたる)を計算します。攻撃者は次にclaimFees()とclaimRewards()を呼び出して利益を上げます。

攻撃分析
以下の分析は、トランザクション0x914a5a...b808bc37に基づいています。
-
ステップ1:攻撃者はまず
ForwarderコントラクトのregisterDomainSeparator()関数を呼び出し、後続のForwarder.execute()呼び出しを可能にした。 -
ステップ2:攻撃者はUniswap V2プールで
0.14e18ETHを13,900,000,000e18XENにスワップ。 -
ステップ3:攻撃者は
13,900,000,000e18XENをForwarderコントラクトに転送。 -
ステップ4:攻撃者は
Forwarder.execute()を使用して、Forwarderが保有する13,900,000,000e18XENを支出するためにDBXenに承認を要求。 -
ステップ5:攻撃者は
Forwarder.execute()を使用してDBXen.burnBatch()を呼び出し、13,900,000,000e18XENをバーンした。バーン量はアドレス0x425D3eC2DCeBE2c04bA1687504D43AFC6be7328dの下に記録され、バーン実行中、XENはonTokenBurned()を介してDBXenにコールバックし、Forwarderの関連サイクルレコードを更新した。
-
ステップ6:攻撃者は
Forwarder.execute()を使用してDBXen.claimFees()を呼び出し、65.36e18ETHを獲得。 -
ステップ7:攻撃者は
Forwarder.execute()を使用してDBXen.claimRewards()を呼び出し、2,305.4e18DXNをミント。
結論
このインシデントの根本原因は、DBXenプロトコルが_msgSender()とmsg.senderを不整合に使用したことです。この2つの値が異なる可能性があるため、プロトコルの内部会計に不整合が生じ、攻撃者がその差異を悪用して利益を上げることを可能にしました。
将来、同様のリスクを軽減するために:
- すべてのロジックパスで一貫して
_msgSender()を使用するか、msg.senderに依存する操作と_msgSender()に依存する会計が常に同じアドレスを参照することを確認する。
Goose Finance Incident
概要
2026年3月15日、BNB Chain上のイールドファーミングプロトコルであるGoose Financeが約8,000ドルで悪用されました。根本原因はStrategyGooseEggにおけるシェア価格設定の順序の不具合でした。deposit()は、収穫された報酬を会計に決済する前にシェアをミントするため、シェア価格設定に使用される総資産の分母にはこれらの報酬が含まれず、真の値よりも低くなります。これは、預金者が本来受けるべきよりも多くのシェアを受け取ることを意味します。withdraw()が呼び出されると、報酬が収穫され、総資産が増加し、各シェアの価値が高まります。攻撃者は、単一のトランザクションでデポジットとウィズドローをループさせることで、過剰に価格設定されたシェアを繰り返しミントし、修正された(より高い)値でそれらを償還し、その差額を利益として抽出しました。
背景
Goose FinanceはBNB Chainのイールドファーミングプロトコルであり、ユーザーの資金はボールトから戦略に流れ込み、そこでMasterChefに資産をステーキングしてEGG報酬を獲得します。
このインシデントに関連するコンポーネントは次のとおりです。
-
VaultChef(0x3f64...):ユーザーポジションを追跡し、StrategyGooseEggに資本を転送します。 -
StrategyGooseEgg(0x0980...):sharesTotalとwantLockedTotalで戦略レベルの会計を維持します。 -
MasterChef(0xe70e...):資産をステーキングし、EGG報酬を支払います。 -
WrappedEgg(0xb815...):EGGを1:1でWEGGにラップしてステーキングします。
運用上、デポジットはVaultChefからStrategyGooseEggにルーティングされ、次にMasterChefにステーキングされます。ウィズドローはVaultChefから開始され、戦略によって実行されます。
シェア価格設定の重要な会計上の期待は、価格設定時に戦略資産の合計(ステーキングされた元本と戦略がすでに保有しているアイドル報酬)を反映することです。しかし、StrategyGooseEggでは、deposit()は_farm()がアイドル資産をwantLockedTotalに決済する前にシェアをミントします。これは、分母が未計上の報酬を含まず、真の総資産よりも低くなるため、預金者は本来受けるべきよりも多くのシェアを受け取ることになります。一方、withdraw()はMasterChefからの報酬収穫をトリガーできます。この順序が、以下の分析の脆弱性の基盤です。


脆弱性分析
根本原因は、StrategyGooseEgg(0x0980...b26b)における報酬収穫とシェア価格設定間の会計の同期ずれです。
StrategyGooseEggでは、シェア価格設定はwantLockedTotalを分母として使用します。shares = deposit * sharesTotal / wantLockedTotal。これが公正であるためには、wantLockedTotalは戦略が実際に保有するすべての資産(戦略の契約にあるアイドルEGG報酬を含む)を反映する必要があります。しかし、deposit()は_farm()がアイドル報酬をwantLockedTotalに決済する前にシェアをミントします。これは、分母が未計上報酬を含まず、真の総資産よりも低くなることを意味し、預金者は本来受けるべきよりも多くのシェアを受け取ることになります。
さらに、withdraw()はMasterChef.withdraw()を呼び出し、ステーキングされた元本と保留中のEGG報酬を戦略に返します。戦略の会計は、要求された_wantAmtをwantLockedTotalから差し引くだけであるため、収穫された報酬は戦略の残高に残ったままで、wantLockedTotalには反映されません。これにより、実際に保有する資産と記録されたwantLockedTotalの間のギャップが広がり、その後のdeposit()シェア価格設定はさらに不正確になります。
攻撃分析
以下の分析は、トランザクション0x86efdf...ce316223に基づいています。
-
ステップ1:攻撃者は2つのPancakeペアから
EGGをフラッシュ借入。 -
ステップ2:最初のデポジットを
VaultChef/StrategyGooseEgg(10,170,000e18EGG)に行う。 -
ステップ3:最初のウィズドロー(
12,593,884e18EGG)はMasterChefから報酬を収穫します。359,561e18EGGがStrategyGooseEggに転送され、アイドル/未計上値(R > 0)として残ります。 -
ステップ4:2回目のデポジットは、ウィズドローされた資本(
12,593,884e18EGG)を再利用します。シェアはアイドル値が決済される前に価格設定されるため、これが過剰ミントステップです。 -
ステップ5:2回目のウィズドロー(
12,826,027e18EGG)は、過剰ミントされたシェアからの利益(つまり、ステップ4のデポジット入力より232,143EGG多い)を実現します。 -
ステップ6:攻撃者はフラッシュスワップを返済し、純粋なスプレッドを保持します。
結論
このエクスプロイトは、StrategyGooseEggにおけるシェア価格設定の順序の不具合から発生しました。deposit()は、_farm()がwantLockedTotalを更新する前にシェアをミントし、withdraw()はMasterChefから一時的にアイドル状態の未計上報酬を収穫できます。これにより、デポジットは古い分母に対してミントされ、後で更新された資産に対してウィズドローできるようになります。
将来、同様のリスクを軽減するために:
-
報酬を決済し、シェアミントとシェアバーンの両方の計算の前に会計を更新する。
-
正確な計算時点での
totalAssets(ステーキング済み+アイドル)に対してシェアを価格設定する。 -
非ゼロのアイドル報酬条件下で
shares_minted <= D * S / (A + R)の不変テストを追加する。
BlockSecについて
BlockSecは、フルスタックのブロックチェーンセキュリティおよび暗号コンプライアンスプロバイダーです。私たちは、顧客がコード監査(スマートコントラクト、ブロックチェーン、ウォレットを含む)、リアルタイムでの攻撃遮断、インシデント分析、不正資金追跡、およびプロトコルとプラットフォームのライフサイクル全体にわたるAML/CFT義務の遵守を支援する製品とサービスを構築しています。
BlockSecは、権威ある会議で複数のブロックチェーンセキュリティ論文を発表し、DeFiアプリケーションのいくつかのゼロデイ攻撃を報告し、複数のハッキングを阻止して2000万ドル以上を救済し、数十億ドルの暗号通貨を確保しています。
-
公式ウェブサイト:https://blocksec.com/
-
公式Twitterアカウント:https://twitter.com/BlockSecTeam



