見出し画像

セキュリティ脆弱性インシデントレポート 12/8

以下のブログを簡易翻訳しています。

我々は最近、オープンソースライブラリにおける2つの標準の使用にセキュリティ脆弱性があることを我々のコミュニティに通知した。この脆弱性は、当社の事前構築済みスマートコントラクトの一部を含む、web3業界全体のスマートコントラクトに影響を与えます。

この発表以来、私たちのチームによって構築されたツールを使用して、ユーザーを支援し、この悪用を防ぐために37のチェーンにわたって9,800以上のスマートコントラクトを緩和することに成功しました。このような進歩を遂げ、エコシステムの他の主要な参加者と対話した結果、今こそ脆弱性の詳細を共有する適切な時だと感じています。

脆弱性

11月20日、私たちの監査パートナーである0xMacroは、私たちのスマート・コントラクト・コードの監査と精査を含む厳格なセキュリティ・プロセスの一環として、私たちに脆弱性を通知しました。私たちのチームは、脆弱性の詳細を調査し、安全な緩和策を構築するために、たゆまぬ努力と積極的な活動を行いました。

ERC2271とMulticall(2つのサードパーティのオープンソース標準)を実装し、有効な信頼されたフォワーダーを持つコントラクトは、転送されたリクエストに含まれる悪意のあるcalldataを介したアドレス詐称の脆弱性があります。この脆弱性により、攻撃者は特権関数コールの実行や、あらゆるアカウントからのアセットコントロールを含むがこれに限定されない、不正なアクションを実行することができる。

当社の事前構築済みコントラクトの多くはERC-2771を実装しており、取引ガス料金のスポンサーシップを可能にしています。これには、信頼されたフォワーダーコントラクトがトランザクションの calldata に元の送信者のアドレスを追加することが含まれる。メタ・トランザクションを受け入れるコントラクトでは、msg.sender に依存するのではなく、元のトランザクショ ンの送信者を取得する別の方法を使用する。トランザクションが転送されると、_msgSender()関数はcalldataの最後の20バイトからメッセー ジ送信者を抽出する。msg.senderが信頼できるフォワーダーでない場合、msg.senderを返します。

function _msgSender() internal view virtual override returns (address sender) {
    if (isTrustedForwarder(msg.sender)) {
        // The assembly code is more direct than the Solidity version using `abi.decode`.
        assembly {
            sender := shr(96, calldataload(sub(calldatasize(), 20)))
        }
    } else {
        return super._msgSender();
    }
}

メタトランザクションは別として、多くの構築済みコントラクトはMulticallを利用し、1つのトランザクションで複数の関数をバッチ処理できるようにすることで、開発者のエクスペリエンスを向上させている。これには、複数のコールのトランザクションカルデータを受け取り、それらを繰り返し処理し、提供されたデータでセルフデリゲートコールを実行することが含まれる。

function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
    results = new bytes[](data.length);
    for (uint256 i = 0; i < data.length; i++) {
        results[i] = Address.functionDelegateCall(address(this), data[i]);
    }
    return results;
}

multicall()を介したデリゲート自身への各コールは、そのコールに関連する データのみを含む、別個の外部トランザクションとして扱われる。multicall()を介したデリゲート・コールは、そのコールに関連するデー タのみを含む、別個の外部トランザクションとして扱われる。ただし、msg.senderの値を含め、呼のコンテキストは保持される。

信頼されたフォワーダが multicall()を呼び出すと、そのフォワーダが _msgSender()を使用して開始するデリゲート呼 び出しは、その呼に送信者データが提供されているものと見なされ、それに従って使用 されます。

攻撃者が信頼されたフォワーダーを操作して、これら2つの関数を継承するコント ラクトを呼び出すシナリオでは、攻撃者はmulticall()内の各呼び出しに余分な送信者 データを注入することができ、それによって、呼び出し時に任意の希望する送信者 になりすますことができる。

また、OpenZeppelinによる脆弱性の公開はこちらで読むことができる。

なぜこの組み合わせが当社の事前構築済みスマート・コントラクトに存在するのか?

当社の事前構築済みスマート・コントラクトは、幅広いユースケースを想定して設計されており、ユーザー・エクスペリエンスのために最適化されている。各コントラクトは常に、コントラクト所有者がトランザクションのためのガスをスポンサーできるようになっている。これにより、クリエイターは自分のコントラクトとやり取りするユーザーのガス代を支払うことができ、プロジェクトへの参加を促すことができる。

Multicallは、複数のコントラクト機能呼び出しを1つのトランザクション呼び出しで一括処理する機能を提供する。

