Back to Blog

[バタフライ効果] バグ修正が引き起こした複合セキュリティインシデント

Code Auditing
October 10, 2021

BlockSecチーム(@BlockSecTeam)より

先週、Compoundプロトコルでは、ユーザーに大量のCOMPトークンを「誤って」送信してしまうバグが発生しました。このバグ(本ブログのバグ2)の原因は、以前発見された別のバグ(本ブログのバグ1)の修正が不適切だったことにあります。

本ブログでは、最初のバグの根本原因と、最初のバグの修正がなぜ2番目のバグを引き起こしたのかを詳しく説明します。

背景

CompoundプロトコルはCompoundホワイトペーパーに基づいています。cTokenコントラクトを介して、ブロックチェーン上のアカウントは資産(EtherまたはERC-20トークン)をプロトコルに供給してcTokenを受け取るか、他の資産を担保としてプロトコルから資産を借り入れます。CompoundのcTokenコントラクトはこれらの残高を追跡し、借り手に対する金利をアルゴリズムで設定します

ユーザーをインセンティブ化するために、Compoundに流動性を提供するユーザー(資本を供給する)は利息を受け取ることができます。具体的には、ユーザーはCompoundに資産(Etherまたは他のERC20トークンなど)を供給し、対応するcTokenを受け取ります。ユーザーがCompoundに債務を一切負っていない場合、cTokenをCompoundに返却すると、元本資産(EtherまたはERC20トークン)と利息がユーザーに返還されます。例えば、ユーザーが1000 Etherを保有している場合、cEth.mint(1000)を通じて資産をCompoundに預け入れ、cTokenを取得できます。

cTokenは、Compoundにロックされている元本資産を表します。ユーザーはさらに、cTokenを担保として他の資産を借り入れることができます。例えば、ユーザーはceth.mint(1000)を通じて1000 Etherを預け入れ、取得したcTokenを使用して、75 Ether相当のxDaiを借り入れることができます(過剰担保 - この数値は担保率によって異なります)(cDai.borrow(x))。

コアロジックはComptrollerコントラクトに実装されています。これは、ユーザーの状態(例えば、ユーザーがCompoundに預け入れたトークンの数、ユーザーが借り入れたトークンの数、ユーザーがさらにトークンを借り入れられるかどうか)を維持します。このプロセスで呼び出される関数には、getHypotheticalAccountLiquidityInternal()borrowAllowed()mintAllowed()などが含まれます。

Compoundは、COMPというガバナンストークンも有しています。COMPトークンは提案に投票するために使用できます。さらに、COMPトークンは取引所で取引できます。現在、COMPの価格は約300ドルです。

バグ1

2021年9月31日、Compound DAOで新しい提案(提案62)が行われ、Comptrollerのバグを修正することを目的としていました。

このバグは、各ブロックでユーザーに配布されるCOMPトークンの数を示すCompSpeedに関連していました。

mint関数のフロー

以下では、mint関数を使用してこのバグの原因を説明します。mint関数の呼び出しチェーンは次のとおりです:mintmintInternalmintFresh

mintFresh関数では、mintAllowedが呼び出され、その後cTokenのユーザー残高が更新されます。

mintAllowed関数では、まずupdateCompSupplyIndexが呼び出され、次にdistributeSupplierCompが呼び出され、1)マーケットのcompSupplyStateを更新し、2) COMPトークンをユーザーに配布します。

updateCompSupplyIndex

updateCompSupplyIndex関数は、各マーケットのステータス、主にcompSupplyState[cToken]を更新します。

CompMarketState構造体には、この更新のブロック番号(block)と、ユーザー(cTokenを保有するユーザー)に配布されるべきCOMPトークンの数に影響するボーナスインデックス(index)が記録されています。

各トークンのボーナスインデックス(index)とは何ですか? これは時間とともに蓄積された値です(以下の式で示されています)。

これは、ユーザーに配布されるべきCOMPの数を示します(ユーザーが保有する各cTokenごと)。

distributeSupplierComp

もう一方の関数distributeSupplierCompは、ユーザー(サプライヤー)に配布されるべきCOMPトークンの数をcompAccrued[supplier]に記録する責任を負います。

