報告期間(2026/08/22 - 2026/08/30)中、5件のセキュリティインシデントが観測され、推定損失総額は約2,270万ドルでした。
| 日付 | インシデント | 種類 | 推定損失 |
|---|---|---|---|
| 2026/08/20* | The Cosmos EVM Exploit Series(MANTRA、TAC、KiiChain、他3件) | 算術アンダーフロー/オーバーフロー | ~570万ドル* |
| 2026/08/27 | Moonwell | 価格操作 | ~910万ドル |
| 2026/08/28 | Ajna | 不適切なビジネスロジック | ~77.5万ドル |
| 2026/08/28 | Rain Card Contract Exploit Series(Avici、Triaなど) | 署名検証バイパス | ~110万ドル† |
| 2026/08/30 | Tectonic | 価格操作 | ~600万ドル‡ |
*Cosmos EVMシリーズは、今週の報告期間より前の08/20(MANTRA)から始まり、先週のレポートでは取り上げられていませんが、完全性のためここに含めています。約570万ドルは、公式のCosmosポストモーテムによると、影響を受けた6つのチェーン全体で攻撃者が実現した金額です(DEX経由で約287万ドル、中央集権型取引所経由で約285万ドル、以降凍結)。これは8月19日時点の価格によるものです。トークン量が判明している場合、名目上のドレイン額はより大きくなります(TAC 約30億TAC、約750万ドル; MANTRA 7億2,090万トークン、約360万ドル; KiiChain 1億4,830万KII)が、そのほとんどは未売却、凍結、またはオンチェーンで回収可能な状態にあります。
†約110万ドルは、共有コントラクトによって露呈したRainがサポートするプログラム全体での推定合計額です。AviciとTriaが最大の2件で、それぞれ約500,859ドル(1,685ユーザー)と約431,945ドル(636ユーザー)を公表しています。
‡約600万ドルは、ロールバック前にEthereumにブリッジされた実現損失です。ドレインされた総額の推定値は、約7,400万ドル(攻撃者のウォレットで追跡された分)から約1億1,950万ドル(市場からの総流出額)までの幅があります。大部分はCronosに残っており、バリデータがチェーンをエクスプロイト前の状態にロールバックした際に消去されました。TectonicもCronosも最終的な損失額を確認していません。
Web3のためのベストセキュリティ監査
ローンチ前に設計、コード、ビジネスロジックを検証
今週の注目: The Cosmos EVM Exploit Series(TACチェーンで追跡)
このシリーズが選ばれた理由は、共有インフラストラクチャの開示に関して示唆する点にあります。バグは深刻なセキュリティ脅威ではないと誤って判断されたため、その修正は協調された非公開配布ではなく、サイレントな公開パッチとして出荷され、その後サードパーティのフォークがエクスプロイトパスを公然と説明したことで、そのモジュールを実行しているすべての未パッチのチェーンが露呈しました。
2026/08/20から08/25の間、攻撃者は6つのCosmos EVMチェーン全体で共有されるcosmos/evmモジュール内の2つの脆弱性を組み合わせた単一のエクスプロイトチェーンを実行しました。両方のバグは、EVM状態とCosmos x/bank台帳を照合するコード内にありました。すなわち、残高のアンダーフローと、それに対応するオーバーフローが、1つの供給量中立トランザクション内で連鎖していました。
公式のCosmosポストモーテムによると、このバグは4月にバウンティプログラムを通じて報告され、当初は本番資金を脅かすものではないと誤って判断されたため、サイレントな公開パッチプロセスを経て、08/19にv0.6.2およびv0.7.2として出荷されました。08/20にサードパーティのフォークがエクスプロイトパスを公然と説明し、その数時間後に最初のドレインが始まり、まずMANTRA(08/20)を襲い、次にTACとKiiChain(08/22)が続きました [1][2]。6つのチェーン全体で、攻撃者は8月19日時点の価格で約570万ドルを実現しました(DEXで売却された約287万ドルと、中央集権型取引所で売却された約285万ドル、以降凍結)[1]。
本レポートでは、詳細な事例としてTACチェーンを分析します。同チェーンはシリーズの中で最も被害を受けたチェーンで、ステーキングプールの損失は名目で約750万ドルに上りました [3]。
背景
TACチェーンはCosmos SDKとEVMの両方を実行します。tac1...アドレスと0x...アドレスは同じ基礎となる20バイトを共有しているため、単一のアドレスは最初にCosmosベスティングアカウントとして作成され、後にそのアドレスにEVMコントラクトがデプロイされることがあります。これらは2つの別々のアカウントではなく、同一のアドレスが同時にベスティング状態とコントラクトコードの両方を持ちます。両者は並行して実行され、TACはステーキングなどのネイティブCosmosアクションを、固定アドレスにあるプリコンパイルコントラクトを通じてEVM呼び出し元に公開しているため、EVMコントラクトは他の呼び出しと同様にそれらを呼び出すことができます。
ベスティングアカウントのBank総残高にはロックされたトークンが含まれます。ロックされた部分はベスティングされるまで送金できませんが、使用可能残高は、総残高からロック額を差し引いた残りです。EVMがアカウントをロードすると、StateDBの残高は使用可能残高から初期化され、実行中のコントラクト送金はその残高に対して動作します。