これらの機能自体は、単独で使用する場合はこの問題を引き起こさない。脆弱性が現れるのは、この2つを一緒に使ったときだけである。したがって、影響を受けるコントラクトは、これら2つの機能の組み合わせによる脆弱性を受け継いでいる。

調査中、私たちはこの組み合わせを使用している他の8つのプロトコルを特定し、契約を軽減するためのサポートを目的として、この脆弱性の影響を受ける可能性があるという事実を警告した。

契約緩和プロセス

脆弱性を緩和するには、マルチコールにパッチを適用し、フォワーダのコンテキストを考慮し、各 calldata ループの最後にトランザクションメッセージの送信者アドレスをエンコードする必要がある。

新しいデプロイメント(11月22日以降)のためのthirdweb構築済みスマートコントラクトの修復

function isTrustedForwarder(address forwarder) virtual returns (bool);

function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
		results = new bytes[](data.length);
		address sender = _msgSender();
		bool isForwarder = msg.sender != sender;
    for (uint256 i = 0; i < data.length; i++) {
				if (isForwarder) {
						results[i] = Address.functionDelegateCall(address(this), abi.encodePacked(data[i], sender));
        } else {
            results[i] = Address.functionDelegateCall(address(this), data[i]);
        }
    }
		return results;
}


脆弱性がさらに伝播するのを防ぐため、私たちは11月22日午後7時(PT)以降に作成されたビルド済みスマート・コントラクト用のパッチを作成した。悪意ある行為者に貴重な情報を与えてしまわないよう、脆弱性に関する情報をあまり明かさずにパッチを作成する必要がありました。これを達成するために、間接的なコードパスを通してパッチを作成した。具体的には、(フォワーダーを含む)あらゆるコントラクトがマルチコール関数を実行できないようにするパッチだ。
潜在的な問題を避けるため、スマート・コントラクト・ウォレットや他のコントラクトがコントラクト内でマルチコールを実行できないようにすることにした。スマート・コントラクト・ウォレットは通常、同じ機能を提供するバッチ実行機能をすでに持っている。さらに、バッチ実行は、必要であれば将来的にトラステッド・フォワーダー・コントラクトに統合することができる。

デプロイされたサードウェブ構築済みコントラクトに対する緩和策

ERC2771セキュリティの考慮に従い、私たちのERC2771Context実装の信頼されたフォワーダーアドレスは不変であり、デプロイ時にのみ設定することができます。私たちは、事前構築コントラクトに対する2つの異なる緩和策を発見しました。

アップグレード可能なダイナミック・パターン・コントラクトの場合、マルチコール・トランザクションを通じて以下の手順でトラステッド・フォワーダーを無効にすることで脆弱性を緩和する:

1.アップグレード可能な契約に、ストレージスロットの値を0に設定する無効化関数を追加する。
2.この関数を実行して、信頼済みフォワーダのストレージスロット値をリセットし、コントラクト内で効果的に無効化する。
3.契約から関数を削除し、緩和後に将来的に使用できないようにする。

[注意]任意のストレージスロットを上書きすることは破壊的な行為である。我々の緩和策では、ストレージスロットの値はオフチェーンで計算され、eth_getStorageAtを使用して検証される。修正は、オンチェーンで実行する前に、フォークされたネットワーク上で検証される。

function disable(bytes32[] memory _slots) external {
		for (uint256 i = 0; i < _slots.length; i++) {
		    bytes32 slot = _slots[i];
        assembly {
		        sstore(slot, 0)
        }
    }
}

アップグレード不可能な契約については、契約をロックダウンし、新しい契約に移行することが緩和策となる。ロックのプロセスは契約の種類によって異なるが、通常、以下のようなものがある:

1.ロックされたことを示すために、コントラクトのメタデータ名に[Locked]を追加する。
2.鋳造、送金、クレームなど、コントラクトの主要なアクションを無効にする。
3.すべてのロールを無効にする。

[注意]コントラクトでエスクローされた資産(流動性プール、ステーキングなど)は、コントラクトをロックする前に引き出すべきである。

トークン型契約をロックした後、オーナーは現在のトークン保有者をスナップショットし、新しい契約を展開し、トークンを正当な所有者に配布することができる。

緩和ツールの構築

脆弱性を認識した後、当社のセキュリティチームとエンジニアリングチームは、セキュリティパートナーとともに、影響を受けるすべてのサードウェブ契約向けに使いやすい緩和ツールを作成するために24時間体制で取り組みました。

当社の事前構築済みスマートコントラクトは、web3エコシステム全体で広く使用されています。私たちのプラットフォームが必ずしも連絡先情報を要求しないため、相当数のユーザーが連絡先情報を提供せずにこれらのスマートコントラクトをデプロイしていました。その結果、私たちが直接関与することなく、潜在的な脆弱性を検出して緩和するために、何千人ものユーザーが簡単に使用できるツールを提供する必要がありました。

このツールにより、契約管理者は(可能であれば)契約をアップグレードするか、上記のように契約をロックして移行することができる。非常に多くの種類のコントラクトに対応するシンプルで強力なツールを作成するためには、限られた時間枠の中で高度な技術的作業が必要だった。12月8日現在、9,800以上のスマート・コントラクトがこのツールを使って緩和されている。

私たちのインシデント対応チームは2つの作業グループに分かれました:

1.データワーキンググループは、オンチェーンデータの収集、充実、分析、監視に重点を置く。
2.緩和ワーキンググループは、脆弱な契約の緩和に焦点を当てる。

アウトリーチのためのデータ収集とエンリッチメント


デプロイされたコントラクトと、それに対応するプロキシ実装コントラクトアドレスのリストを取得するために、ファクトリーコントラクトアドレスにインデックスを付けました。しかし、EVMプラットフォームがあらゆるEVMネットワークでの契約展開をサポートしているため、影響を受ける契約のリストを収集するのは困難でした。私たちは、コントラクト展開の使用メトリクスに基づいて、26のネットワークにリストを絞り込みました。私たちがデータを持っているネットワークのリストは以下の通りです:

Arbitrum Nova、Arbitrum One、Avalanche C-Chain、BNB Smart Chain、Base、Celo、Ethereum、Fantom、Gnosis、Klaytn、Kroma、Linea、Manta Pacific、Mantle、Moonbeam、OKXChain、Optimism、PGN、Polygon、Polygon zkEVM、PulseChain、Scroll、Telos、Zora、opBNB、zkSync

データの質とデータの正確性を評価するため、収集したデータセットを外部のデータソースで検証した。しかし、インフラ・プロバイダーのサポートが限られていたため、一部のネットワークについてはデータの検証を実行することができなかった。

我々は、特定のネットワークについてデータ収集のブートストラップを行い、thirdweb以外の脆弱な契約を特定するために、データ基盤サービスを利用した。我々は、これらの脆弱なコントラクトのコントラクト・アドレスとプラットフォーム名を、ライブラリのメンテナへのセキュリティ開示に含めた。

契約展開リストを最終決定した後、社内外のデータソースから、すべての契約に名前、数量、金額、分析情報を付加した。

この充実したデータセットを使って、リスクを軽減するためのアウトリーチ対象者リストを作成した。一部の影響を受けるユーザーについては、一般に公開されている連絡先情報をインターネット上で検索した。

スナップショット

スナップショットツールは2種類のスナップショットを取得します:

1.契約メタデータの状態:契約メタデータ、パーミッション、トークンのロイヤリティ情報など。
2.トークン残高スナップショット:トークンの種類(ERC20、ERC721、ERC1155)に応じて、ウォレットの残高とそれぞれのトークンID。

スナップショットは当社の緩和戦略の重要な要素であり、ほとんどの契約タイプの移行に必要である。私たちはエンリッチされたデータを使用し、複数のファーストおよびサードパーティのインフラストラクチャ・プロバイダーと統合して、必要なデータを収集しました。

当社のツールは、指定したブロック番号で関連する契約状態をスナップショットすることができます。不運にもコントラクトが悪用された場合、コントラクトの状態を最後に確認された良好な状態に復元することができる。

当社のスナップショット・ツールは、6000万トークンを超える最大級のNFTコレクションをサポートする必要がありました。どのインフラストラクチャ・プロバイダーもこのような大規模なコレクションをスナップショットすることができなかったため、大規模なコレクションを処理するために当社のアプローチを見直しました。

移行コントラクト

よりシームレスなマイグレーションを可能にするため、イニシャライザーで以前にデプロイされたコントラクト・アドレスを受け入れる9つのマイグレーション・コントラクトを構築した:

・ドロップERC1155M
・ドロップERC20M
・ドロップERC721M
・ロイヤルティカードM
・オープンエディションERC721M
・サインドロップ
・トークンERC1155M
・トークンERC20M
・トークンERC721M

これらの契約における注目すべき変更点:

