投稿者: Kong@ スローミストセキュリティチーム
背景
6 月 9 日、Optimism と Wintermute は両方とも発表を行い、2,000 万の OP トークンが失われた事件をコミュニティに公開しました。オプティミズムは流通市場におけるOPの流動性サービスの提供をWintermuteに依頼し、2,000万のOPトークンをWintermuteに提供する予定です。このトークンを受け取るために、Wintermute は Optimism にマルチシグネチャ アドレスを与え、Optimism がテストで 2 つのトランザクションを送信し、Wintermute がそれらが正しいことを確認した後、Optimism は 2,000 万 OP をこのアドレスに転送しました。 Optimism がトークンを転送した後、Wintermute はこれらのトークンを制御する方法がないことに気づきました。提供したマルチシグネチャ アドレスは当面イーサリアム メインネットにのみ展開されており、Optimism ネットワークにはまだ展開されていなかったためです。 Wintermuteはすぐに修復作業を開始しましたが、攻撃者はこの脆弱性に気づき、Wintermuteよりも先にOptimismネットワークのアドレスにマルチシグネチャを展開し、2,000万トークンの制御に成功しました。そこで問題は、なぜそのような抜け穴があるのかということです。
予備知識
まず、トランザクション署名が [EIP155] 標準に準拠しているかどうかを判断する必要があります。[EIP155] 標準に準拠した署名は、9 つの RLP エンコーディング要素 (nonce、gasprice、gas、to、value、data、chainid) をハッシュします。 , 0, 0)。ここで、chainid が含まれるため、[EIP155] 準拠の署名の v 値は {0,1} +chainid * 2 + 35 となります。 [EIP155] 標準に準拠していない署名の場合、6 つの要素 (nonce、gasprice、gas、to、value、data) のみをハッシュするため、署名後の v の値は {0,1} + 27 になります。異なるチェーンは異なるチェーン ID を定義し、異なるチェーン ID は異なる v 値を取得します。 ECDSA によれば、v の値が異なると、r と s の値が同じであっても、署名によって復元される公開鍵も異なることがわかっています。したがって、[EIP155] 標準を満たすトランザクションは、他のチェーンでは正常に再生できません。
[EIP2718] が新しいトランザクション形式 0x02 || RLP([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list,signature_y_parity,signature_r,signature_s]) を導入したことは言及する価値があります。chainid は個別にエンコードされます。署名 v 値には含まれません。署名 v 値は単純なパリティ ビットとしてのみ使用されるため、現在のトランザクション署名によって取得される v 値は 0 または 1 になります。
トランザクションのリプレイ
上記のトランザクション署名構造を理解すると、署名 v の値 27 または 28 が異なるチェーンで再生できることが明確にわかります。では、異なるチェーンでリプレイするにはどうすればよいでしょうか?これはトランザクションの送信と何ら変わりません。必要なのは、元のトランザクションのコンテンツを他のチェーンに送信することだけです。
Wintermute での 2,000 万 OP トークンの盗難を例に挙げます。攻撃者は、Gnosis Safe が Factory コントラクトを展開するトランザクションをリプレイしました。ここでは、ノンス 3 を使用して Gnosis Safe Deployer 3 トランザクションを再生してみます。
より簡単な方法は、最初に Etherscan を通じて元のトランザクションを取得することです。
次に、Optimistic の eth_sendRawTransaction [RPC] を介して直接実行します。
(https://eth.wiki/json-rpc/API) 送信するインターフェース。
元のトランザクションの内容を直接取得できない場合は、最初に eth_getTransactionByHash を渡すことができます。
トランザクションコンテンツを取得するための[RPC](https://eth.wiki/json-rpc/API)インターフェース。
次に、RLP はトランザクション コンテンツをエンコードして、元のトランザクション コンテンツを取得します。
次に、Optimistic の eth_sendRawTransaction [RPC] を渡します。
(https://eth.wiki/json-rpc/API) 送信するインターフェース。
参考:
https://eips.ethereum.org/EIPS/eip-155
https://eips.ethereum.org/EIPS/eip-2718
https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
https://github.com/ethereum/go-ethereum/blob/master/core/types/transaction_signing.go