トランザクションの終了時に、StateDB内の残高変更は正式な台帳であるx/bankに決済され戻され、そこで決済されたものだけが実際に送金可能なTACとなります。TACはv0.7.x系統を実行しており、これはEVM残高を直接x/bankに書き込みますが、その値がuint256からint256への変換を生き延びる場合に限られるため、2^256に近い残高はまったく決済できません。
ロックされたトークンは送金できませんが、ステーキングのために委任することは可能です。委任はロックされたトークンを含むBank総残高をチェックし、その後DelegatedVestingを通じてベスティング制限を維持します。総残高total=1の完全にロックされたアカウントの場合、そのユニットを委任するとBank総残高は0に減少し、DelegatedVestingが更新されますが、使用可能残高は委任の前後どちらも正しく0のままです。
脆弱性分析
問題のあるコンポーネントは、すべてのステートフルなステーキングプリコンパイル呼び出し時に実行される共有cosmos/evmハンドラー内の残高同期処理で、0x0000...0800のステーキングプリコンパイルを通じて公開されており、[4]で実装されています。これはEVM残高をCosmos台帳とチェックされていないuint256算術で照合しており、これが2つの補完的な欠陥を露呈させています。すなわち、ステーキングの書き戻しパス上の残高アンダーフローと、通常の加算パス上のそれに対応するオーバーフローです。どちらか単独では危険ではなく、リスクは共有された算術パス上でのそれらの組み合わせから生じます。
最初の欠陥は、残高の書き戻し時のアンダーフローです。 ステートフルなステーキングプリコンパイル呼び出しが実行されると、ネイティブCosmosアクションがBeforeBalanceChangeとAfterBalanceChangeフックの間で実行され、AfterBalanceChangeはネイティブの残高変更をEVM StateDBに反映する役割を担います。


委任はBank総残高に対して検証されるため、完全にロックされたアカウントは、使用可能残高が0のままでも委任チェックを通過できます。ネイティブの委任は、委任された金額をBank総残高から差し引き、coin_spentイベントを発行します。欠陥はAfterBalanceChangeがそのイベントをどのように消費するかにあります。アカウントの現在の使用可能残高を再読み込みして代入する代わりに、coin_spentの金額をstateDB.SubBalance(spender, amount)として再生し、既存のEVM残高から差し引きます。

SubBalanceはチェックされていないuint256算術で減算を実行するため、EVM残高がイベント金額より小さいアカウントはアンダーフローします。EVM残高0かつcoin_spent金額1のアカウントの場合、0 - 1はMAX_UINT256にラップします。

2つ目の欠陥は、加算パス上の対応するオーバーフローです。 すべての残高転送は、同じStateDBを通じて送信者に対するSubBalanceと受信者に対するAddBalanceを実行するため、両方向が1つの算術パスを共有します。

両者は同じstateObjectのプリミティブに帰着し、そこでAddBalanceとSubBalanceは範囲チェックなしでnew(uint256.Int).Add(s.Balance(), amount)と.Sub(s.Balance(), amount)を計算します。減算が0を超えてアンダーフローするのと同様に、十分に大きな加算はMAX_UINT256を超えてオーバーフローし、下方にラップして戻ります。

この2つの欠陥は補完的です。単独では、アンダーフローはMAX_UINT256の残高を生成しますが、2^256に近い値はx/bankへの決済に必要なuint256からint256への変換を生き延びられないため、これは不活性です。チェックされていない加算はその対となるもので、これはそのような過大な残高をその決済上限より下に戻すことができる唯一のパスです。
攻撃分析
以下の分析はトランザクション0xae4e9b...da46fcに基づいています。
-
ステップ1: トランザクション0x4da591...df1af7で、攻撃者は
CREATE2ファクトリーをデプロイし、攻撃コントラクトアドレス0x5711...c978がデプロイ前に計算できるようにしました。 -
ステップ2: トランザクション95F43742...6A885BAで、攻撃者は
MsgCreateVestingAccountを使用して、その将来のコントラクトアドレスに1基本単位(1utac)を転送しロックしました。これにより、そのアドレスは使用可能残高を0に保ちながら委任チェックを通過できるBank総残高を持つことになりました。

- ステップ3: トランザクション0x2400f8...c57c81で、攻撃者は
CREATE2を使用して攻撃コントラクトを同じ0x5711...c978アドレスにデプロイし、それをCosmosベスティングアカウントとEVMコントラクトの両方にしました。これにより、外部アカウントがガス代を支払う一方で、コントラクトアドレスはspendable=0のまま委任者であり続けることができました。

