BTC
ETH
HTX
SOL
BNB
View Market
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt

etherscan の 0day (ゼロデイ) 脆弱性のレビュー

星球君的朋友们
Odaily资深作者
この記事は約4717文字で、全文を読むには約7分かかります
etherscan に 0day (ゼロデイ) 脆弱性が見つかったらどうしますか?
AI要約
etherscan に 0day (ゼロデイ) 脆弱性が見つかったらどうしますか?

「etherscan で 0day を見つけたらどうしますか? 私は「ピンボール マシン」を作ることにしました。"——samczsun、パラダイムリサーチパートナー

注: 原作者は samczsun です。

私は仮説に異論を唱えるのが好きです。

私は不可能に挑戦したり、他の人が見逃しているものを見つけたり、見たことのないもので驚かせたりするのが大好きです。昨年、私は非常に複雑な Solidity の脆弱性に基づいて、Paradigm CTF 2021 のチャレンジを書きました。亜種は公的に公開されましたが、私が発見したエクスプロイトは実際に議論されることはありませんでした。その結果、これに挑戦しようとしたほとんどすべての人が、その一見不可能に見える性質に躓いています。

数週間前、パラダイム CTF 2022 の計画について話し合っていたとき、ゲオルギオスがパズルについてツイートし、キックオフ コールと同じ日にチャレンジを投稿するのはとてもクールだと思いました。ただし、これが単なる陳腐な挑戦であってはなりません。私はこの世のものではないもの、誰も見たことのないもの、人々の想像の限界を超えたものを望んでいます。 0day (ゼロデイ) 脆弱性を悪用する最初の Ethereum CTF チャレンジを作成したいと考えています。

1

0day (ゼロデイ) 脆弱性を作成する方法

セキュリティ研究者として、私たちは時間を最適化するために、いくつかの基本的な仮定を立てます。 1 つは、私たちが読んでいるソース コードが、分析しているコントラクトを実際に生成しているということです。もちろん、この仮定は、Etherscan などの信頼できるソースからソース コードを読んだ場合にのみ当てはまります。したがって、Etherscan に何かを誤って検証させる方法を見つけることができれば、それをめぐる非常に悪質なパズルを考案できるでしょう。

Etherscan の契約検証システムを活用する方法を知るために、いくつかの契約を検証する必要がありました。 Ropsten テストネットにいくつかのコントラクトをデプロイして、それらを投げて検証してみました。すぐに、以下のインターフェイスが表示されました。

正しい設定を選択し、次のページに進みました。ここで、契約のソースコードを提供するように求められます。

ソース コードを入力して確認ボタンをクリックすると、案の定、ソース コードが契約に添付されました。

仕組みがわかったので、検証プロセスを試してみましょう。私が最初に試みたのは、新しいコントラクトをデプロイし、foo を bar に変更し、元のソース コードでコントラクトを検証することでした。当然のことながら、Etherscan は私の契約の確認を拒否しました。

しかし、2 つのバイトコード出力を手動で比較すると、何か奇妙なことに気づきます。コントラクトのバイトコードは 16 進数である必要がありますが、一部はそうではないようです。

Solidity がデプロイされたバイトコードにコントラクトのメタデータを追加することは知っていますが、それがコントラクトの検証にどのような影響を与えるかを実際に考えたことはありませんでした。どうやら、Etherscan はメタデータのバイトコードをスキャンし、「この領域にあるものはすべて異なる可能性がありますが、それでも同じバイトコードであると考えます」というマーカーに置き換えているようです。

これは、潜在的なゼロデイ (ゼロデイ) エクスプロイトの有望な手掛かりのように思えます。 Etherscan をだまして非メタデータをメタデータとして解釈させることができれば、{ipfs} とマークされた領域にデプロイするバイトコードを微調整しながら、正当なバイトコードとして検証させることができるようになります。

作成トランザクションに任意のバイトコードを含める最も簡単な方法は、それらをコンストラクター パラメーターとしてエンコードすることです。 Solidity は、ABI エンコードされたフォームを作成トランザクション データに直接アタッチすることで、コンストラクター パラメーターをエンコードします。

ただし、Etherscan は非常に賢いため、あらゆる種類のメタデータ スニッフィングからコンストラクター パラメーターを除外します。コンストラクターのパラメーターがコード自体から分離されていることを示すために斜体で示されていることがわかります。

これは、何らかの方法で Solidity コンパイラーをだまして、埋め込みメタデータに似せるように制御するバイトのシーケンスを出力させる必要があることを意味します。ただし、これを理解するのは難しい問題のようです。深刻なコンパイラのラングリングを行わない限り、Solidity がどのオペコードまたはバイトを使用するかを制御することはほとんどできず、その後のソース コードは非常に疑わしいように見えます。

