Ethernautの良問を解説する

お知らせ
Table of Contents

概要

Ethernaut [1]とはSolidity/EVMベースのスマートコントラクトの脆弱性調査をCTFのようなゲームを通して体験できるサービスです。
スマートコントラクトの監査を行う際に必要な観点を26種類(2022年8月現在)のゲームを通じて学べます。
今回は、Ethernautの全問題の中から特に初見では難しいような問題を4つ紹介します。

問題

GateKeeper One

The Ethernaut
The Ethernaut is a Web3/Solidity based wargame played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'. The game is 100...

この問題は"gateOne", "gateTwo", "gateThree" という修飾子の条件を満たすような入力を求める問題です。gateOneとgateThreeは通常のプログラミングの知識でなんとかなりますが、gateTwoはEVMバイトコードの各ステップで消費されるガスの量を正確に把握しないと回答できないため、補助ツールが無いと厳しいです。しかし、どのツールを使えばいいかというヒントが与えられていません。
自分が回答した時はRemix IDE[2]というコントラクト開発の定番のツールを使いました。これを使うことでローカルの仮想マシン上でデプロイしたコントラクト実行の挙動をステップ毎に追うことができ、各ステップでのガス消費量をトラッキングすることができました。

デバッガの使い方は[5]などを参考にしてください。

MagicNumber

The Ethernaut
The Ethernaut is a Web3/Solidity based wargame played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'. The game is 100...

この問題は42という数字を返却するようなwhatIsTheMeaningOfLife()関数を実装するだけの簡単な問題です。しかし、EVMのバイトコードを直接編集して10バイトのコード長を持つコントラクトを作成する必要があります。
さらに、そのようなコントラクトはIDEから直接デプロイできないので、[3]のような記事を参考にして「コントラクトをデプロイする0x00...00 アドレスに送るdataをコードとして実行した結果、10バイトのコントラクトコードを返す」ようなバイト列を作成してブラウザコンソールから実行しました。
部分部分を見れば別に難しくないのですが、コントラクト作成の仕様などはそもそもEthereumや暗号通貨以外で類似の仕様が存在しないので大変でした。
この記事[6]が参考になります。

Alien Codex

The Ethernaut
The Ethernaut is a Web3/Solidity based wargame played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'. The game is 100...

この問題は古いSolidityのコントラクトに存在した「配列の長さを任意に変えられてしまうバグ」を突いたものです。現在最新のSolidityバージョンでは修正されています。
配列のインデックス計算が大変です。自分はほぼ同じ問題である[4]をヒントとして見ながら回答しました。

Puzzle Wallet

The Ethernaut
The Ethernaut is a Web3/Solidity based wargame played in the Ethereum Virtual Machine. Each level is a smart contract that needs to be 'hacked'. The game is 100...

この問題はSolidityのコードやdelegatecallの仕様に詳しくない人が見たら何がおかしいかまるでわからないと思います。要点としては delegatecall が呼び出し元のストレージを操作する時、変数自体ではなくコードの上から順に定義された順番だけを見てストレージを操作しているということに気づく必要があります。
プロキシを介した二重呼び出しでプロキシ側のストレージが操作されるのですが、コード上はownerになっている変数が実は pendingAdmin に代入した値を参照してしまうことになり、ここが脆弱性になっています。
さらにそれだけでなく、この問題では後半入金残高を二重計上させるために multicall [ deposit, multicall [depost] ] のような入れ子の実行コードを組み立てる場面もあります。
他の問題でもそうなのですが

  1. Remix IDEでコントラクトのabiを取得
  2. ブラウザコンソールでコントラクトを作成
  3. myContract01.methods.proposeNewAdmin("0xabcd...1234").encodeABI() のようなコードを実行して実際に実行するコードを取得
  4. 取得したコードをさらに別のメソッドに代入して新たにコードを作る

といった手順を踏む必要があります。

それと、全然本質的ではないのですが、Ethernautのコントラクトそのままではコンパイル出来なかったためgithubから直接依存ライブラリを引っ張ってきてコンパイルする必要がありました。

// import "@openzeppelin/contracts/math/SafeMath.sol";
// import "@openzeppelin/contracts/proxy/UpgradeableProxy.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.4/contracts/math/SafeMath.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.4/contracts/proxy/UpgradeableProxy.sol";

全体を通して広範な知識が求められるため、良い問題だと思いました。

まとめ

SolidityやEVMの仕様に詳しくなることができるので、コードのセキュリティ監査業務を行う必要が出てきた時はまずEthernautを一通りやってみるのがよいと思います。
「Ethereumの調査にそこまで時間を使いたくないが、どのくらいのレベルの複雑さなのかは一応知りたい」と思っているエンジニアの方でも、とりあえず上記の4つを試しに解いてみると、求められている技術のレベル感がなんとなく分かるのではないかと思います。

参考

[1] Ethernaut https://ethernaut.openzeppelin.com/
[2] Remix IDE https://remix.ethereum.org/
[3] https://leftasexercise.com/2021/09/05/a-deep-dive-into-solidity-contract-creation-and-the-init-code/
[4] https://github.com/Arachnid/uscc/tree/master/submissions-2017/doughoyte
[5] https://www.xdc.dev/daniel_weber_eaba2f02503a/steps-to-execute-solidity-smart-contract-using-remix-ide-2i2h
[6] https://blog.trustlook.com/understand-evm-bytecode-part-1/

タイトルとURLをコピーしました