-
ステップ4: 攻撃コントラクトはステーキングプリコンパイルを通じてロックされた基本単位を委任しました。ステーキングフローはBank総残高に対する委任を受け入れ、その後の残高書き戻しでコントラクトのEVM残高が
MAX_UINT256にラップしました。 -
ステップ5: ラップされた
MAX_UINT256の残高はそのままではCosmos台帳に決済できないため、攻撃コントラクトはまずそれを決済可能な値に引き下げました。攻撃コントラクトは、チェーン最大のアカウントであり、ステーキングされたすべてのTACを保持するbonded_tokens_poolにほぼ残高全体を送金し、加算時にプールの残高がオーバーフローして0にラップするように金額を選びました。その金額は攻撃コントラクト自身のMAX_UINT256から差し引かれたため、コントラクトはプールの以前の残高と正確に同じ額を保有することになり、新たな供給は生成されませんでした。プールをゼロにすることで、このオーバーフローが生み出せる最大額であるプールの残高全体を得られるため、攻撃者はそもそもチェーン最大のアカウントを標的にしたのです。 -
ステップ6: 攻撃コントラクトはその後、その残高2,985,651,403.40 TACを攻撃者のアドレスに転送しました。

私たちのトランザクションレベルの分析は、2つの連鎖した脆弱性を説明する公式のCosmosポストモーテムと一致しています。上記のアンダーフローが異常な残高を生成し、攻撃分析ステップ5のオーバーフローがそれをプールの実際の資金に変換します [1]。その公式レポートより前に公開された初期のKiiChainポストモーテムでは、少なくとも3つの上流の欠陥が関与しており、アンダーフローのみがパッチされたと主張していました [2]。独立した報道機関の報道でも、いくつの上流欠陥が残っているかについて同様の見解の相違が要約されています [5]。
結論
Cosmos EVM Exploit Seriesの根本原因は、EVM層とCosmos層が同じ残高をどのように計上するかの不整合と、境界チェックが一度もされていなかった算術演算の組み合わせにありました。2つのランタイムが1つの台帳を共有する場合、それらは各個別アカウントに至るまで残高のセマンティクスに合意しなければならず、すべての残高変更はオーバーフローとアンダーフローについてチェックされなければなりません。この欠陥は特定の単一チェーンのコードではなく共有モジュール内に存在していたため、1つの欠陥がそれを実行しているすべてのチェーンを露呈させ、これが単一のバグを複数チェーンにまたがるイベントに変えました。コードの問題を超えて、このインシデントは深刻度評価における教訓でもあります。この脆弱性は当初、本番資金を脅かすものではないと判断されたため、その修正はサイレントな公開パッチとして出荷されました。その判断が修正された時にはすでにパッチは公開されており、サードパーティによるエクスプロイトパスの開示が、その誤判断を6つのチェーンへの攻撃に変えました。実際の資金を動かし得る共有インフラストラクチャのバグは、誰にでも解読できるサイレントな公開パッチではなく、最初から非公開で協調された配布が必要です。
今週のその他のインシデント
Moonwell
2026/08/27、Base上のMoonwellが、担保計上のインフレーションと、Core Marketに上場していた低流動性資産MAMOのオラクル価格操作を組み合わせることで、約910万ドルの被害を受けて悪用されました。MAMOの価格を押し上げることに加え、攻撃者はMAMOをmMAMO市場コントラクトに直接転送し、シェアをミントすることなくその裏付けを引き上げ、価格変動に加えて担保価値をインフレさせました。この二重にインフレした担保に対して、攻撃者はcbBTC、WETH、USDC、wstETHにわたって約1,103万ドルの総借入を行い、清算後も約913万ドルの残存債務が残りました [6][7]。
脆弱性分析
MoonwellのCore MarketはCompound v2コード上で動作しており、1つの市場シェア(mMAMO)の価値は(cash + totalBorrows - totalReserves) / totalSupplyです。ここでは2つの弱点が組み合わさっています。第一に、MAMO供給上限は正式なミントパスのみをチェックするため、mMAMOコントラクトへのMAMOの直接転送は、シェアをミントすることなく市場のキャッシュに加算されます。これにより計算された交換レート(exchangeRateStored())が上昇し、既存のすべてのシェアの担保価値を引き上げつつ、上限を完全にバイパスします。第二に、MAMOは流動性が薄いにもかかわらず50%の担保係数で担保として上場されていたため、そのオラクル価格は少ない資金で動かすことができました。担保価値はシェア数×交換レート×オラクル価格として計算されるため、交換レートと価格の両方が操作可能な面であり、それらを一緒にインフレさせることで、はるかに流動性の高い資産に対する借入力が倍増しました。
攻撃分析
以下の分析はトランザクション0x09687d...395593eに基づいています。この作戦は約194.7万ドル(799 ETHをUSDCに変換しBaseにブリッジ)で開始され、借入資産が循環利用された後のMAMO購入総量は約750万ドルに達しました。
-
ステップ1: 攻撃者は正式に
MAMOを供給してmMAMOをミントし、その後Mintイベントを発行しない2件のトランザクションで53,393,290 MAMOをmMAMOコントラクトに直接転送しました。これにより市場の計算上の交換レートが約3.68倍に上昇し、攻撃者が直前にミントしたmMAMOシェアや、他のすべての保有者のシェアが再評価されました。 -
ステップ2: 攻撃者は流動性が薄い間にDEXプール全体で
MAMOを購入し、MAMO/USDフィードを約0.0106ドルから約0.43ドルまで押し上げました。