私はしばらくこれについて考えましたが、実際には、Solidity に(ほぼ)任意のバイトを発行させるのは非常に簡単であることに気づきました。次のコードにより、Solidity は 32 バイトの 0xAA を発行します。

bytes32 value = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;

勇気づけられて、私はすぐに一連の定数をプッシュする小さなコントラクトを作成して、埋め込まれたメタデータとまったく同じように見えるバイトコードを Solidity が出力できるようにしました。

嬉しいことに、Etherscan は私の契約の途中で IPFS ハッシュの存在をマークしました。

私はすぐに予想されるバイトコードをコピーし、IPFS ハッシュをいくつかのランダムなバイトに置き換えて、結果のコントラクトをデプロイしました。案の定、Etherscan は通常とは異なるバイト ビジネスを検討し、私の契約を検証することを許可しました。

この規約を適用すると、ソース コードでは、example() を呼び出すと単純なバイト オブジェクトが返されることが示唆されます。ただし、実際に呼び出してみるとこうなります。

$ seth call 0x3cd2138cabfb03c8ed9687561a0ef5c9a153923f 'example()'
seth-rpc: {"id":1,"jsonrpc":"2.0","method":"eth_call","params":[{"data":"0x54353f2f","to":"0x3CD2138CAbfB03c8eD9687561a0ef5C9a153923f"},"latest"]}
seth-rpc: error: code -32000
seth-rpc: error: message stack underflow (5 <=> 16)

Etherscan の 0day (ゼロデイ) 脆弱性を発見することに成功し、ソース コードが示唆するものとはまったく異なる動作をするコントラクトを検証できるようになりました。あとは、それを中心にパズル ゲームをデザインするだけです。

2

間違ったスタート

明らかに、この復号化ゲームは、Etherscan で見られるソース コードがコントラクトが実際にどのように動作するかを示唆するものではないという考えを中心に展開します。また、プレイヤーがトランザクションを直接リプレイできないようにしたいので、ソリューションはアドレスごとに一意である必要があります。最善の方法は明らかに署名を求めることです。

しかし、どのような状況でプレーヤーはデータに署名するよう求められるのでしょうか?私の最初のデザインは、単一の機能が公開された単純なパズルでした。プレイヤーは何らかの入力を使用して関数を呼び出し、解決策を思いついたことを証明するためにデータに署名します。入力がさまざまなチェックすべてに合格した場合、ソルバーとしてマークされます。しかし、その後数時間を費やしてデザインを具体化していくうちに、すぐに結果に不満を感じるようになり、非常に不格好で洗練されていないように感じ始め、このような貧弱なデザインのパズルに取り組むことに耐えられなくなりました。非常に素晴らしい 0day (ゼロデイ) 脆弱性のアイデアです。

正直に言うと、金曜日までにこれを終わらせることはできなかったので、少し寝ることにしました。

3

ピンボール パズル

週末にかけて最初のデザインを繰り返し試行しましたが、それ以上の進歩はありませんでした。私の現在のアプローチは壁にぶつかっているようで、それを認めたくないのですが、満足のいくものを望むなら最初からやり直さなければならないかもしれないことはわかっています。

結局、私はこの問題を第一原理から再検討していることに気づきました。私が欲しいのは、プレイヤーがさまざまな知識チェックを完了する必要があるパズル ゲームです。ただし、知識チェックを完了すること自体が勝利の条件であるとは主張しません。むしろ、それはプレイヤーが選択できる多くの道のうちの 1 つである可能性があります。おそらく、プレイヤーはパズル全体を通じてポイントを蓄積し、このバグを利用して何らかの報酬を得ることができます。勝利の条件は単純に最高スコアであるため、エクスプロイトの使用が間接的に奨励されます。

私が昨年設計したチャレンジ、Lockbox を思い出します。このチャレンジでは、プレイヤーは 6 つの異なる契約の要件を満たすために単一のデータ ブロックを構築する必要がありました。コントラクトは同じバイトに異なる制約を適用するため、プレーヤーはペイロードの構築方法を賢くする必要があります。ここでも同様のことをやりたいと考えていました。プレイヤーにデータの 1 つのブロブを送信するよう依頼し、特定の要件を満たすデータの特定の部分に基づいてポイントを付与するということです。

この時点で、私は基本的にピンブールについて説明していることに気づきました。これは、DEFCON CTF 2020 決勝中に直面した課題でした。 pinboool のギミックは、バイナリを実行すると、ピンボール マシン内でボールが跳ね返るように、実行が制御フロー グラフ上で跳ね返ることです。入力を適切に構成することで、特定のコードセクションを見つけてポイントを獲得できるようになります。もちろんバグはありますが、正直言ってそれが何なのか忘れてしまったので探すつもりもありません。さらに、私には悪用したい脆弱性がすでにありました。