具体的には、updateCompSupplyIndex関数でグローバルボーナスインデックスをcompSupplyStateに更新します。次にdistributeSupplierComp関数で、supplyIndexは現在のボーナスインデックスを記録し、supplierIndexはユーザー(サプライヤー)の最後のボーナスインデックスを示します。差分値 (supplyIndex - supplierIndex) * ユーザーのcToken残高 が、ユーザーに配布されるべきCOMPトークンの数を示します。

バグ1の原因

マーケットのsupplySpeedcompSpeeds[address[cToken]])を調整するためのsetCompSpeedという別の関数もあります。

これは、マーケットのCompSpeedをゼロに設定すると、そのマーケットのユーザーにCOMPトークンが配布されなくなるためです。したがって、まずマーケットのCOMP配布を無効にし、その後再度有効にしたい場合は、以下の手順に従うことができます。

  • ステップI: CompSpeed[cToken]をゼロに設定して、COMPトークンの配布を無効にします。
  • ステップII: setCompSpeed関数を呼び出して、CompSpeed[cToken]をゼロ以外の値に設定します。

ステップI: ステップIでCOMPトークンの配布が無効にされたマーケット(supplySpeed == 0)では、ブロックはゼロではありません。なぜなら、updateCompSupplyIndexelse if (deltaBlocks > 0))でブロックが継続的に更新されているためです。

ステップII: ステップIIの操作を実行する際、setCompSpeedInternal関数はelse if (compSpeed != 0)ステートメント(1083行目)を通過します。次に、1088行目から1093行目にかけて、if (compSupplyState[address(cToken)].index == 0 && compSupplyState[address(cToken)].block == 0)というチェックがあり、これは新しいマーケットindexblockを初期化します。しかし、既存のマーケット(新しいマーケットではない)でCOMPトークンの配布を再度有効にしているため、compSupplyState[address(cToken)].blockがゼロではないため、1090行目と1091行目のステートメントは実行されず、indexblockは初期化されません。

要約すると、現在無効になっているマーケットでは、indexはゼロですが、blockはゼロではありません。これは、CompSpeed[cToken]をゼロ以外の値に設定するためにsetCompSpeedを呼び出して無効なマーケットを再度有効にすると、index値がCompInitialIndex(1e36)に再初期化されない(1090行目と1091行目が実行されない)ことを意味します。

バグ1の影響

さらに、COMPトークンの配布を担当するdistributeSupplierComp関数を詳しく調べます。

supplierIndexcompInitialIndexです。しかし、バグのためsupplyIndexは依然としてゼロであり、これによりDouble memory deltaIndex = sub_(supplyIndex=0, supplierIndex=1e36)でアンダーフローが発生します。

バグ2:バグ1の修正によって導入された

バグを修正するために、プロジェクトオーナーはコードロジックを変更しました。具体的には、新しいマーケットを初期化する際に、indexを直ちにcompInitialIndexに初期化します。

グローバルボーナスインデックス(index)がcompInitialIndexに初期化されたため、ユーザーボーナスインデックスもこの値に初期化されるべきです。distributeSupplierComp関数を見てみましょう。

1234行目のif条件は、supplierIndex == 0であっても満たされることはありません。なぜなら、supplyIndexcompInitialIndex1e36)に等しく(それより大きいわけではない)ためです。これにより、supplierIndexcompInitialIndexに適切に初期化されず(値は0のまま)、deltaIndexsupplyIndex - supplierIndex)はゼロではなくcompInitialIndexになります。ユーザーのcToken残高がゼロでない場合、supplierTokensは大きな値になります。

要約すると、バグ1が修正される前にユーザーがmint操作を実行した場合、そのユーザーはcTokenを保有しており、COMPトークンが配布されていたためsupplierIndexはゼロになります。その後、バグ1の修正(バグ2を導入)が行われた後に、ユーザーが再度mint関数を呼び出すと、大量のCOMPトークン(1e36 * ctoken.balanceOf(user))を受け取ることができます。

実例

影響を受けたマーケットを以下に示します。

0xF5DCe57282A584D2746FaF1593d3121Fcac444dC: cSAI
0x12392F67bdf24faE0AF363c24aC620a2f67DAd86: cTUSD
0x95b4eF2869eBD94BEb4eEE400a99824BF5DC325b: cMKR
0x4B0181102A0112A2ef11AbEE5563bb4a3176c9d7: cSUSHI
0xe65cdB6479BaC1e22340E4E755fAE7E509EcD06c: cAAVE
0x80a2AE356fc9ef4305676f7a3E2Ed04e12C33946: cYFI