- ステップ3: 交換レートと価格の両方がインフレした状態で、攻撃者は
cbBTC、WETH、USDC、wstETHにわたる18件の借入(総額約1,103万ドル)を完了し、その後収益を変換して統合し、約872.9万USDCをCCTP経由でEthereumにブリッジし、約872.8万DAIに変換しました。
結論
根本原因は、Moonwellが低流動性の担保資産を、独立して操作可能な2つの面で同時に評価していたことです。すなわち、薄い流動性ゆえに攻撃者が少ない資金で動かせるオラクル価格と、対象となる資産の直接転送によってインフレした、供給上限が正式なミントパスのみを守っていたレシートトークンの交換レートです。担保価値はこの2つを乗算するため、両方を一緒にインフレさせることで、はるかに流動性の高い資産に対する借入力が倍増しました。レンディング市場は、担保の価格とシェア計上の両方を操作可能なものとして扱うべきであり、要求されていない転送を交換レート計算から除外し、保守的な担保係数を流動性を考慮した価格設定、および薄い資産に対する厳格な供給・借入上限と組み合わせるべきです。
Ajna
2026/08/28、Ajnaは清算パスにおけるビジネスロジックの欠陥により、7つのEthereumプール全体で約77.5万ドルの被害を受けて悪用されました。Ajnaは外部オラクルを使用しないレンディングプロトコルであり、バケットの流動性から導出されるLUP(最低利用価格)を通じてポジションを価格設定します。攻撃者はまずLUPを操作して著しく債務超過のポジションを作り出し、その後プロトコルに、プロトコルがまだ市場価格をはるかに上回る価格を保持しているダッチオークション価格で、コントロールされたポジションを清算させました。これにより、バケットの預金請求権が額面価値で消費されて債務の返済に充てられ、その見返りとして攻撃者は実際の担保を受け取りました [8]。
背景
Ajnaは、Uniswapプールと同様に、誰でも供給と借入のためのペア化されたトークンのプールを作成できる、パーミッションレスなプロトコルです。各プールは、Uniswap V3の価格ティックに似たバケットに分割されており、各ティックは担保1単位あたりの固定量のクォートトークンを表します。貸し手は自分の希望するローン・トゥ・バリュー比率に応じてバケットを選択し、そこに流動性を提供して、その見返りとして(そのバケットの預金に対する請求権である)LPを受け取ります。外部オラクルが存在しないため、LUPはバケットの預金分布と総債務から直接導出されます。
ポジションは、そのしきい値価格(担保に対する債務の比率)がLUPを上回った時点で清算可能になります。誰でもその時点で保証金を投じてポジションを清算にキックすることができ、これによりダッチオークションが開始されます。オークション価格は現物市場に連動しておらず、基準価格の32倍から始まり、1時間ごとに半減しますが、減衰が始まる前の最初の1時間(治癒期間)はフラットに保たれます。
オークションされた担保は2つの方法で取得できます。呼び出し元は現在のオークション価格でクォートトークンを支払って担保をtake()することも、選択したバケットのクォート預金を使ってオークションされた担保を購入し借り手の債務を返済するbucketTake()を呼び出すこともできます。bucketTake()では、担保はそのバケットに計上されるため、そのバケットの貸し手が担保を取得することになります。アービトラージバケットテイクの場合、呼び出し元はさらに、清算を引き起こすインセンティブとして、バケット価格とオークション価格のスプレッド分のLP報酬(そのバケットに対する請求権)を支払われます。この設計は、テイカーがオークション価格が担保の価値以下に下がってから初めて介入することを前提としています。オラクルがない状態で、この下降オークションこそがAjnaが公正な価格を発見する方法であり、バケットの貸し手は自分のバケット価格を下回る価格で担保を取得することで利益を得ます。オークション自体は、借り手の債務が清算されるかローンが再び担保化された時にのみ終了します。別の決済パスが、72時間の猶予期間の後、または担保が残っていない場合はそれより早く、オークションを解決し、残存する不良債権を吸収して残った担保を借り手に返却します。
このメカニズムの3つの実装の詳細が、テイクが生み出す数値を決定します。第一に、オークション価格は市場から導出されるものではなく、32 * max(kickMomp, neutralPrice) * 2^(-max(elapsedHours - 1, 0))として減衰するため、最初の1時間の治癒期間はフラットに保たれ、その後初めて下降を始め、その期間の直後には基準価格の32倍近くに留まります。

第二に、キックは借り手がペナルティ加算前の債務ですでに担保割れしている場合にのみ受け入れられます。_kick()は_isCollateralized()チェックを実行し、3ヶ月分の利息ペナルティを加算する前にBorrowerOk()でリバートするため、そのペナルティはポジションが適格となる助けにはなっていないにもかかわらず、記録されたキック後の債務を引き上げます。

