著者: CertiK

 

以前、CertiK チームは、Sui ブロックチェーンに一連のサービス拒否の脆弱性を発見しました。これらの脆弱性の中で、新たに影響の大きい脆弱性が目立っています。この脆弱性により、Sui ネットワーク ノードが新しいトランザクションを処理できなくなり、ネットワーク全体が完全にシャットダウンされる可能性があります。

つい先週の月曜日、CertiK は、この重大なセキュリティ脆弱性を発見したことに対して、50 万ドルの SUI バグ報奨金を受け取りました。米国の業界で権威あるメディアである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 バリデーターと呼びます。

スイの荷物チェックの順番

上の図に示すように、バリデーターのほとんどのチェックは、CompiledModule (ユーザーが提供するコントラクト ペイロードの実行を表す) に対して構造レベルのセキュリティ検証を実行します。たとえば、「重複チェッカー」を使用してランタイム ペイロードに重複エントリがないことを確認し、「制限チェッカー」を使用してランタイム ペイロードの各フィールドの長さが許可されたエントリ制限内にあることを確認します。

構造レベルのチェックに加えて、検証者の静的チェックでは、信頼できないペイロードの堅牢性をセマンティック レベルで保証するために、さらに複雑な分析方法が必要です。

Move の抽象インタープリタについて学びます。

線形および反復分析

Move が提供する抽象インタープリタは、抽象解釈を通じてバイトコードの複雑なセキュリティ分析を実行するために特別に設計されたフレームワークです。このメカニズムにより、検証プロセスがより洗練され、正確になり、各検証者は分析用に独自の抽象状態を定義できるようになります。

実行を開始すると、抽象インタープリタはコンパイルされたモジュールから制御フロー グラフ (CFG) を構築します。これらの CFG の各基本ブロックは、一連の状態、つまり「事前順序状態」と「事後順序状態」を維持します。 「事前順序状態」は基本ブロックが実行される前のプログラム状態のスナップショットを提供し、「事後順序状態」は基本ブロックが実行された後のプログラム状態の説明を提供します。

抽象インタプリタが制御フロー グラフ内でジャンプ (またはループ) に遭遇しない場合、抽象インタプリタは単純な線形実行原理に従います。つまり、各基本ブロックが順番に分析され、ブロック内の各命令のセマンティクスに基づいて先行する命令が計算されます。シーケンシャル状態とポストシーケンシャル状態。その結果、実行中のプログラムの各基本ブロック レベルの状態の正確なスナップショットが得られ、プログラムのセキュリティ プロパティの検証に役立ちます。

抽象インタープリタのワークフローを移動する

ただし、制御フローにループがある場合、このプロセスはさらに複雑になります。ループの発生は、制御フロー グラフにバウンス エッジが含まれていることを意味します。バウンス エッジのソースは現在の基本ブロックの後続の状態に対応し、バウンス エッジの対象となる基本ブロック (ループ ヘッド) は以前に解析されたものです。基本ブロックのプリオーダー状態であるため、抽象インタプリタはジャンプに関連する 2 つの基本ブロックの状態を慎重にマージする必要があります。

マージされた状態がループヘッド基本ブロックの既存のプリオーダー状態と異なることが判明した場合、抽象インタプリタはループヘッド基本ブロックの状態を更新し、この基本ブロックから解析を再開する。この反復分析プロセスは、ループの事前状態が安定するまで継続されます。言い換えれば、このプロセスは、ループの先頭にある基本ブロックの事前順序状態が反復間で変化しなくなるまで繰り返されます。固定点に到達すると、ループ解析が完了したことを示します。

Ai IDLeak バリデーター:

カスタマイズされた抽象解釈分析

オリジナルの Move 設計とは異なり、Sui のブロックチェーン プラットフォームは、独自の「目標」を中心としたグローバル ストレージ モデルを導入しています。このモデルの注目すべき特徴は、キー属性を持つデータ構造 (チェーン上のインデックスとして保存される) は構造の最初のフィールドとして ID タイプを持たなければならないことです。各オブジェクトにはグローバルに一意の ID が必要であるため、ID フィールドは不変であり、他のターゲットに転送できません。これらのプロパティを確保するために、Sui は抽象インタープリターの上に一連のカスタム分析ロジックを構築しました。