ユーザー(0xa7b95d2a2d10028cc4450e453151181cbcac74fc)は、このトランザクションで4,466.542459954989867175 COMPトークンを受け取りました(0x6416ed016c39ffa23694a70d8a386c613f005be18aa0048ded8094f6165e7308)。

トランザクションのさらなるデバッグにより、バグ2のためdeltaIndexが1e36であり、ユーザーがちょうどその時にcTokenを保有していたことが判明しました。

バグ2への対応

バグ2への対応は簡単です。distributeSupplierComp関数のif条件が変更されました。

教訓

  • これは別のバグの修正によって引き起こされたバグです。影響力の大きいプロジェクトのコード変更を徹底的にレビューする方法は、依然として未解決の問題です。
  • DAOは中央集権化のリスクを排除できます。しかし、セキュリティインシデントへの対応プロセスが遅くなるという側面もあります。
  • 影響力の大きいDeFiプロジェクトは、従来のプログラムの良いセキュリティプラクティスを取り入れることができます。例えば、継続的なテストプロセスを備えた効率的なファジングシステムを導入するなどです。

BlockSecについて

BlockSecは、2021年に世界的に著名なセキュリティ専門家グループによって設立された、先駆的なブロックチェーンセキュリティ企業です。当社は、新興のWeb3世界におけるセキュリティとユーザビリティの向上に専念し、その大規模な普及を促進することを目指しています。この目的のため、BlockSecはスマートコントラクトおよびEVMチェーンのセキュリティ監査サービス、セキュリティ開発と脅威のプロアクティブなブロックのためのPhalconプラットフォーム、資金追跡と調査のためのMetaSleuthプラットフォーム、そしてWeb3ビルダーが仮想通貨の世界を効率的にサーフィンするためのMetaSuites拡張機能を提供しています。

現在までに、MetaMask、Uniswap Foundation、Compound、Forta、PancakeSwapなど300以上の著名なクライアントにサービスを提供し、Matrix Partners、Vitalbridge Capital、Fenbushi Capitalなどの著名な投資家から2回の資金調達で数千万米ドルを受け入れています。

公式ウェブサイト:https://blocksec.com/

公式Twitterアカウント:https://twitter.com/BlockSecTeam

Sign up for the latest updates
Drift Protocol Incident: Multisig Governance Compromise via Durable Nonce Exploitation
Security Insights

Drift Protocol Incident: Multisig Governance Compromise via Durable Nonce Exploitation

On April 1, 2026 (UTC), Drift Protocol on Solana suffered a $285.3M loss after an attacker exploited Solana's durable nonce mechanism to delay the execution of phished multisig approvals, ultimately transferring administrative control of the protocol's 2-of-5 Squads governance with zero timelock. With full admin privileges, the attacker created a malicious collateral market (CVT), inflated its oracle price, relaxed withdrawal protections, and drained USDC, JLP, SOL, cbBTC, and other assets through 31 rapid withdrawals in approximately 12 minutes. This incident highlights how durable nonce-based delayed execution can decouple signer intent from on-chain execution, bypassing the temporal assumptions that multisig security implicitly relies on.

Weekly Web3 Security Incident Roundup | Mar 23 – Mar 29, 2026
Security Insights

Weekly Web3 Security Incident Roundup | Mar 23 – Mar 29, 2026

This BlockSec weekly security report covers eight DeFi attack incidents detected between March 23 and March 29, 2026, across Ethereum and BNB Chain, with total estimated losses of approximately $1.53M. Incidents include a $679K flawed burn mechanism exploit on the BCE token, a $512K spot-price manipulation attack on Cyrus Finance's PancakeSwap V3 liquidity withdrawal, a $133.5K flash-loan-driven referral reward manipulation on a TUR staking contract, and multiple integer overflow, reentrancy, and accounting error vulnerabilities in DeFi protocols. The report provides detailed vulnerability analysis and attack transaction breakdowns for each incident.

Newsletter -  March 2026
Security Insights

Newsletter - March 2026

In March 2026, the DeFi ecosystem experienced three major security incidents. Resolv Protocol lost ~$80M due to compromised privileged infrastructure keys, BitcoinReserveOffering suffered ~$2.7M from a double-minting logic flaw, and Venus Protocol incurred ~$2.15M following a donation attack combined with market manipulation.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit