CertiK:Suiの最新の脆弱性「ハムスターホイール」、技術的詳細と深堀り分析
著者:CertiK
以前、CertiKチームはSuiブロックチェーン上で一連のサービス拒否の脆弱性を発見しました。これらの脆弱性の中で、新型かつ深刻な影響を持つ脆弱性が特に注目を集めました。この脆弱性は、Suiネットワークのノードが新しいトランザクションを処理できなくなり、ネットワーク全体が完全に停止したのと同じ効果をもたらします。
先週の月曜日、CertiKはこの重大なセキュリティ脆弱性を発見したことにより、SUIから50万ドルのバグ報奨金を受け取りました。アメリカの業界権威メディアCoinDeskはこの事件を報じ、その後各メディアも続いて関連ニュースを発表しました。
このセキュリティ脆弱性は「ハムスター・ホイール」と形象的に呼ばれています:その独特な攻撃手法は現在知られている攻撃とは異なり、攻撃者は約100バイトのペイロードを提出するだけで、Sui検証ノード内の無限ループを引き起こし、新しいトランザクションに応答できなくします。
さらに、攻撃による損害はネットワークの再起動後も持続し、Suiネットワーク内で自動的に拡散し、すべてのノードがハムスターがホイールの上で無休止に走り続けるように新しいトランザクションを処理できなくなります。したがって、この独特な攻撃タイプを「ハムスター・ホイール」攻撃と呼ぶことにしました。
この脆弱性を発見した後、CertiKはSuiのバグ報奨金プログラムを通じてSuiに報告しました。Suiも迅速に効果的な対応を行い、この脆弱性の深刻性を確認し、メインネットの立ち上げ前に問題の修正に向けた適切な措置を講じました。この特定の脆弱性を修正するだけでなく、Suiはこの脆弱性が引き起こす可能性のある潜在的な損害を軽減するための予防的な緩和策も実施しました。
CertiKチームの責任ある開示に感謝するため、SuiはCertiKチームに50万ドルの賞金を授与しました。
以下では、技術的な観点からこの重要な脆弱性の詳細を開示し、この脆弱性の根本原因と潜在的な影響を明らかにします。
脆弱性の詳細
Suiにおける検証者の重要な役割
SuiやAptosのようなMove言語に基づくブロックチェーンでは、悪意のあるペイロード攻撃を防ぐための保障メカニズムは主に静的検証技術です。静的検証技術を通じて、Suiは契約が公開またはアップグレードされる前に、ユーザーが提出したペイロードの有効性を確認できます。検証者は、構造と意味の正確性を確保するための一連のチェックを提供し、チェックを通過した場合にのみ、契約はMove仮想マシンで実行されます。
Moveチェーン上の悪意のあるペイロードの脅威
Suiチェーンは、元のMove仮想マシンの上に新しいストレージモデルとインターフェースを提供しているため、Suiにはカスタム版のMove仮想マシンがあります。新しいストレージ原語をサポートするために、Suiは信頼できないペイロードの安全検証に対して一連の追加のカスタムチェック手段を導入しました。これには、オブジェクトの安全性やグローバルストレージアクセスなどの機能が含まれます。これらのカスタムチェック手段はSuiの独自の機能に適合しているため、これらのカスタムチェックをSui検証者と呼びます。
Suiによるペイロードのチェック順序
上の図に示すように、検証者内のほとんどのチェックは、CompiledModule(ユーザーが提供した契約ペイロードの実行を表す)に対して構造的な安全検証を行います。たとえば、「重複チェック」を通じて、実行時ペイロードに重複するエントリがないことを確認し、「制限チェック」を通じて、実行時ペイロード内の各フィールドの長さが許可されたエントリの上限内にあることを確認します。
構造的なチェックに加えて、検証者の静的チェックは、信頼できないペイロードの意味的な堅牢性を確保するために、より複雑な分析手段を必要とします。
Moveの抽象解釈器を理解する:
線形および反復分析
Moveが提供する抽象解釈器は、バイトコード上で複雑な安全分析を実行するために設計されたフレームワークです。このメカニズムにより、検証プロセスがより精密かつ正確になり、各検証者は独自の抽象状態を定義して分析を行うことが許可されます。
実行を開始すると、抽象解釈器はコンパイルされたモジュールから制御フローグラフ(CFG)を構築します。これらのCFG内の各基本ブロックは、「前状態」と「後状態」の一組の状態を維持します。「前状態」は基本ブロック実行前のプログラム状態のスナップショットを提供し、「後状態」は基本ブロック実行後のプログラム状態の説明を提供します。
抽象解釈器が制御フローグラフ内で戻り跳び(またはループ)に遭遇しない場合、単純な線形実行原則に従います:各基本ブロックは順次分析され、ブロック内の各命令の意味に基づいて前状態と後状態が計算されます。その結果は、プログラムが実行される過程での各基本ブロックレベルの状態の正確なスナップショットとなり、プログラムの安全属性を検証するのに役立ちます。
Move抽象解釈器の作業フロー
しかし、制御フローにループが存在する場合、このプロセスはより複雑になります。ループの出現は、制御フローグラフ内に戻り跳びの辺が含まれていることを意味し、戻り跳びの辺の源は現在の基本ブロックの後状態に対応し、戻り跳びの辺のターゲット基本ブロック(ループの先頭)は以前に分析された基本ブロックの前状態です。したがって、抽象解釈器は戻り跳びに関連する2つの基本ブロックの状態を注意深く統合する必要があります。
合併後の状態がループの先頭基本ブロックの既存の前状態と異なる場合、抽象解釈器はループの先頭基本ブロックの状態を更新し、この基本ブロックから分析を再起動します。この反復分析プロセスは、ループの前状態が安定するまで続きます。言い換えれば、このプロセスは、ループの先頭基本ブロックの前状態が反復の間に変わらなくなるまで繰り返されます。固定点に達すると、ループ分析が完了したことを示します。
Sui IDLeak検証器:
カスタム抽象解釈分析
元のMove設計とは異なり、Suiのブロックチェーンプラットフォームは「ターゲット」を中心にした独自のグローバルストレージモデルを導入しています。このモデルの顕著な特徴は、key属性(インデックスとしてブロックチェーンに保存される)を持つデータ構造は、必ずIDタイプをその構造の最初のフィールドとして持たなければならないことです。IDフィールドは変更できず、他のターゲットに移転することもできません。なぜなら、各オブジェクトはグローバルに一意のIDを持たなければならないからです。これらの特性を確保するために、Suiは抽象解釈器上にカスタム分析ロジックを構築しました。
IDLeak検証器、またはidleakverifierは、抽象解釈器と協力して分析を行います。これは独自のAbstractDomainを持ち、AbstractStateと呼ばれます。各AbstractStateは、複数のローカル変数に対応するAbstractValueで構成されています。AbstractValueを通じて、各ローカル変数の状態を監視し、ID変数が新しいものであるかどうかを追跡します。
構造体のパッケージ化の過程で、IDLeak検証器は新しいIDを1つの構造体にパッケージ化することのみを許可します。抽象解釈分析を通じて、IDLeak検証器はローカルデータフローの状態を詳細に追跡し、既存のIDが他の構造体オブジェクトに移転されていないことを確認します。
Sui IDLeak検証器の状態維持不一致問題
IDLeak検証器は、AbstractState::join関数を実装することでMove抽象解釈器と統合されています。この関数は、状態管理、特に状態値の統合と更新において不可欠な役割を果たします。
これらの関数の動作を詳しく確認します:
AbstractState::joinでは、この関数は別のAbstractStateを入力として受け取り、そのローカル状態を現在のオブジェクトのローカル状態と統合しようとします。入力状態内の各ローカル変数について、その変数の値をローカル状態内の現在の値と比較します(見つからない場合、デフォルト値はAbstractValue::Otherです)。これらの値が等しくない場合、「changed」フラグを設定し、最終的な状態の統合結果が変化したかどうかの基準とし、AbstractValue::joinを呼び出してローカル状態内のローカル変数の値を更新します。
AbstractValue::joinでは、この関数はその値を別のAbstractValueと比較します。それらが等しい場合、渡された値を返します。等しくない場合、AbstractValue::Otherを返します。
しかし、この状態維持ロジックには隠れた不一致の問題が含まれています。AbstractState::joinは新旧の値の違いに基づいて、状態の統合が変化したことを示す結果(JoinResult::Changed)を返しますが、統合更新後の状態値は依然として不変である可能性があります。
この不一致の問題は操作順序によって引き起こされます:AbstractState::join内での状態変更の判定は、状態更新(AbstractValue::join)の前に発生し、この判定は実際の状態更新結果を反映していません。
さらに、AbstractValue::join内で、AbstractValue::Otherは統合結果に決定的な役割を果たします。たとえば、旧値がAbstractValue::Otherであり、新値がAbstractValue::Freshである場合、更新された状態値は依然としてAbstractValue::Otherとなり、新旧の値が異なっていても、更新後の状態自体は変わりません。
例:状態接続の不連続性
これにより、不一致が生じます:基本ブロック状態の統合結果が「変更」と判定されても、統合後の状態値自体は変化しないということです。抽象解釈分析の過程でこのような不一致が発生すると、深刻な結果をもたらす可能性があります。抽象解釈器が制御フローグラフ(CFG)内でループに遭遇したときの動作を振り返ります:
ループに遭遇すると、抽象解釈器は戻り跳びのターゲット基本ブロックと現在の基本ブロックの状態を統合するために反復的な分析手法を採用します。統合後の状態が変化した場合、抽象解釈器はジャンプターゲットから再分析を開始します。
しかし、抽象解釈分析の統合操作が誤って状態統合結果を「変化」とマークし、実際には状態内部変数の値が変わらなかった場合、無限の再分析が発生し、無限ループを引き起こします。
不一致をさらに利用する
Sui IDLeak検証器で無限ループを引き起こす
この不一致を利用して、攻撃者は悪意のある制御フローグラフを構築し、IDLeak検証器を無限ループに誘導することができます。この精巧に構築された制御フローグラフは、3つの基本ブロックで構成されています:BB1、BB2、BB3。特に注目すべきは、BB3からBB2への戻り跳びの辺を意図的に導入してループを構築したことです。
悪意のあるCFG+状態は、IDLeak検証器内部での死ループを引き起こす可能性があります
このプロセスはBB2から始まります。このとき、特定のローカル変数のAbstractValueが::Otherに設定されます。BB2を実行した後、フローはBB3に移り、そこで同じ変数が::Freshに設定されます。BB3の終わりには、BB2に戻る跳びの辺があります。
この例の抽象解釈分析の過程で、前述の不一致が重要な役割を果たします。戻り跳びの辺が処理されると、抽象解釈器はBB3の後状態(変数が「::Fresh」)をBB2の前状態(変数が「::Other」)と接続しようとします。AbstractState::join関数はこの新旧の値の違いに気付き、「変化」フラグを設定し、BB2の再分析が必要であることを示します。
しかし、AbstractValue::joinにおける "::Other"の支配的な動作は、AbstractValueの統合後、BB2の状態変数の実際の値が依然として「::Other」であり、状態統合の結果が変わらなかったことを意味します。
したがって、このループプロセスが一度始まると、検証器はBB2およびそのすべての後続基本ブロックノード(この例ではBB3)を再分析し続け、無限に持続します。無限ループはすべての利用可能なCPUサイクルを消費し、新しいトランザクションに応答できなくなります。この状況は、検証器が再起動した後も続きます。
この脆弱性を利用することで、検証ノードはハムスターがホイールの上で無休止に走り続けるように無限ループに陥り、新しいトランザクションを処理できなくなります。したがって、この独特な攻撃タイプを「ハムスター・ホイール」攻撃と呼ぶことにしました。
「ハムスター・ホイール」攻撃は、Sui検証器を効果的に停止させ、結果としてSuiネットワーク全体を麻痺させることができます。
脆弱性の原因とトリガーのプロセスを理解した後、以下のMoveバイトコードを使用して具体的な例を構築し、実際の環境でこの脆弱性を成功裏にトリガーしました:
この例は、精巧に構築されたバイトコードを通じて、実際の環境で脆弱性をトリガーする方法を示しています。具体的には、攻撃者はIDLeak検証器内で無限ループをトリガーし、約100バイトのペイロードを利用してSuiノードのすべてのCPUサイクルを消費し、新しいトランザクションの処理を効果的に阻止し、Suiネットワークをサービス拒否に陥れることができます。
「ハムスター・ホイール」攻撃のSuiネットワークにおける持続的な危害
Suiのバグ報奨金プログラムは、脆弱性のレベル評価に厳格な規定を設けており、主にネットワーク全体への危害の程度に基づいて評価されます。「深刻(critical)」評価を満たす脆弱性は、ネットワーク全体を停止させ、新しいトランザクションの確認を効果的に妨げ、問題を修正するためにハードフォークが必要です;脆弱性が一部のネットワークノードのみをサービス拒否にする場合、最大でも「中危(medium)」または「高危(high)」脆弱性として評価されます。
CertiK Skyfallチームが発見した「ハムスター・ホイール」脆弱性は、Suiネットワーク全体を停止させ、公式に新しいバージョンをリリースしてアップグレード修正する必要があります。この脆弱性の危害の程度に基づき、Suiは最終的に「深刻」レベルに評価されました。「ハムスター・ホイール」攻撃が引き起こす深刻な影響の原因をさらに理解するためには、Suiのバックエンドシステムの複雑なアーキテクチャ、特にチェーン上のトランザクションの公開またはアップグレードの全プロセスを理解する必要があります。
Suiにおけるトランザクションの提出の相互概要
最初に、ユーザーのトランザクションはフロントエンドRPCを通じて提出され、基本的な検証を経てバックエンドサービスに渡されます。Suiのバックエンドサービスは、受信したトランザクションペイロードのさらなる検証を担当します。ユーザーの署名が成功裏に検証されると、トランザクションはトランザクション証明書(トランザクション情報とSuiの署名を含む)に変換されます。
これらのトランザクション証明書は、Suiネットワークの運用の基本的な構成要素であり、ネットワーク内の各検証ノード間で伝播されます。契約の作成/アップグレードトランザクションについては、ブロックチェーンに載せる前に、検証ノードがSui検証器を呼び出してこれらの証明書の契約構造/意味の有効性を確認します。まさにこの重要な検証段階で、「無限ループ」脆弱性がトリガーされる可能性があります。
この脆弱性がトリガーされると、検証プロセスが無期限に中断され、新しいトランザクションの処理能力が効果的に妨げられ、ネットワークが完全に停止します。さらに悪いことに、ノードが再起動した後もこの状況は続き、従来の緩和策では不十分であることを意味します。この脆弱性が一度トリガーされると、「持続的な破壊」が発生し、Suiネットワーク全体に持続的な影響を与えます。
Suiの解決策
CertiKからのフィードバックを受けて、Suiは迅速にこの脆弱性を確認し、重要な欠陥を解決するための修正プログラムをリリースしました。この修正プログラムは、状態の変更と変更後のフラグの間の一貫性を確保し、「ハムスター・ホイール」攻撃による重要な影響を排除しました。
上記の不一致を排除するために、Suiの修正にはAbstractState::join関数に対する小さくも重要な調整が含まれています。このパッチは、AbstractValue::joinを実行する前に状態統合結果を判定するロジックを削除し、まずAbstractValue::join関数を実行して状態を統合し、最終的な更新結果と元の状態値(old_value)を比較して統合が発生したかどうかのフラグを設定します。
これにより、状態統合の結果は実際の更新結果と一致し、分析プロセス中に無限ループが発生しなくなります。
この特定の脆弱性を修正するだけでなく、Suiは将来の検証器脆弱性の影響を軽減するための緩和策も展開しました。Suiのバグ報告に対する返信によれば、緩和策にはDenylistという機能が含まれています。
「ただし、検証器にはノードの設定ファイルがあり、特定のカテゴリのトランザクションを一時的に拒否することができます。この設定は、公開およびソフトウェアパッケージのアップグレードの処理を一時的に禁止するために使用できます。このバグは、公開またはソフトウェアパッケージのアップグレードtxの署名前にSui検証器を実行しているときに発生するため、拒否リストは検証器の実行を停止し、悪意のあるtxを破棄します。これらのtxタイプを一時的に拒否することは、100%有効な緩和策です(ただし、コードの公開またはアップグレードを試みている人のサービスを一時的に中断します)。
ちなみに、私たちはこのTX拒否リストの設定ファイルをしばらく前から持っていますが、証明書に対しても同様のメカニズムを追加しました。これは、以前に報告された「検証器の死ループ」脆弱性の後続の緩和策として機能します。このメカニズムにより、私たちはこの攻撃に対してより大きな柔軟性を持つことができます:私たちは証明書の拒否リスト設定を使用して、検証器が悪い証明書を忘れさせ(死ループを打破)、TX拒否リスト設定を使用して公開/アップグレードを禁止し、新しい悪意のある攻撃トランザクションの作成を防ぎます。この問題を考えさせていただき、ありがとうございます!
検証器はトランザクションを署名する前に、バイトコード検証のために限られた「ticks」(ガスとは異なる)を持っており、トランザクション内で公開されたすべてのバイトコードがこれらのticksの中で検証されない場合、検証器はそのトランザクションの署名を拒否し、ネットワーク上での実行を防ぎます。以前は、計測は選択された複雑な検証器のグループにのみ適用されていました。この問題に対処するために、私たちは計測を各検証器に拡張し、各tickの検証プロセスで検証器が実行する作業に制約を設けることを保証します。また、ID漏洩検証器内の潜在的な無限ループのバグも修正しました。」
--Sui開発者からの脆弱性修正に関する説明
要するに、Denylistは検証者が公開またはアップグレードプロセスを無効にすることで、検証器内の脆弱性の悪用を一時的に回避し、一部の悪意のあるトランザクションによる潜在的な破壊を効果的に防ぐことを可能にします。Denylistの緩和策が発効すると、ノードは自身の公開/更新契約機能を犠牲にすることで、引き続き機能し続けることができます。
まとめ
本記事では、CertiK Skyfallチームが発見した「ハムスター・ホイール」攻撃の技術的詳細を共有し、この新型攻撃がどのようにして重要な脆弱性を利用してSuiネットワークを完全に停止させるかを説明しました。また、Suiがこの重要な問題を修正するために行った迅速な反応を詳細に検討し、脆弱性修正およびその後の同様の脆弱性の緩和策についても共有しました。