IDLeak ベリファイア (id_leak_verifier とも呼ばれます) は、抽象インタープリターと連携して分析を実行します。これには、AbstractState と呼ばれる独自の一意の AbstractDomain があります。各 AbstractState は、複数のローカル変数に対応する AbstractValue で構成されます。 AbstractValue を通じて各ローカル変数のステータスを監視し、ID 変数が新しいかどうかを追跡します。

構造体のパッケージ化プロセス中、IDLeak バリデーターは新しい ID を構造体にパックすることのみを許可します。 IDLeak バリデーターは、抽象解釈分析を通じてローカル データ フローの状態を徹底的に追跡し、既存の ID が他の構造オブジェクトに転送されないようにします。

Sui IDLeak バリデーターの状態維持の不一致の問題

IDLeak バリデーターは、AbstractState::join 関数を実装することによって、Move 抽象インタープリターと統合されています。この関数は、状態管理、特に状態値のマージと更新において重要な役割を果たします。

これらの関数を詳しく調べて、その動作を理解してください。

AbstractState::join では、関数は別の AbstractState を入力として受け取り、そのローカル状態を現在のオブジェクトのローカル状態とマージしようとします。入力状態のローカル変数ごとに、変数の値をローカル状態の現在の値と比較します (見つからない場合、デフォルトは AbstractValue::Other です)。 2 つの値が等しくない場合は、最終状態のマージ結果が変更されたかどうかの基準として「変更済み」フラグを設定し、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 バリデーターを無限ループに陥らせる可能性があります。この慎重に構築された制御フロー グラフは、BB1、BB2、BB3 の 3 つの基本ブロックで構成されます。ループを構成するために、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 ネットワーク上でサービス妨害を引き起こす可能性があります。 。

スイネットワークにおける「ハムスターホイール」攻撃の永続的な被害

スイのバグ報奨金プログラムには、主にネットワーク全体への害の程度に基づいて、脆弱性レベルの評価に関する厳格な規制があります。 「重大」の評価を満たす脆弱性はネットワーク全体をシャットダウンし、新しいトランザクションの確認を効果的に阻止する必要があり、その脆弱性によって一部のネットワーク ノードがサービスを拒否するだけの場合は、問題を修正するためにハード フォークが必要になります。最大で「中リスク (中)」または「高リスク (高)」と評価されます。

CertiK Skyfall チームが発見した「ハムスターホイール」の脆弱性は、Sui ネットワーク全体をシャットダウンする可能性があり、アップグレードして修正するには新しいバージョンの正式リリースが必要です。脆弱性の重大度に基づいて、Sui は最終的にそれを「緊急」と評価しました。 「ハムスターホイール」攻撃の深刻な影響をさらに理解するには、Sui のバックエンド システムの複雑なアーキテクチャ、特にオンチェーン トランザクションのリリースまたはアップグレードのプロセス全体を理解する必要があります。

Sui でトランザクションを送信するための対話の概要

最初に、ユーザー トランザクションはフロントエンド RPC 経由で送信され、基本的な検証後にバックエンド サービスに渡されます。 Sui バックエンド サービスは、受信トランザクション ペイロードをさらに検証する責任があります。ユーザーの署名が正常に検証された後、トランザクションはトランザクション証明書 (トランザクション情報とスイの署名を含む) に変換されます。

これらのトランザクション証明書は、Sui ネットワークの運用の基本的な部分であり、ネットワーク内のさまざまな検証ノード間で伝播できます。コントラクト作成/アップグレード トランザクションの場合、検証ノードは、チェーンに追加する前に、これらの証明書のコントラクト構造/セマンティクスの有効性をチェックおよび検証するために、Sui バリデーターを呼び出します。 「無限ループ」の脆弱性が引き起こされ、悪用される可能性があるのは、この重要な検証フェーズ中にです。

この脆弱性が発生すると、検証プロセスが無期限に中断され、システムの新しいトランザクション処理能力が事実上妨げられ、ネットワークが完全にシャットダウンされます。さらに悪いことに、ノードが再起動された後も状況は依然として存在しており、従来の軽減策では十分とは言えません。この脆弱性が発生すると、「継続的な被害」が発生し、Sui ネットワーク全体に永続的な影響を及ぼします。

スイさんの解決策

CertiK からのフィードバックを受けて、Sui はすぐに脆弱性を確認し、重大な欠陥に対処する修正プログラムをリリースしました。この修正により、状態の変更と変更後のフラグの間の一貫性が保証され、「ハムスターホイール」攻撃の重大な影響が排除されます。

上記の矛盾を解消するために、Sui の修正には AbstractState::join 関数に対する小さいながらも重要な調整が含まれています。このパッチは、AbstractValue::join を実行する前に状態のマージ結果を決定するロジックを削除します。代わりに、最初に AbstractValue::join 関数を実行して状態のマージを実行し、最終的な更新結果を元の状態と比較することによってマージが発生するかどうかを設定します。値 (old_value) を変更します。

このようにして、状態のマージの結果は実際の更新の結果と一致し、分析プロセス中に無限ループが発生することはありません。

この特定の脆弱性を修正することに加えて、Sui は将来のバリデーターの脆弱性の影響を軽減するための緩和策を導入しました。バグレポートのSuiさんの回答によると、軽減策にはDenylistと呼ばれる機能が含まれています。

「ただし、バリデーターには、特定のカテゴリのトランザクションを一時的に拒否できるノード構成ファイルがあります。この構成は、リリースやパッケージのアップグレードの処理を一時的に無効にするために使用できます。このバグのため、リリースまたはパッケージに署名する前に、Sui を実行する必要があります。」拒否リストがバリデーターの実行を停止し、悪意のある TX を削除する間に TX をアップグレードする場合、これらの TX タイプのリストを一時的に拒否することは 100% 効果的な軽減策です (ただし、コードをリリースまたはアップグレードしようとするユーザーのサービスは一時的に中断されます)。

ところで、この TX 拒否リスト構成ファイルはしばらくの間使用されてきましたが、以前に報告された「バリデータの無限ループ」脆弱性に対するフォローアップ緩和策として、証明書にも同様のメカニズムを追加しました。このメカニズムを導入すると、この攻撃に対する柔軟性がさらに高まります。証明書拒否リスト設定を使用して、バリデーターに不正な証明書を忘れさせ (無限ループを解消)、TX 拒否リスト設定を使用してリリース/アップグレードを無効にします。これにより、新たな悪意のある攻撃トランザクションの作成を防ぎます。このことについて考えさせてくれてありがとう!

バリデーターは、トランザクションに署名する前にバイトコードを検証するための (ガスとは異なり) 限られた数の「ティック」を持っています。トランザクションで発行されたすべてのバイトコードを多くのティックで検証できない場合、バリデーターはトランザクションへの署名を拒否し、トランザクションの署名を防ぎます。ネットワーク上で実行されます。以前は、メータリングは、複雑なバリデーター パスの選択されたセットに対してのみ機能していました。この問題に対処するために、各ティックの検証プロセス中にバリデーターによって実行される作業に制約があることを保証するために、メータリングを各バリデーターに拡張します。また、ID リークバリデーターの潜在的な無限ループのバグも修正しました。 」

--Sui 開発者からのバグ修正に関する指示

全体として、Denylist を使用すると、バリデーターはバリデーターのエクスプロイトを一時的に回避し、リリースまたはアップグレードのプロセスを無効にすることで、一部の悪意のあるトランザクションによって引き起こされる潜在的な損害を効果的に防ぐことができます。 Denylist の緩和策が有効になると、ノードは自身のパブリッシュ/更新コントラクト機能を犠牲にして動作を継続できるようになります。

要約する

この記事では、CertiK Skyfall チームが発見した「ハムスター ホイール」攻撃の技術的な詳細を共有し、この新しい攻撃が主要な脆弱性を悪用して、Sui ネットワークを完全にシャットダウンさせる方法を説明します。さらに、この重大な問題を修正するためのSuiのタイムリーな対応も詳しく調べ、脆弱性の修正とその後の同様の脆弱性の緩和方法を共有しました。