第三に、オークションに対する最初のテイクは、テイクの返済額と担保額が計算される前に借り手の債務に7%のペナルティを加算するため、単一のbucketTake()が単独で債務を清算する必要はありません。

脆弱性分析
問題のあるコントラクトはAjnaプール(0xad24...178e)であり、ここで説明する清算ロジックはその検証済みソースから取得したものです。bucketTake()内部で、プロトコルはバケットの預金請求権を額面価値でクォートトークンとして使用し、オークションの借り手の債務を返済しています。また、アービトラージバケットテイクでは、バケット価格とオークション価格のスプレッドに基づいてテイカーにLP報酬を支払います。
これらの預金請求権の実際の回収可能価値や、オークション価格が経済的に妥当かどうかを一度もチェックしておらず、単にその請求権が後に額面価値で償還できると想定しています。それが依拠する2つの価格は独立に計算されており、どちらも相互に検証されていません。LUPはバケットの預金分布とプールの総債務に従う一方、オークション価格はキック時に固定された基準価格からの経過時間のみに基づいて純粋に減衰します。したがって、請求権のオンチェーン上の額面価値は、実際に回収できるものと乖離する可能性がありますが、bucketTake()はそれでもその額面価値に対して債務を決済します。
内部的には、bucketTake()は_takeBucket()を経由して_rewardBucketTake()にルーティングされます。ここでは、アービトラージテイクの場合、テイカーのLP報酬は、取得された担保にバケット価格とオークション価格のスプレッドを乗じて計算されます。

攻撃分析
以下は、被害を受けた7つのプールの1つであるcbETHプールについて、トランザクション0x8a8793...016e64と0x12dfde...14e4f5を使用したオンチェーン分析に基づいています。
- ステップ1: 準備トランザクションで、攻撃者は市場をはるかに上回る価格であるバケットインデックス
2000に約49.343WETHのクォート流動性を追加しました。そのインフレしたバケットに裏付けられ、攻撃者はほぼ無担保のポジションを開設し、約0.001cbETHを担保に入れて約49.319WETHを借り入れました。このポジションはほとんど担保を保有していないため、狙われた対象ではありません。これは2つの準備目的を果たします。第一に、市場が正常に戻った際に、この債務超過ポジションがLUPを引き下げます。第二に、バケット2000に預けられた49.343WETHのほぼ全てがすでに借り戻されているため、微小な額しか残っておらず、バケット2000には実際に回収できる額を大きく上回る額面価値(約49.343WETH)を持つ預金請求権が残ります。この毀損した請求権こそが、後にステップ4で攻撃者が額面価値で使う弾薬となります。

-
ステップ2: 同じ準備トランザクションで、攻撃者は2つ目のコントロールされたアドレス
0x02d329...6f5fを使用して、通常の価格水準で借り手ポジションを開設し、約48.128cbETHの担保を差し入れ、それに対して約49.319WETHを借り入れました。これにより市場価格は正常な範囲に戻り、最初のポジションを著しく債務超過のままにしつつ、2つ目のポジションはその健全性しきい値付近に留まりました。実際の担保を保有するこの2つ目のポジションこそが、攻撃者が本当に排出しようとしていたものであり、債務超過の最初のポジションはLUPを動かすためのレバーに過ぎません。 -
ステップ3: 攻撃者は次に、債務超過の最初のポジションではなく、2つ目のポジション(
0x02d329...6f5fが開設したもの)をオークションにキックしました。キックは、現在のLUPに対してペナルティ加算前の債務ですでに担保割れしているポジションに対してのみ受け入れられますが、この時点までに債務超過の最初のポジションがLUPを十分に押し下げていたため、2つ目のポジションの累積債務がその基準を満たしました。適格性チェックの後にのみ、プロトコルは3ヶ月分の利息のキックペナルティを加算するため、記録されたキック後の債務は約49.362WETHになります。このキックによりポジションのダッチオークションが開始されました。

- ステップ4: 攻撃者は治癒期間の1時間が経過した直後まで待機しました。この時点でオークション価格は減衰し始めたばかりで、まだ基準価格の32倍に近い状態でした。バケット
2000(現在は毀損した請求権を保持している同じバケット)に対して2つ目のポジションでbucketTake()を呼び出すことで、そのまだ高い、市場価格をはるかに上回る価格で清算を実行しました。これがそのオークションに対する最初のテイクだったため、プロトコルはテイクの返済額と担保額を計算する前に借り手の債務に7%のペナルティを加算しました。担保はオークション価格で価格設定されるため、バケット2000の預金約49.34WETHを消費してもその債務の一部しか返済されず、約1.51cbETHの担保しか除去されませんでした。これにより担保のほとんどが手つかずのまま残り、呼び出し元はそのスプレッドに対して大量のLP報酬を得ました。

