過去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 | 設定ミス | 約101万ドル |
| 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が、誤った二重計上により悪用され、約25Kドルの損失が発生しました。ゲーム内の各NFTは、引き出し可能なETH残高(「エネルギー」と呼ばれる)を保持しています。ゲームのメカニズムとして、プレイヤーはattack()を使用して、ある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() による直接入金は basic のみを増加させ、freakerIndex には影響しません。したがって、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)を読み取り、その一部をプールに分散させます。これにより、freakerIndexが新しいEtherの裏付けなしに増加するため、同じターゲットエネルギーが支払いとプール入力の両方としてカウントされます。これは再入可能性バグではなく、ビジネスロジックのインフレバグです。
将来的に同様のリスクを軽減するために:
-
トランザクションがまだ処理中である間に、転送フック内で経済的値を変更可能な状態から再計算することを避ける。
-
転送フックが状態変数を読み取る場合、実行順序が結果に影響しないことを確認する(例:フックが実行される前、後ではなく、状態を確定させる)。
Alkemi Incident
概要
2026年3月10日、Ethereum上のAlkemiプロトコルが悪用され、約89Kドルの損失が発生しました。根本原因は会計エラーと不適切なビジネスロジックです。不適切な清算ロジックにより、誰でも同じトランザクション内で自分のポジションを清算して利益を得ることができます。さらに、会計エラーにより、清算中に攻撃者自身の担保の控除が上書きされ、攻撃者は意図されたコストを負担することなく清算報酬を得ることができます。
背景
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が悪用され、約242Kドルの損失が発生しました。根本原因は、取引制限ロジックの不備と、特別な転送条件の不整合な処理です。デフレフェーズ中、コントラクトはプール準備金が固定しきい値を超えると買い操作を制限します。しかし、コントラクトは(例:2e17 MT)のような正確な金額の転送を紹介者バインディングアクションとして扱い、攻撃者が買い制限を回避して初期トークンを取得できるようにします。さらに、制限ロジックは不完全なパス検出(isBuy)に依存しており、PairからRouterのような間接的なスワップルートをカバーせず、ホワイトリストチェックは重要な検証をさらにショートサーキットさせます。攻撃者は、制限や手数料をトリガーすることなくMTトークンを蓄積し、管理された流動性操作と取引を通じてpendingBurnAmountを操作し、トークン価格が人工的にインフレした異常な状態にプールを強制しました。
背景
MT Tokenは、BNB Chain上のデフレショナリートークンであり、組み込みの取引制限があります。デフレフェーズ中、コントラクトはプール内のMT準備金が21,000e18を超えると買い操作をブロックします。準備金がこのしきい値を下回ると、デフレフェーズが終了し、買いが再開されます。MT Tokenは紹介メカニズムも備えています。2e17 MTまたは1e17 MTの転送は、通常の取引ではなく、紹介者バインディングアクションとして扱われます。
脆弱性分析
根本原因は、MTコントラクト(0x037E...b449)における買い制限設計の不備でした。通常の条件下では、攻撃者は制限フェーズ中にシードキャピタルとしてMTを取得することはできないはずです。しかし、コントラクトは正確に2e17 MTの転送を買いではなく紹介者バインディングアクションとして扱います。これにより、攻撃者は買い制限を回避して2e17 MTを購入できます。

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

攻撃分析
以下の分析は、トランザクション 0xfb57...fca6 に基づいています。
-
ステップ1:攻撃者は
~358,681e18WBNBをフラッシュローンで借入。 -
ステップ2:攻撃者は
2e17MTを購入し、買い制限を回避。 -
ステップ3:攻撃者は
4e12WBNBと2e17MTをPairに供給して流動性を追加。この転送は、上記と同じ理由で手数料徴収ロジックを回避した。 -
ステップ4:攻撃者はPairからRouterへ
~10,000,000e18MTトークンを購入し、買い制限と手数料徴収ロジックの両方を回避。 -
ステップ5:攻撃者は流動性ポジションの半分を削除し、その過程でRouterが保持していたすべての
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トークンを取得した後、攻撃者は流動性を追加し、RouterにMTを購入させ、最後に流動性を削除してトークンを回収することによって、手数料徴収ロジックと買い制限の両方をさらに回避することができました。その後、攻撃者は売却操作を通じてpendingBurnAmountを蓄積し、バーンを実行してプール準備金を異常な状態にし、高値でMTを売却して利益を上げることを可能にしました。
将来的に同様のリスクを軽減するために:
- 転送セマンティクスと取引ロジックの間に厳格な分離を強制する。
AAVE Liquidation Incident
概要
2026年3月11日、AAVEはEthereum上で2,100万ドルの不正確な清算に見舞われ、約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上で悪用され、推定約10Kドルの損失が発生しました。根本原因は、プロトコルが借り手の保存された借入残高の増加を利息として誤って扱い、攻撃者が繰り返し借りて割引決済をトリガーして記録された債務を過小評価できるようにしたことです。
背景
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が悪用され、推定131Kドルの損失が発生しました。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をPairに転送すると、売却パスのバーンロジックがトリガーされ、AM準備金は1に削減されました。さらに、ステップ4でtotalFeeAmountが0にリセットされていたため、手数料からUSDTへの変換はもはや実行されず、攻撃者は人工的にインフレした価格でAMを売却することができました。
-
ステップ7:攻撃者はフラッシュローンを返済し、残りの利益を実現しました。
結論
このインシデントは、AM Tokenの不適切なバーンメカニズムによって引き起こされました。これは、各売却のスワップに関与したAMをtoBurnAmountとして蓄積し、次の売却時にAM/USDTペアからその金額をバーンし、pool.sync()を呼び出すものです。これにより、攻撃者はペアのAM準備金を極端なレベルまで操作し、人工的にインフレした価格でAMを売却してUSDTを排出することができます。
将来的に同様のリスクを軽減するために:
- トランザクションあたりの最大バーン額を制限し、バーンがトリガーされる頻度をレートリミットすることで、攻撃者が短期間にプールトークン準備金の大部分を消費するのを防ぎます。
DBXen Incident
概要
2026年3月12日、EthereumおよびBNB Chain上のバーン・トゥ・アーン・プロトコルであるDBXenが悪用され、総損失額は約149Kドルとなりました。根本原因は、_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が悪用され、約8Kドルの損失が発生しました。根本原因はStrategyGooseEggにおけるシェア価格設定の順番の不備でした。deposit()は、収穫された報酬を会計に決済する前にシェアをミントするため、シェア価格設定に使用される総資産の分母にはこれらの報酬が含まれず、真の値よりも低くなります。これにより、預金者は本来よりも多くのシェアを受け取ります。withdraw()が呼び出されると、報酬の収穫がトリガーされ、総資産が増加するため、各シェアの価値が高まります。攻撃者は、単一トランザクションでデポジットとウィズドローをループさせることで、繰り返し高額なシェアをミントし、修正された(より高い)価値でそれらを償還し、その差額を利益として抽出しました。
背景
Goose Financeは、ユーザー資金がボルトから戦略に流れ込み、その戦略がMasterChefに資産をステークしてEGG報酬を獲得するBNB Chainのイールドファーミングプロトコルです。
このインシデントに関連するコンポーネントは次のとおりです。
-
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から収穫できます。これにより、デポジットは古い分母に対してミントでき、後で更新された資産に対してウィズドローできます。
将来的に同様のリスクを軽減するために:
-
報酬を決済し、シェアミントとシェアバーン計算の両方の前に会計を更新すること。
-
アイドル報酬がゼロでない条件下で、
shares_minted <= D * S / (A + R)の不変条件テストを追加すること。
BlockSecについて
BlockSecは、フルスタックのブロックチェーンセキュリティおよび暗号コンプライアンスプロバイダーです。当社は、顧客がコード監査(スマートコントラクト、ブロックチェーン、ウォレットを含む)、リアルタイムでの攻撃傍受、インシデント分析、不正資金追跡、およびAML/CFT義務の履行を、プロトコルとプラットフォームのライフサイクル全体にわたって支援する製品とサービスを構築しています。
BlockSecは、著名なカンファレンスで複数のブロックチェーンセキュリティ論文を発表し、DeFiアプリケーションのいくつかのゼロデイ攻撃を報告し、多数のハッキングをブロックして2,000万ドル以上を救済し、数十億ドルの暗号通貨を確保しました。
-
公式ウェブサイト:https://blocksec.com/
-
公式Twitterアカウント:https://twitter.com/BlockSecTeam



