DeFi エコシステムの急速な発展に伴い、Compound Finance V2 はこの分野の先駆者の 1 つとして、その革新的な融資モデルで多くのユーザーを魅了してきました。ただし、複雑な分散アプリケーションは、特に数百万ドル、さらには数億ドルに相当する資金の流れが関係する場合、潜在的なセキュリティ上の脅威に直面します。したがって、Compound Finance V2 とそのフォークされたプロジェクトの包括的かつ詳細なセキュリティ監査を実施することが特に重要です。このマニュアルは、開発者、セキュリティ研究者、DeFi 愛好家に詳細なセキュリティ監査ガイドを提供し、誰もが潜在的なリスクをより効果的に特定して防止できるようにすることを目的としています。
1. プロジェクトの背景概要
Compound Finance V2 は、イーサリアム ブロックチェーン上に構築されたオープンな融資プラットフォームであり、ユーザーがさまざまな ERC-20 基礎となるトークンを預けて利子を得ることができると同時に、利息の支払いの形で市場でトークンを借りることもできます。 「金利市場」の概念を導入することで、分散型資本プール管理と自動金利調整メカニズムを実現します。
2. プロジェクト構造の分析
Compound Finance V2 のコア アーキテクチャ コンポーネントには次のものが含まれます。
- Comptroller: 金利計算、口座ステータスの維持など、システム ロジック全体を制御します。
- cToken: ERC-20 標準を実装し、システムにおけるユーザーの権利と利益を表すカスタム トークン。
- InterestRateModel: 預金金利と借入金利を計算するためのモデル。
- PriceOracle: 資産価格のオラクルを提供します。
- ガバナンス: コミュニティのガバナンス関連機能を担当します。
2.1 コントローラー
Comptroller コントラクトは Compound Finance V2 の中枢神経システムであり、各 cToken インスタンスの動作を調整する役割を果たします。主な責任は次のとおりです。
- マーケット リストを管理し、どのマーケットがアクティブであるかを判断します。
- ユーザーポジションの健全性チェックなど、クロスマーケット業務のためのさまざまなチェックを実行します。
- 借入限度額、担保要素、清算閾値などのグローバルパラメータを設定および更新します。
2.2 cトークン
サポートされている各 ERC-20 トークンには、対応する cToken インスタンス (つまり、CErc20 / CEther コントラクト) があり、トークンとプロジェクト間のすべての対話を処理するために使用されます。基本的なトークン転送機能の実装に加えて、各 cToken には、貸し出し、利息の蓄積、報酬の分配など、いくつかの Compound 固有の機能も追加されます。したがって、cToken は、Compound に資産を預けるためのユーザーの証明書であり、ユーザーが融資操作を実行するための入り口であると考えることができます。
ユーザーが原資産トークンをコントラクトに預けると、対応する cToken トークンが鋳造されます。cToken と原資産の間の交換比率は次の式に従って計算されます。
注:借入金は借入額、現金は資金プールの残高、準備金は準備金を表します。借入金利は利用率によって決まり、預金金利は借入金利によって決まります。
ユーザーは通常、さまざまな cToken コントラクトを操作して、さまざまな市場でトークン貸付操作を実行します。
2.3 金利モデル
InterestRateModel コントラクトは、金利の計算方法を定義します。市場が異なれば、それぞれのリスク選好度や流動性ニーズに合わせて、異なるタイプの金利モデルが使用される場合があります。
Compound V2 の市場で使用される金利モデルには主に 2 つのタイプがあり、1 つは直線モデル、もう 1 つは変曲点モデルです。
定額モデルの借入金利計算式は以下のとおりです。
資金利用率の計算式は以下のとおりです。
預金金利は借入金利に応じて直線的に変化します。
利用量が徐々に増加するということは、資金プール内の資金が徐々に減少することを意味し、一定のピークに達すると、ユーザーは正常に預けたり借りたりできなくなる可能性があります。この状況を回避するために、Compound は 2 番目の金利モデルである変曲点タイプを導入しました。
転換点となる借入金利の計算式は以下のとおりです。
利用率が一定のピークに達すると、借入金利と預金金利が瞬時に大幅に上昇し、利用者がより多くの預金をし、より少ない借入を行うように動機付けられ、利用率が適切な範囲内に制御されます。このピークは変曲点とも呼ばれます。 (通常、使用率が 80% に達したとき)。
2.4 オラクルの価格
PriceOracle コントラクトは、外部の市場価格情報を取得し、それをシステムが内部で使用する値に変換する責任を負います。これは、ユーザーのポジションの価値を正確に計算するために重要です。
2.5 ガバナンスメカニズムとインセンティブモデル
Compound は、ガバナンス トークン (COMP) を保有するユーザーが、特定のパラメーターの変更や新しい資産タイプの追加などの重要な決定に関する投票に参加できるようにする独自のガバナンス メカニズムを導入します。 Compound はガバナンス トークン (COMP) を発行することで、ユーザーがプラットフォームのアクティビティに積極的に参加するよう奨励し、貢献者に報酬を提供します。詳細については、Compound の公式ドキュメントとコード リポジトリを参照してください。 (https://docs.compound.finance/v2/; https://github.com/compound-finance/compound-protocol)
3. インタラクションプロセス
次に、簡単な例を使用して、Compound Finance V2 でのユーザー操作の一般的なプロセスを説明します。
3.1 入金と引き換えのプロセス
ユーザーのアリスが 1 WBTC を Compound にデポジットする必要がある場合、彼女は cWBTC コントラクトの mint 関数を呼び出してデポジットを行います。このコントラクトは cToken コントラクトを継承します。まず、mintInternal 関数を通じて内部でAccumulateInterest 関数を呼び出して借入金利と預金金利を更新し、次に mintFresh を呼び出して特定のキャスト操作を実行します。
mintFresh 関数は、Comptroller コントラクトの mintAllowed 関数を外部から呼び出して、現在の市場で入金が許可されているかどうかを確認し、doTransferIn 関数を通じてユーザーの 1 WBTC をコントラクトに転送し、次に、次の条件に基づいてユーザーの対応する数の cToken トークンを鋳造します。その時点の最新の為替レート (現在の最新の為替レートが 0.1 であると仮定すると、アリスは 10 cWBTC トークンを受け取ります)。
アリスが将来デポジットを引き換えることに決めた場合、引き換え関数を呼び出すことで cWBTC を WBTC に引き換えることができます。為替レートが変更された可能性があります (0.15 と仮定)。これは、アリスがそのうち 1.5 WBTC を引き換えることができることを意味します。 0.5 WBTC は利息収入です。
3.2 借入と返済のプロセス
アリスはまず Comptroller コントラクトの enterMarkets 関数を呼び出して、cWBTC を担保として使用できる状態に設定する必要があります。そうすれば、お金を借りることができるようになります。
アリスが 70 USDC を融資することを選択したとします。WBTC の担保係数は 0.75 であるため、アリスは WBTC の価値の 75% まで融資でき、これは最大借入額を超えることはありません。
注: 清算のリスクを避けるために、アリスはバッファーを保持し、借入限度額を完全に使い切ってはなりません。
アリスは、cUSDC コントラクトの借用関数を呼び出します。この関数は、最初に、borrowInternal 関数内で、accumulateInterest 関数を呼び出して、借入金利と預金金利を更新します。次に、borrowFresh を呼び出して、特定の借入操作を実行します。
Comptroller コントラクトのborrowAllowed 関数を通じてユーザーのポジション値を確認した後、最初にローン データが記録され、次に doTransferOut 関数を通じてトークンがユーザーに転送されます。
Comptroller コントラクトのborrowAllowed 関数を通じてユーザーのポジション値を確認した後、最初にローン データが記録され、次に doTransferOut 関数を通じてトークンがユーザーに転送されます。
アリスが返済する必要がある場合は、cUSDC 契約の repayBorrow 関数を呼び出して自分で返済することも、他の人に repayBorrowBehalf 関数を呼び出して彼女に代わって返済させることもできます。
3.3 清算プロセス
WBTC の価格が大幅に下落し、アリスの担保価値が借入額の 75% を下回った場合、アリスのローンポジションは清算されます。
外部清算人 (ボブなど) は、cUSDC コントラクトの清算関数liquidateBorrow を呼び出して、アリスが借金の一部を返済できるようにすることができます。まず、liquidateBorrowInternal 関数を通じて cUSDC の金利と返済に使用される担保 cToken を同時に更新し、次に、liquidateBorrowFresh を呼び出して特定の清算操作を実行します。
Comptroller コントラクトのliquidateBorrowAllowed 関数を通じて清算が許可されているかどうかを確認した後、repayBorrowFresh 関数が最初に呼び出され、返済のために USDC をコントラクトに転送し、清算された人のローン データを更新します。次に、Comptroller コントラクトのliquidateCalculateSeizeTokens 関数が呼び出され、ボブが清算値に基づいてアリスの対応する価値を取得できる担保の量が計算されます。最後に、指定された担保市場での cToken コントラクト (cWBTC など) の差し押さえ関数が使用されます。ボブとアリスの cToken を転送します。
このリンクを開いて、上の画像の高解像度バージョンを表示します。クリックして元のテキストを読んで直接ジャンプしてください: https://www.figma.com/board/POkJlvKlWWc7jSccYMddet/Compound-V2?node-id=0-1&node -type=キャンバス。
ボブはアリスのローンの一部 (たとえば、20 USDC) を返済し、したがってアリスの対応する担保価値 (WBTC など) を取得します。同時に、ボブは追加の清算インセンティブ (5% と想定) も受け取ることができます。最終結果として、ボブは 21 USDC 相当の WBTC (20 USDC ローン + 1 USDC 清算インセンティブ) を受け取りました。
4. セキュリティ脆弱性チェックリスト
4.1 空市場によって引き起こされる丸めの脆弱性
4. セキュリティ脆弱性チェックリスト
4.1 空市場によって引き起こされる丸めの脆弱性
cToken が空の市場 (つまり、市場でユーザーが貸し出していない) の場合、exchangeRateStoredInternal 関数の ExchangeRate の値は、契約に対応する原資産トークンの数に依存するため、大量の原資産が移転される可能性があります。 cToken コントラクトにトークンを追加して、cToken の価格を操作します。
したがって、少量の cToken を使用して他の大量のトークンを貸し出し、cToken の redeemUnderlying 関数を呼び出して原資産トークンを引き出すことができます。償還を計算する際、差し引かれる必要がある cToken の数は、除算の切り捨てにより、予想よりもはるかに少ない結果 (ほぼ半分) になります。
この時点で保持されている cToken の数が 2 (totalSupply の合計でもある) であり、操作後の ExchangeRate が 25,015,031,908,500,000,000,000,000,000 に増加し、償還が必要な原資産トークンの数が 50,030,063,815 であると仮定します。この場合、差し引かれる予想される cToken の数は次のようになります。
ただし、実際に計算される cToken の数は次のとおりです。
ただし、実際に計算される cToken の数は次のとおりです。
したがって、他の市場から貸与された多数の資産トークンを取得するために、最終的に清算する必要があるのはごく少数の cToken だけです。
この脆弱性により、複合フォーク プロジェクト Hundred Finance のハッキングされたトランザクションを参照できます: https://optimistic.etherscan.io/tx/0x6e9ebcdebbabda04fa9f2e3bc21ea8b2e4fb4bf4f4670cb8483e2f0b2604f451
監査のポイント: 監査では、為替レートの計算方法が操作されやすいかどうか、四捨五入の方法が適切かどうかに注意を払う必要があります。同時に、プロジェクト チームに少額通貨の鋳造を推奨することもできます。新しいマーケットが作成された直後の cToken は、マーケットが空になってから操作されるのを防ぎます。
4.2 ERC677/ERC777 トークンによる再入可能性の脆弱性
ERC677 / ERC777 は ERC20 契約の拡張であり、ERC20 トークンのプロトコル標準と互換性があります。これらのトークンにより、転送プロセス中に、受信アドレスがコントラクトの場合、受信アドレスのコールバック関数 (transferAndCall や tokensReceived など) がトリガーされます。
Compound Finance V2 コードの古いバージョンでは、ユーザーが cToken マーケットでお金を借りると、最初に借りたトークンが転送され、次にローン データが記録されます。
ユーザーが貸し出したトークンがコールバック関数を備えた ERC677/ERC777 トークンである場合、そのトークンを受け取る悪意のあるコントラクトが構築され、コールバック関数を介して借用関数に再入力して再度借用することができます。前回の借入はデータがまだ考慮されていないため、アカウントの健全性チェックに正常に合格することで、この時点でトークンを再度貸し出すことができます。
この脆弱性によりハッキングされた複合フォーク プロジェクト Hundred Finance のトランザクションを参照できます: https://blockscout.com/xdai/mainnet/tx/0x534b84f657883ddc1b66a314e8b392feb35024afdec61dfe8e7c510cfac1a098
監査ポイント: 最新バージョンの Compound V2 コードでは借用ロジックが修正され、代わりに、借用データが最初に記録され、次に借用されたトークンが転送されます。監査の際には、融資関数の該当コードがCEI(Checks-Effects-Interactions)仕様に準拠しているかに注意する必要があり、コールバック関数を伴うトークンの影響も考慮する必要があります。
4.3 不適切なオラクルの仕組みによる価格操作リスク
4.3 不適切なオラクルの仕組みによる価格操作リスク
Compound Finance は過剰担保融資モデルを採用しているため、ユーザーが融資できるトークンの数は担保の価値が十分であるかどうかによって異なります。
したがって、プロジェクトが担保の価値を計算する際に使用するオラクルの価格フィードメカニズムが簡単に操作されると、予想よりも多くのトークンを貸し出すことが容易になります。
たとえば、複合フォーク プロジェクト Lodestar Finance がハッキングされた場合、オラクルが担保 plvGLP トークンの価格を取得する方法は、まず plvGLP 契約内の plsGLP トークンの数 (totalAssets) を plvGLP の総供給量で割ることでした。 ( totalSupply) は為替レートを計算し、その為替レートに GLP トークンの価格を乗算して、plvGLP トークンの価格を計算します。
plvGLP には、ユーザーが sGLP を寄付して、plvGLP トークン契約に対応する plsGLP トークンを鋳造できる寄付機能があります。
したがって、攻撃者はまずフラッシュ ローンを使用して Lodestar Finance 市場に多数の plvGLP 担保ポジションを作成し、次にフラッシュ ローンを使用して GMX 上で大量の sGLP を鋳造し、次に寄付機能を使用して、 plvGLP 契約により totalAssets の価値が増加します。総資産が増加すると、plvGLPの為替レートが大きくなり、plvGLPトークンの価格が瞬時かつ急速に上昇し、予想を超えて他のトークンが市場に貸し出されるようになります。
Lodestar Finance のハッキングされたトランザクションを参照できます: https://arbiscan.io/tx/0xc523c6307b025ebd9aef155ba792d1ba18d5d83f97c7a846f267d3d9a3004e8c
Compound Finance またはそのフォークされたプロジェクトも、担保の価格を取得するために ChainLink や CoinBase などのオフチェーン オラクルを使用することにも注意してください。深刻な市場変動に遭遇した場合、オフチェーン価格とオンチェーン価格の間に価格差が生じ、プロジェクトの財務上の安全性が危険にさらされる可能性があります。
たとえば、LUNAトークンの価格は市場の理由により急速に急落し、Compound FinanceのフォークされたプロトコルであるVenus ProtocolとBlizz Financeはどちらも、担保の価値を計算する価格フィードソースとしてChainlinkオラクルを使用しており、その中でLUNAトークンの最低価格( minAnswer) は $0.10 の値でハードコーディングされています。
LUNA トークンの価格が 0.1 ドル (例: 0.001 ドル) を下回ると、誰でも市場価格で大量の LUNA を購入し、プラットフォームから他の資産を貸すための担保 (0.10 ドル相当) として使用できます。
監査のポイント: 監査では、担保の価値を計算する際に使用されるオラクルの価格供給メカニズムが外部によって容易に操作されていないかどうかに注意する必要があります。プロジェクト当事者は、複数の価格ソースを使用して包括的な監査を行うことが推奨される場合があります。単一の価格源によって引き起こされるリスクを回避するための評価。
4.4 複数のエントリーポイントトークンによる為替操作のリスク
Compound のコードには SoupToken と呼ばれる関数があり、これを使用して、誤ってトークンをコントラクトに転送したユーザーがこれらのトークンを取り消すことができます。古いバージョンのコードは次のとおりです。この関数には重要なセキュリティ チェックがあります。渡されたトークン パラメーターをコントラクトの基礎となる資産トークンにすることはできません。
ただし、特定の cToken 市場の原資産トークンに複数のエントリ ポイント契約がある場合 (同じ基礎残高に複数の契約アドレスを介してアクセスでき、外部インタラクションがすべてのエントリ ポイントの残高に影響を与える)、これは初期の代理店のようなものです。モデル) を使用すると、攻撃者は weakToken 関数を呼び出して、基礎となるものとは異なるエントリ ポイント コントラクトを渡すことによって、コントラクト内の基礎となる資産トークンを転送することができます。
以下に TUSD を例に挙げます。これには 2 つのエントリ ポイント コントラクト 0x8dd5fbce がすべての呼び出し (transfer や BalanceOf など) をメイン コントラクトに転送します。これは、いずれかのコントラクトとの対話が両方のコントラクトに影響することを意味します。の残高データ (つまり、2 つの異なる契約が同じ残高データを共有します)。
現時点では、市場で設定されている基礎となるトークン アドレスが TUSD のメイン コントラクト アドレスであると仮定すると、スイープトークン関数を呼び出すときに受信トークン パラメーターとして補助エントリ ポイント コントラクト アドレス 0x8dd5fbce を使用でき、アドレス(トークン)正常にチェックできました! = 基礎となる場合、コントラクトはすべての基礎となる資産トークン TUSD を管理者のアドレスに転送します。
TUSD/cTUSD の為替レートは、cTUSD 契約の原資産トークン TUSD の量に影響され、すべての TUSD が管理者のアドレスに送金されると、TUSD/cTUSD の為替レートは即座に急落します。現時点では、攻撃者は他のユーザーを非常に低い為替レートで清算するか、借入後に予想されるトークン数よりも少ない額を返済することで利益を得る可能性があります。
最新バージョンの Compound V2 コードでは、マネージャ ロールによってのみコントラクトを呼び出せるようにするために、スイープトークン関数に権限の検証が追加され、複数のエントリ ポイント トークンを持つすべてのマーケットが削除されたことに言及する価値があります。
監査ポイント: 監査では、契約内のトークンの転送機能に関して、複数のエントリ ポイント トークンのシナリオがプロジェクトに与える影響を考慮する必要があります。プロジェクト パーティはマルチ エントリ ポイントを使用しないことが推奨される場合があります。トークンを確認するか、契約内の原資産トークンの数が変更されるかどうかの前後のトークン転送を確認し、関連する機能の権限を確認します。
4.5 契約コードの新旧バージョン間の互換性の問題
Compound Finance V2 フォーク プロジェクトで、コア コントラクトのコードが Compound Finance V2 コードの新しいバージョンでフォークされているが、それと対話する他のコントラクトが古いコード バージョンを使用している場合、互換性の問題が発生する可能性があります。
たとえば、cToken の旧バージョンで使用されていた InterestRateModel コントラクトの借入金利を取得する関数 getBorrowRate の戻り値は 2 uint 型の値でしたが、新バージョンの InterestRateModel 関数では、getBorrowRate 関数は 1 uint のみを返します。型の値。
ただし、Compound Finance V2 フォーク プロジェクト Percent Finance では、プロジェクト チームが古いバージョンの cToken コントラクト コードを使用していましたが、InterestRateModel コントラクトでは getBorrowRate 関数の呼び出し時に失敗しました。 CapsuleInterest関数は出金と融資の両方に使用されるため、最終的に出金と融資の機能が正常に実行できなくなり、契約内の資金が完全にロックされます。
監査ポイント: 監査中は、更新されたコード内のコントラクト インターフェイス、状態変数、関数シグネチャ、およびイベントの変更が既存のシステムの通常の動作を混乱させるかどうかに注意を払い、すべてのコントラクト コード バージョンの一貫性を確保する必要があります。更新するか、更新されたコードが古いバージョンのコードと互換性があることを確認します。
4.6 マルチチェーン展開によって発生するハードコーディングの問題
Compound Finance V2 のコードでは、定数 blockPeryear は毎年生成されるブロックの推定数を表し、その値は金利モデル コントラクトに 2102400 にハードコードされています。これは、イーサリアムの平均ブロック生成時間が 15 秒であるためです。
ただし、異なるチェーンのブロック時間は必ずしも同じであるとは限りません。また、年間を通じて生成されるブロックのおおよその数も同じであるとは限りません。複合フォーク プロジェクトが他のチェーンにデプロイされても、ハードコードされた値が異なるチェーンの条件に従って変更されない場合、金利の最終計算は予想を超える可能性があります。これは、blocksPeryearの値がbaseRatePerBlockとmultiplierPerBlockの値に影響し、baseRatePerBlockとmultiplierPerBlockが最終的に借入金利に影響を与えるためです。
たとえば、BSC チェーンのブロック生成時間が 3 秒である場合、年間の推定ブロック数 (blocksPer Year) は 10,512,000 になります。デプロイ前にblocksPer Yearの値が変更されない場合、最終的に計算された借入金利は予想よりも5倍高くなります。
監査ポイント: 監査中は、プロジェクト契約内のハードコーディングされた定数または変数が、さまざまなチェーンの特性の下で予期しない結果を引き起こすかどうかに注意を払う必要があります。プロジェクト当事者は、それらの値を正しく変更することをお勧めします。さまざまなチェーンの状況に応じて。
他の
上記の主な懸念事項に加えて、Compound V2 のフォークされたプロジェクトは通常、外部サードパーティ プロトコルと対話するためのコードの追加など、プロジェクト チームの設計に基づいてビジネス ロジックの一部を変更します。これは、Compound Finance V2 自体の中核となる融資モデルやプロジェクトに影響を与えるかどうか、特定のビジネス ロジックと設計要件に基づいて監査中に評価する必要があります。
最後に書きます
Compound Finance V2 とその Fork プロジェクトのセキュリティ監査マニュアルが、監査中にこのような複雑なシステムのセキュリティをよりよく理解して評価できるようになれば、このマニュアルも更新され、改善されることを願っています。
[1] https://github.com/YAcademy-Residents/defi-fork-bugs
[1] https://github.com/YAcademy-Residents/defi-fork-bugs
[2] https://medium.com/chainsecurity/trueusd-compound-vulnerability-bc5b696d29e2
[3] https://github.com/code-423n4/2023-05-venus-findings/issues/559
[4] https://learnblockchain.cn/article/2593
[5] https://github.com/compound-finance/compound-protocol
著者 | 九九
編集者 | リズ
全てのコメント