-
ステップ5: 攻撃者は
removeCollateral()を通じてそれらのLP報酬を償還し、利益の一部を担保として取り出しました。 -
ステップ6: 締めくくりとして、攻撃者はBalancerのフラッシュローンを利用し、Ajnaの
take()を呼び出して、bucketTake()が残していた残存債務を、今度は実際のクォートトークンで支払い、借り手の債務がゼロになりオークションが終了するまで続けました。最後にrepayDebt()が呼び出され、quoteRepaid=0のまま、今や担保されていない担保約46.51cbETHが引き出されました。それに対してクォートトークンは一切返却されませんでした。この利得はプールの犠牲によって生じたものです。不足分は消えたわけではなく、移動しただけです。2つ目のポジションが閉じられると、最初のポジションのまだ返済されていない、ほぼ無担保のローンは不良債権としてプール内に残り、他の貸し手が負担することになりました。

結論
根本原因は、Ajnaの清算パスが、実際の回収可能額やオークション価格が経済的に妥当かどうかをチェックすることなく、バケットの預金請求権を額面価値でオークションの借り手の債務に対して決済していることです。このチェックの欠如こそが、意図的に作り出された毀損した請求権を額面価値で消費して実際の担保を排出することを可能にし、不足分をプール内に不良債権として残しました。清算パスは、預金請求権を額面価値ではなく実際の回収可能額で評価し、テイクを完了する前にオークション価格が経済的に妥当であることを確認し、プールの既存の不良債権によってすでに毀損している請求権が消費されることを防ぐべきです。影響を受けたプールコントラクトは不変であり、管理者による一時停止機能を持たないため、ドレインが始まると止める手段はなく、ユーザーは自分自身で脱出するしかありませんでした。
The Rain Card Contract Exploit Series(Aviciで追跡)
2026/08/28(UTC)、Rainの共有Solanaカード担保プログラムの旧バージョンが、Ed25519署名検証バイパスによって悪用されました。プログラムに捏造された管理者承認を受け入れさせることで、攻撃者はユーザーの担保アカウントの制御を奪い、そこに保持されていたトークン残高を排出しました。この欠陥はRainを使用する複数のカードプログラム間で共有されるプログラムコード内にあったため、単一のバグがそれら全てを一度に露呈させました。このエクスプロイトは合計で推定約110万ドルをドレインし、AviciとTriaが最大の2件で、それぞれ約500,859ドル(1,685ユーザー)と431,945ドル(636ユーザー)を公表しています [9]。
以下の分析では、詳細な事例としてAviciを使用します。
背景
Solanaでは、署名チェックはトランザクション処理の一部としてネイティブEd25519プリコンパイルによって実行されます。参照される署名が無効な場合、トランザクション全体が失敗します。ビジネスプログラムはその結果を直接受け取るのではなく、同じトランザクション内の他の命令を(Instructions sysvarを通じて)検査し、検証が合格していたことに依拠します。ユーザーがRainを利用するカード(Aviciの場合など)にトップアップすると、その残高は共有Rainプログラムが管理するユーザーごとの担保アカウントに保持され、そのアカウントのトークン権限はプログラムから導出されるため、そのアカウントの管理者はプログラムの転送フローを通じてそのアカウント内の資産を移動できます。
担保アカウントの管理者を変更するには2つの署名が必要です。1つはプロトコルが指定した管理者からのものでなければなりませんが、もう1つの署名者には特別な身元要件はありません。この設計はオフチェーンの認可に依拠しています。すなわち、プロトコル管理者が管理者変更メッセージに署名すると、プログラムはそれを承認済みとして扱います。
Ed25519検証命令は、チェックすべき署名の数を示す1バイトのカウントとパディングバイトから始まり、署名ごとに1つのEd25519SignatureOffsets構造体が続きます。各構造体は、署名、公開鍵、メッセージのバイトオフセットだけでなく、それぞれをどの命令インデックスから読み取るべきかも指定します [10]。これらのインデックスは意図的な機能であり、1つの検証がトランザクション内の任意のインデックス付き命令のデータからその入力を読み取れるようにするものです。例えば、別の命令のデータをコピーせずにその上の署名を検証するためです。ネイティブベリファイアは、オフセットと命令インデックスが指し示すものを単純に読み取ります。

脆弱性分析
この欠陥は担保プログラム(3zVB...yBzDuc)内にあります。それは、ある公開鍵が検証者が実際にチェックしたものであることを確認することなく、その鍵を信頼しています。管理者承認の署名者を記録するために、それが検査するEd25519検証命令内の固定位置から公開鍵を読み取り、その後、検証が合格したという事実だけをもって、この鍵の保持者が管理者変更メッセージに署名したことの証明とみなします。
それが決して確認しないのは、その検証が実際にどこを見ていたかです。それらの命令インデックスフィールドは呼び出し元によって制御可能であり、ランタイムはすべての命令のデータをネイティブベリファイアに渡すため、検証命令はその本体に1つの公開鍵を持ちながら、その命令インデックスをベリファイアが別の命令に向けるように誘導することができます。これにより、別々に取得されたメッセージに対する別の公開鍵の下での署名を検証させることができます。


このため、「管理者鍵」の2つの読み取りが、それらを結びつけるものが何もないまま存在します。すなわち、プログラムはそれが検査する命令内にある鍵を信頼する一方、ベリファイアはインデックスが指し示したものだけをチェックしています。管理者の鍵は、その鍵の下で有効な署名が一度も検証されていなくても、トランザクション内にデータとして存在し得ます。この分離こそが脆弱性であり、欠けているチェックとは、ベリファイアが実際に検証した公開鍵とメッセージが、プログラムが信頼する公開鍵とメッセージと同一であることを強制するバインディングです。
攻撃分析
以下の分析はトランザクションZmpBgn...mqWLに基づいています。
- ステップ1: 攻撃者は、
SubmitSignaturesの前に2つのEd25519検証命令を含むトランザクションを構築しました。命令0は攻撃者自身の公開鍵(cafa…53db)と管理者変更メッセージに対する本物の署名を運んでおり、トランザクションに1つの真に有効なEd25519検証を与えました。

-
ステップ2: 命令
1はプロトコル管理者の公開鍵(a2fc…959a)を公開鍵スロットに配置しましたが、署名スロットには偽の0x09バイトを埋め込みました。そのため、この命令はもしその自身のデータが実際に検証されれば失敗するはずでした。 -
ステップ3: 攻撃者は命令
1のEd25519ヘッダーを01003000000010000000700020000000に設定しました。リダイレクションを行うのは3つの命令インデックスフィールドのみで、デコードするとすべてゼロであるため、署名、公開鍵、メッセージはすべて命令0から読み取られます(バイトオフセットは依然としてその命令のデータ内を指しています):num_signatures = 1, padding = 0 signature_offset = 48, signature_instruction_index = 0 public_key_offset = 16, public_key_instruction_index = 0 message_data_offset = 112, message_data_size = 32, message_instruction_index = 0そのため、2つ目の検証は命令
0を再読み込みし、命令1内の無効な0x09ペイロードの代わりに攻撃者自身の有効な署名を再検証しました。 -
ステップ4: 攻撃者は
SubmitSignaturesを呼び出しました。プログラムは2つの成功したEd25519検証を確認しましたが、2つ目の署名者を記録する際に、命令1に埋め込まれたプロトコル管理者の公開鍵を読み取ったため、そのプロトコル管理者鍵を承認済みの2つ目の署名者として受け入れました。 -
ステップ5: この虚偽の承認が記録された状態で、攻撃者は管理者変更フローを使用して、被害者の担保アカウントの管理者を攻撃者に設定し、検証バイパスをそのアカウントの直接的な制御に変換しました。

- ステップ6: 呼び出し元を担保管理者として検証したプログラムは、そのトークンアカウントの権限として自身のプログラム派生アドレス(PDA)を使用して転送を呼び出し、その転送フローを通じてユーザーのカード担保トークンアカウントから資産を移動させました。

結論
The Rain Card Contract Exploit Seriesの根本原因は、担保プログラムがネイティブEd25519検証が実行されたことを確認していたものの、それが信頼した公開鍵とメッセージが、ベリファイアが実際にチェックしたものであることを一度も確認していなかったことです。ネイティブの署名検証に依拠するプログラムは、消費する認可データをその検証に結びつける必要があります。すなわち、現在の命令からその入力を読み取る自己完結型のEd25519レイアウトを要求するか、参照されるすべての命令を解決し、検証された公開鍵とメッセージを、それが基づいて動作するデータとバイト単位で比較するかのいずれかです。同じ欠陥が多くのRainを利用するカードプログラムにわたって未パッチのまま実行されていたことこそが、1つのバグを複数プログラムにまたがるイベントに変えたのです。
Tectonic
2026/08/30、Cronos上のCompoundスタイルのレンディングプロトコルであるTectonicは、その低流動性ガバナンストークンTONICが20%の担保係数で担保として許可されていたことにより悪用されました。攻撃者はTONIC建ての担保を2つの面で同時にインフレさせました。すなわち、tTONIC交換レートの直接転送操作と、オラクル価格を押し上げるDEX購入です。この二重にインフレした担保に対して、攻撃者は複数のレンディング市場にわたって借入を行いました。約629万ドル(2,592 ETH)がEthereumにブリッジされ、実現損失として残っています。ドレインの大部分はCronosに残り、バリデータがチェーンをエクスプロイト前の状態にロールバックした際に消去されました。TectonicもCronosも最終的な損失額を確認していません [11]。
脆弱性分析
根本原因は、低流動性のガバナンストークンであるTONICを20%の担保係数で担保として上場したことです。そのガバナンス上の役割にもかかわらず、TONICは極めて薄いオンチェーン流動性しか持っていなかったため、その評価は限られた資金で大きく動かすことができました。Moonwellのエクスプロイトと同様に、2つの面が同時に操作可能でした。すなわち、TONIC/USDオラクル価格と、tTONICレシートトークンの交換レートです。後者は、対応する債務を相殺することなく、市場にTONICを単純に転送するだけで引き上げることができました [11]。ゼロでない担保係数があれば、そのインフレした評価を、はるかに流動性の高い資産に対する借入力に変換できました。
攻撃分析
以下の攻撃の再構成は、オンチェーンインテリジェンスと詳細なアーカイブノードの再構成に基づいています [11][12]。
-
ステップ1: 攻撃者は約500万
USDCを担保としてTectonicに供給しました。TONIC/USDオラクルがまだそのトークンを約1.06e-8ドルと評価している間に、コントロールされた1つのアカウントが約376.54兆TONICを借り入れ、2つ目のアカウントに移動しました。 -
ステップ2: 2つ目のアカウントは約41.87兆
TONICを通常通り供給し、その見返りとして(市場のレシートトークンである)tTONICを受け取りました。 -
ステップ3: 攻撃者は、元の
TONIC債務を返済することなく、借り入れた残りのTONICの大部分をtTONIC市場コントラクトに直接転送し、tTONIC交換レートを引き上げ、2つ目のアカウントが保有するtTONICの担保価値を押し上げました。 -
ステップ4: インフレした
tTONIC担保を使用して、攻撃者は200,000USDCと約696万CROを借り入れ、その後TONIC/USDC、TONIC/WCRO、TONIC/VVSの各プール全体でさらに約16.23兆TONICを購入し、それを再び市場に投入しました。これにより交換レートがさらに引き上げられ、DEXの現物価格が上昇しました。TectonicのオフチェーンTONIC/USDフィードは、その後、急速に上昇する一連の見積もり(12:19 UTCの約1.06e-8ドルから12:49 UTCまでに約2.08e-6ドル)を受け入れました。これが外部の面です。

-
ステップ5: 約331万
USDCと2,121万CROのさらなる借入により、さらに768億TONICが購入され、再び市場に投入され、インフレした交換レートと抽出直前に操作された価格の両方がさらに強化されました。 -
ステップ6: 最後のトランザクションで、攻撃者は複数のレンディング市場全体でこの二重にインフレした担保に対して引き出しを行い、約5,524万
USDC、4,565万USDT、98WBTC、1,895WETH、1,675万CRO、およびその他の資産を抽出しました。バリデータがチェーンを停止させる前に、約629万ドル(約2,592 ETH)がEthereumにブリッジされました。ブロック生成は2026/08/30 23:49 UTC(08/31に発表)に、バリデータが状態をエクスプロイト前のブロックにロールバックしてCronos上の残高を消去した後に再開されました。ブリッジされた約629万ドルのみが実現損失として残っています。
結論
今週初めのMoonwellのエクスプロイトと同様に、これは低流動性トークンを担保として受け入れたCompoundスタイルの市場に対する価格操作攻撃であり、担保の価値を2つの面(オラクル価格とレシートトークンの交換レート)で同時にインフレさせるものでした。レンディングプロトコルは低流動性資産を担保として上場することを避けるべきであり、サポートせざるを得ない場合は厳格な供給・借入上限を適用し、薄い現物市場がオラクルを動かせないように時間加重または流動性を考慮した価格設定を使用すべきです。交換レートの面には独自の防御策が必要です。市場の担保計上は、対象資産の要求されていない転送を除外すべきであり、それにより、対応する債務が未払いのまま残っている間に直接転送によってレシートトークンの交換レートがインフレするのを防げます。異常な価格・借入活動のオンチェーン監視により、価値がブリッジで流出する前の対応時間をさらに短縮できます。
参考文献
- [1] Cosmos EVM GHSA-7g4w-cg88-2cq2 ポストモーテム
- [2] KiiChainインシデント開示
- [3] TACチェーンインシデント開示
- [4] cosmos/evm残高ハンドラーのコミット
- [5] crypto.news: Cosmos EVMの脆弱性がMANTRA、TAC、KiiChainからドレイン
- [6] Moonwellエクスプロイトに関するBlockaidアラート
- [7] Moonwell: ポストモーテム、Base上のMAMOマーケットインシデント
- [8] Ajnaエクスプロイトに関するDefimonアラート
- [9] crypto.news: Rainのコントラクトエクスプロイトがカードユーザーから110万ドルをドレイン
- [10] Solana Docs: Ed25519プログラム
- [11] The Defiant: Tectonicエクスプロイト後、Cronosがチェーンをロールバック
- [12] MASTR: Tectonicアーカイブノードの再構成
BlockSecについて
BlockSecは、フルスタックのブロックチェーンセキュリティおよび暗号資産コンプライアンスのプロバイダーです。私たちは、コード監査(スマートコントラクト、ブロックチェーン、ウォレットを含む)の実施、攻撃のリアルタイムでの阻止、インシデントの分析、不正資金の追跡、AML/CFT義務の遵守を、プロトコルおよびプラットフォームのライフサイクル全体にわたってお客様が実現できるよう支援する製品とサービスを構築しています。
BlockSecは、権威ある学会で複数のブロックチェーンセキュリティ論文を発表し、DeFiアプリケーションの複数のゼロデイ攻撃を報告し、複数のハッキングをブロックして2,000万ドル以上を救済し、数十億ドル規模の暗号資産を保護してきました。
-
公式ウェブサイト: https://blocksec.com/
-
公式Twitterアカウント: https://twitter.com/BlockSecTeam