1.コレクションとトークンのメタデータ、請求条件など、初期化時に以前に構成された設定を新しいコントラクトにコピー。
2.提供されたMerkle証明または認可されたマイグレーターの役割のウォレットを使用して、以前の保有者にトークンを配布するマイグレート機能を追加しました。
3.ERC2771の統合を削除し、ERC4337を採用。

新しいマイグレーション契約は、監査パートナーとエコシステムパートナーによってレビューされました。

モニタリング

脆弱性が悪用されたときにアラートを出すために、モニタリングに2つの異なる戦略を導入した。トランザクションの警告はデータベースに保存され、コントラクトのスナップショットが、脆弱性が悪用される直前にコントラクトを元の状態に戻そうと試みることができる。

検出

私たちの分析では、いくつかのコントラクトが、基礎となる実装アドレスとコントラクト・バイトコードを共有しているが、初期化設定、特に信頼されたフォワーダーに設定された値の違いにより、同じ脆弱性の影響を受けていないことを発見した。

複数のEVMネットワークにまたがるスケールで脆弱性を検出するために、コントラクトに特化した脆弱性を検出するツールを構築した。このツールは、コントラクトのバイトコードに対する静的解析と、コントラクトにおける脆弱性の影響度を評価するための一連のトランザクションをシミュレートする動的解析の組み合わせによって動作する。現在のところ、検出ツールはサードウェブ・コントラクトにのみ最適化されている。

信頼と安全性

信頼と安全性は、悪意のある利用を防ぐことを目的としたミティゲーション・ツールを構築する際の最優先事項でした。私たちのツールのセキュリティを強化するために、私たちはウォレットアドレスの所有権を確認するために「Sign In With Ethereum (SIWE)」による認証をユーザーに要求しています。すべての緩和ロジックと操作は、認証されたAPIエンドポイントを通じてのみアクセス可能です。

ウォレットの所有権を証明した後、ツールはウォレットに関連する脆弱なコントラクトのリストを提供し、所有者はコントラクトのステータスを確認し、緩和を開始することができる。

また、主要なマーケットプレイスやブロックエクスプローラーと緊密に連携し、影響を受けるスマートコントラクトの最新リストを提供することで、彼らが自社で安全対策を実施できるようにしました。

Mitigation(ミティゲーション)

選択した契約タイプに応じて、さまざまな軽減策がユーザーに提示される。

アップグレード可能なコントラクトの場合、ツールは移行に必要なすべての calldata を単一のマルチコール・トランザクションにまとめます。これにより、コントラクト所有者は、コントラクトが中間状態にあるウィンドウを残すことなく、単一のトランザクションでアトミックにアップグレードを実行できる。

アップグレードが不可能な契約については、ツールは、契約を緩和するために必要なステップを通じて契約所有者をガイドします。ロック・ステップの間、ツールは、コントラクトをロックダウンするためにリセットする必要があるすべてのコントラクトの状態をスキャンする。次に、すべてのコールデータを単一のマルチコール・トランザクションにまとめ、契約オーナーが単一のトランザクションで契約をロックできるようにする。コントラクトをロックした後、オーナーには、コントラクトをスナップショットするか、独自のスナップショットをアップロードするオプションが与えられます。新しいマイグレーション・コントラクトをデプロイするには、ロックされたコントラクト・アドレスとトークン所有の証明となるスナップショット・メルクル・ルートが必要です。新しいコントラクトをデプロイした後、トークンの配布には2つのオプションがあります:(1)すべてのトークンの自動エアドロップ、または(2)トークン所有者は提供された請求リンクから請求することができます。

一般公開の前に、フォークされたネットワークを使用してテスト環境を構築し、展開前に安全かつ慎重にツールをテストした。さらに、パートナーの契約上でツールをテストするために、少人数のパートナー・グループに参加してもらった。

サポート

  • スマートコントラクトの緩和プロセスをできるだけスムーズにするため、私たちは文書ビジュアルガイドを作成し、スマートコントラクト所有者に24時間体制でサポートを提供するグローバルサポートチームを発足させました。

今後に向けて

脆弱なスマートコントラクトを緩和するために迅速かつ積極的に対応してくれたコミュニティに感謝したい。私たちは、すべての脆弱なスマートコントラクトの所有者が緩和プロセスに取り組んでいる間、サポートを続けます。

私たちは、web3のためにクラス最高の開発者ツールを構築するという私たちの使命に引き続きコミットし、これは業界全体のセキュリティのベストプラクティスを強化する機会であると信じています。このプロセスについてさらにご質問がある場合は、support@thirdweb.comまで。

この記事が気に入ったらサポートをしてみませんか?