私は機能するゼロデイ エクスプロイトに対処していたので、このパズルをできるだけ早く解決することにしました。結局、pinboool の仕組みを再理解するのに数時間を費やし、再実装するのに数日を費やしました。これでパズルのブラケットの問題は解決しました。あとはエクスプロイトを統合するだけです。

4

ゼロデイエクスプロイトの準備

Solidity に正しいバイトを出力させる方法は、いくつかの定数をロードし、Solidity に対応する PUSH 命令を発行させることでした。ただし、このような任意の定数は大きな危険信号になる可能性があるため、よりよく調和するものが必要です。また、すべての定数を 1 行でロードする必要がありますが、これを実際のコードで説明するのは困難です。

実際には 2 つのマジック バイト シーケンス (0xa264...1220 と 0x6473...0033) をハードコードするだけで済みましたので、3 番目の定数の代わりにそれらの間にコードを挟むことができるかどうかを確認することにしました。デプロイされたコントラクトでは、サンドイッチされたコードを他の命令に置き換えるだけです。

address a = 0xa264...1220;
uint x = 1 + 1 + 1 + ... + 1;
address b = 0x6473...0033;

いくつか実験した結果、オプティマイザが有効になっている場合に限り、これが可能であることがわかりました。そうしないと、Solidity は過剰な値のサニタイズ コードを発行します。これは許容できるので、コード自体の改善に進みます。

コードを変更できるのは 2 つのアドレス内だけですが、最後にぶら下がったアドレスが表示されるのは不自然なので、条件文で使用することにしました。 2 番目の条件の必要性も証明する必要があったので、最終的には少しのボーナス ポイントに投票しました。私は、tx.origin がハードコードされた値と一致するという最初の条件チェックを実行して、このコード パスをさらに追求する必要がないという最初の印象を与えました。

if (tx.origin != 0x13378bd7CacfCAb2909Fa2646970667358221220) return true;
state.rand = 0x40;
state.location = 0x60;
if (msg.sender != 0x64736F6c6343A0FB380033c82951b4126BD95042) return true;
state.baseScore += 1500;

ソース コードの準備がすべて整ったので、実際のバックドアを作成する必要があります。私のバックドアは、プレーヤーがエクスプロイトを正しくトリガーすることを確認し、トリガーしない場合はプロンプトを表示せずに失敗し、トリガーが成功した場合は報酬を与える必要があります。このエクスプロイトが簡単に再現できないようにしたかったので、プレイヤーに自分のアドレスに署名し、トランザクションで署名を送信することだけを要求することにしました。話を盛り上げるために、署名がトランザクション データのオフセット 0x44 (通常はボールが開始される位置) にあることを要求することにしました。これには、プレーヤーが ABI エンコーディングがどのように機能するかを理解し、ボール データを手動で別の場所に再配置する必要があります。

しかし、ここで大きな問題に遭遇します。このロジックすべてを 31 バイトの手書きアセンブリに収めることはまったく不可能です。幸いなことに、少し考えた後、さらに 31 バイトを処理できることに気付きました。結局のところ、実際の埋め込みメタデータには、Etherscan も無視する別の IPFS ハッシュが含まれています。

コードゴルフをいくつか行った後、機能するバックドアに到着しました。最初の IPFS ハッシュで、プッシュしたばかりのアドレスをすぐにポップし、次に 2 番目の IPFS ハッシュにジャンプします。そこで、呼び出し元をハッシュし、ecrecover を呼び出すようにメモリ/スタックを部分的にセットアップします。次に、最初の IPFS ハッシュに戻り、そこでスタックのセットアップを完了し、呼び出しを実行します。最後に、スコア乗数を (msg.sender == ecrecover()) * 0x40 + 1 に設定しました。これは、追加の分岐が必要ないことを意味します。

バックドアを特定のサイズにコーディングした後、私は自分の Rinkeby アドレスをツイートして蛇口からテストネット ETH を取得し、ツイートを見ている人に何かが起こっている可能性があることを示唆しました。次に、コントラクトをデプロイして検証しました。

あとは運命の相手が人目につかない裏口を発見するのを待つだけだ。

注: Rocket Pool のソフトウェア開発者、Kane Wallmann はこの記事の時点でこのパズルを解決しています。プロセスは次のとおりです: https://medium.com/@kanewallmann_71759/an-untrustworthy-pinball-machine-d9dcd07882c

この記事は Yuan Universe の Tao からのものであり、許可を得て転載しています。

この記事は Yuan Universe の Tao からのものであり、許可を得て転載しています。

ETH
Odaily公式コミュニティへの参加を歓迎します
購読グループ:
https://t.me/Odaily_News
チャットグループ:
https://t.me/Odaily_GoldenApe
公式アカウント:
https://twitter.com/OdailyChina
記事目次
Odailyプラネットデイリーアプリをダウンロード
一部の人々にまずWeb3.0を理解させよう
IOS
Android