Mythril Classic: Ethereumのスマートコントラクト解析ツール



この記事は、ヤフー株式会社の2018年新卒有志でつくる[Yahoo! JAPAN 18 新卒 Advent Calendar 2018](https://adventar.org/calendars/3388)の17日目の記事です。
昨日は @hiroaki-dev さんによる「[ViewPagerの先読みデータ取得をやめる](https://qiita.com/hiroaki-dev/items/a26adac5c7523073ae09)」でした。

Mythril Classicの部分まで飛びたい場合は[こちら](https://qiita.com/kyamuise/items/c7dc8cde4565c5db42df#スマートコントラクト解析ツール-mythril-classic)

## はじめに
こんにちは、私はYahoo! JAPANの2018年の新卒の山本です、普段ヤフーでは社内向けデータ分析基盤の開発運用を行っています。

しかし今回はヤフーでの業務とは何の関係ない、Ethereumのことを書きたいと思います。ヤフーでは申請を正しく行えば副業が可能ですので、こちらの記事は副業として行っていたことに関連しています。

ヤフーにおける副業に関してはこんな記事が弊社の公式メディアLinoticeに掲載されているため、興味のある方はのぞいてみてください。
[ヤフーは副業制度があるけど、本当に副業してる人はいるの？ エンジニア・デザイナーに話を聞いてきた](https://linotice.tumblr.com/post/162308767709/20170627)

## Ethereumって？
前置きが長くなりましたが、ここからが本文です。
この記事を読んでくれている方の中にはそもそもEthereumって何？という疑問を抱く方もいらっしゃるかもしれません。

一言で説明すると「任意のプログラムを実行し、任意の情報を記録できるようにしたブロックチェーン」です。
一番有名なブロックチェーンであるビットコインは、基本的にコイン(BTC)の情報しかチェーンに記録できませんが、Ethereumなら、開発者が作ったプログラムをチェーン上にデプロイし、そのプログラムを通じて任意の情報をブロックチェーンに記録させることができます。ブロックチェーンでプログラムを実行することにより、処理結果がブロックチェーン自身によって証明され、対改ざん性や非中央集権性を持つことになります。
この、Ethereumにデプロイされ、情報を記録するプログラムのことを"スマートコントラクト"、または単に"コントラクト"と呼びます。

ちなみに今年のQiitaには[Ethereum Advent Calendar](https://qiita.com/advent-calendar/2018/ethereum)もあるみたいですよ。

## Ethreumとセキュリティ
Ethereumはブロックチェーンですので、Etherと呼ばれる内部通貨(コイン)を使っています。このコインは他のアドレスに送金することができ、支払いや投資などに用いられます。また、スマートコントラクトで処理を実行するためには必ずGasと呼ばれる処理手数料の支払いが必要です。Gasの支払いにもEtherが使用されます。

つまり、**Ethereum上で何か作るときには必ずお金が関わってくるのです**。

そこで大事になってくるのがセキュリティ。通常のWebシステムでもセキュリティが大事なのはもちろんですが、スマートコントラクトの場合はセキュリティの穴が、開発者やユーザーの資産を失うことにダイレクトにつながってしまうため、より一層セキュリティに気を配らなければなりません。

## スマートコントラクト解析ツール Mythril Classic
さて、今回の本題であるMythril Classicです。
GitHub: https://github.com/ConsenSys/mythril-classic

Mythril Classicはスマートコントラクトを解析して、セキュリティ上の問題を洗い出してくれるツールです。
他の言語にも似たようなツールはありますね。

Mythril Classicには便利な機能がたくさんあるのですが、今回はコントラクトの解析に絞って使い方を紹介します。

### インストール
インストール方法は[公式Wiki](https://github.com/ConsenSys/mythril-classic/wiki/Installation-and-Setup)に書かれてあるとおりです。
macOSなら以下のようにHomebrewでインストールできます。

```bash
$ brew update
$ brew upgrade
$ brew tap ethereum/ethereum
$ brew install leveldb
$ brew install solidity
$ pip3 install mythril
```

### できることその1: Solidityファイルを解析
Solidityはスマートコントラクトを記述するためのプログラミング言語です。Mythril Classicの一番基本的な機能はSolidityで書かれたコントラクトのソースコードをコンパイルし、解析してくれるものです。

Solidityコード解析は以下のように`-x`オプションをつけて行います。

```bash
$ myth -x MyContract.sol
```

解析の結果、問題が見つからなかった場合は以下のようなメッセージが表示されますが、検出された場合はそれぞれの問題がリストとなって表示されます。

```
The analysis was completed successfully. No issues were detected.
```

Solidityファイルを解析する際に気をつけなければいけないのはSolidityコンパイラのバージョンです。現在Solidityをインストールすると最新版である0.5系が入ります。しかし0.5系がリリースされてからまだ1カ月もたっていないため、既存の0.4系以前で書かれたコントラクトはコンパイルできません。

実行時に他のバージョンのコンパイラを自動的にインストールしてくれる`--solv`オプションが`myth`コマンドにあるのですが、サポートされるOSが限られており、 私の環境であるMojaveでは動作しませんでした。(2018年12月6日現在)
古いバージョンのコードをコンパイルする場合は適するバージョンのSolidityコンパイラを手動でインストールしましょう。

今回は0.5系に対応したシンプルなコントラクトを作成してMythril Classicに解析させてみました。コントラクトで行っている処理はSolidityの文法を知らなくてもJavaなどの他の言語を書いたことがある方は直感的に理解できるかと思います。

```Sample.sol
pragma solidity 0.5.1;

contract Message {

    uint256 num;

    constructor() public {
        num = 3000;
    }

    function increse(uint256 _val) external {
        num += _val;
    }

    function decrease(uint256 _val) external {
        num -= _val;
    }
}
```

このコントラクトをMythril Classicで解析してみます。
このシンプルなコントラクトでも解析に2分くらいかかりました。どのくらいの深さまで探索するかは`--max-depth`で指定できるので時間とカバレッジのバランスを考えて調整してみてください。数字の小さい方が解析は早いですが、カバレッジが低くなります。
以下が実行コマンドと解析結果です。

```bash
$ myth -x contracts/Sample.sol --max-depth 12
==== Integer Underflow ====
SWC ID: 101
Type: Warning
Contract: Message
Function name: decrease(uint256)
PC address: 190
Estimated Gas Usage: 615 - 710
The subtraction can result in an integer underflow.

--------------------
In file: contracts/Sample.sol:16

num -= _val

--------------------

==== Integer Overflow ====
SWC ID: 101
Type: Warning
Contract: Message
Function name: increse(uint256)
PC address: 208
Estimated Gas Usage: 637 - 732
This binary add operation can result in integer overflow.

--------------------
In file: contracts/Sample.sol:12

num += _val

--------------------
```

各項目の意味は以下のとおりです。

|項目|意味|
|:--|:--|
|SWC ID|脆弱性のIDです <br/> [SWC registry](https://smartcontractsecurity.github.io/SWC-registry/)に一覧があります|
|Type|Warning, Informationalなどの脆弱性のタイプです|
|Contract|脆弱性が見つかったコントラクト名です|
|Function name|脆弱性が見つかった関数名です|
|PC address|脆弱性が見つかった実行位置です<br/>一般のプログラムで言うメモリアドレスのようなものです|
|Estimated Gas Usage|処理を実行するのに必要なGas(処理手数料)の推測値です|

今回はオーバーフローとアンダーフローの警告が出ていますね。これらはその名の通り「型が表現できる最大値を超過、最小値を下回る危険性がありますよ」という警告です。

これらの警告のSWC IDはともに101で、こちらにその詳細が書かれており、どのように修正したら良いのかの例も載っています。
https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-101


これらはかなり危険性が高く、オーバーフローが原因で不正にコイン[^1]が発行されてしまうなどの事故が起こっています。
https://blockchain.gunosy.io/entry/erc20-token-Vulnerability

[^1]: コイン表記していますが、厳密にはトークンです。初心者向け記事のためコインと表記しています。

[SWC registryの例](https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-101#overflow-simple-add-fixedsol)を参考に元のコードを改善したのが以下です。加算(減算)したあとに値チェックを挿入しました。こちらのコードを再びMythril Classicにて解析しても同じ警告が出ますが、対策は行われているので大丈夫です。Mythril Classicはそこまではチェックしてくれないようです。

```Sample.sol(対策済み)
pragma solidity 0.5.1;

contract Message {

    uint256 num;

    constructor() public {
        num = 3000;
    }

    function increse(uint256 _val) external {
        uint256 incresed = num + _val;
        require(incresed >= num, "Overflow");
    }

    function decrease(uint256 _val) external {
        uint256 decreased = num - _val;
        require(decreased <= num, "Underflow");
    }
}
```

### できることその2: すでにデプロイされたコントラクトを解析
これがMyrthril Classicの大きな特徴です。
スマートコントラクトを実際に動作させるにはEthereumブロックチェーンにデプロイするのですが、デプロイされたコントラクトは公開されるため、全世界の誰もが見ることができます。つまり、他の人がデプロイしたコントラクトでもMythril Classicによって解析を行うことができるのです。

デプロイ済みのコントラクトを解析するには`-x`に加え、`-a`オプションでコントラクトのアドレスをわたします。

```bash
$ myth -xa 解析したいコントラクトのアドレス
```

私が以前遊びで[mugen-money](https://github.com/kyamuise/mugen-money)という何の価値もないコインのコントラクトを作成し、Rinkebyというテストネット(公開された検証環境)にデプロイしたものがあります。今回はそちらを解析してみます。
`--rpc` は、どのEthereumクライアントを使ってチェーンの情報にアクセスするかを指定するオプションです。クライアントについて詳しく説明すると長くなるので割愛しますが、今回は何も用意しなくても使えるINFURAというものを使っています。`infura-rinkeby`は「INFURAを使ってRinkebyテストネットに接続する。」という意味です。

以下が実行コマンドと解析結果です。

```bash
$ myth -xa 0xab01e34b9F879c1c7F9897E433B925896D4287a2 --max-depth 10 --rpc infura-rinkeby
==== Exception state ====
SWC ID: 110
Type: Informational
Contract: 0xab01e34b9F879c1c7F9897E433B925896D4287a2
Function name: transferFrom(address,address,uint256)
PC address: 1978
Estimated Gas Usage: 1372 - 3027
A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.
--------------------
```

さきほどのコントラクトを解析したときと異なるのはContractの部分がコントラクト名ではなくアドレスになっていることと、該当部分のコードが表示されていないことです。これは、今回解析したコントラクトがすでにコンパイルされてデプロイされたものであり、ソースコードが取得できないためです。

今回検出された問題はSWC 110で、詳細はこちらです。
https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-110

TypeがInformationalなので、脆弱性というほどではないかもしれません。内容としては「例外を投げ、処理をロールバックする関数である`assert()`はコード中のバグチェックのために用いるべきであって、入力値のバリデーションのために用いてはいけない。」という内容です。`assert()`を使用するとGas(処理手数料)が余計にかかってしまうという問題があります。
検出されたのは自分で書いた部分のコードではなく[OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-solidity)という有名なライブラリの部分でした。私がこのコントラクトをデプロイしたときのバージョンのzeppelinでは`assert()`が使用されていた部分がいくつかあったのですが、現在はすべて`require()`という別の関数に置き換えられていますので、これからzeppelinを使用する場合は大丈夫です。また、現状のままでも`assert()`はバリデーションではなくバグチェックのために挿入されており、基本的に到達しない部分なので問題ありません。assertとrequireについて詳しく知りたい方は公式ドキュメントをご覧ください。
[Error handling: Assert, Require, Revert and Exceptions](https://solidity.readthedocs.io/en/develop/control-structures.html#error-handling-assert-require-revert-and-exceptions)

### できることその3: 処理内容を可視化
最後に紹介するのはスマートコントラクトの制御フローをグラフとして書き出す機能です。こちらはセキュリティスキャンではないですが、opcode単位で処理を追うことができます。opcodeとはスマートコントラクトの処理の最小単位です。

```bash
$ myth MyContract.sol -g graph.html
```

先程の「できること１」で用いたSample.solを解析してみます。

```bash
$ myth contracts/Sample.sol --max-depth 15 -g graph.html
```

出力されたのは以下のようなグラフです。
![スクリーンショット 2018-12-09 23.37.47.png](https://qiita-image-store.s3.amazonaws.com/0/16445/f7ab3ff3-6b75-6b18-7a12-83fd6457e14e.png)
このままでは何が表されているか分かりませんね。拡大してみましょう。
![スクリーンショット 2018-12-09 23.38.00.png](https://qiita-image-store.s3.amazonaws.com/0/16445/0377321e-59ae-1ad8-11be-3baf7fd7dd3d.png)
このように、処理がどのように進み、分岐していくかを可視化できます。各行の右側にある数字がPC addressで、右側のにあるのがopcodeです。

デプロイされたコントラクトでも同じことができます。先程の「できること２」で用いたmugem-moneyのコントラクトを解析してみます。

```bash
$ myth -a 0xab01e34b9F879c1c7F9897E433B925896D4287a2 --max-depth 15
 --rpc infura-rinkeby -g graph.html
```

グラフの中で注目してほしいのは以下の部分です。右上のノードに`1978 ASSERT_FAIL`という表示があります。先程このコントラクトを解析したときにPC addressが1978で問題が検出されたと表示されていましたが、それが制御フローのなかでどの部分に当たるかをグラフ化することによって特定できます。

![スクリーンショット 2018-12-09 23.17.28.png](https://qiita-image-store.s3.amazonaws.com/0/16445/acf94bda-bd90-e7e4-a276-798bf7fc3e9b.png)

## まとめ
以上簡単でしたが、Mythril Classicの紹介でした。

スマートコントラクト開発入門の記事はQiitaをはじめ、インターネット上でかなり多く出てきているため、初心者が困ることは少なくなりました。しかし、セキュリティ関連のツールの記事はまだ少なかったため、入門したあとにセキュリティにも気をつけてほしいという思いでこの記事を書かせていただきました。ぜひMythril Classicなどのツールを活用して安全なスマートコントラクト開発を行っていただければと思います。

最後まで読んでいただきありがとうございました！


## 参考文献や関連情報の紹介
### Mythril Classic Wiki
https://github.com/ConsenSys/mythril-classic/wiki
当記事は公式wikiを参考に、自分の環境で検証しながら書きました。今回用いたMythril Classicのバージョンは`v0.19.8`です。

### Mythril Platform
https://mythril.ai
Mythril Classicは以前は単にMythrilという名前でした。今ではMythrilはMythril Platformとして、スマートコントラクトのセキュリティを高めるより広範なプロジェクトの名前となったため、ツールの名前はMythril Classicとなりました。
Classicとついていますが、開発が止まったわけではありません。

### SWC-registry
https://smartcontractsecurity.github.io/SWC-registry/
SWCはSmart Contract Weakness Classificationの略で、スマートコントラクトで発生しうる脆弱性のリストです。Mythril Classicではこのリストを元に脆弱性が検出されます。記事中で例に出したもの以外にも多くの項目がありますので、適宜参照してください。

### Ethereum Smart Contract Best Practices
https://consensys.github.io/smart-contract-best-practices/
Ethereumのスマートコントラクトを作る際に気をつけなければいけない点がまとまっています。
本格的にスマートコントラクト開発を始める前に一度は読んでおきたいドキュメントだと思います。
[有志による日本語版](https://msykd.github.io/smart-contract-best-practices/)もありますが、情報が古いです。

### ツール群
https://qiita.com/sot528/items/6ff13ebfc846aa313307
Ethereum関連のツールはこちらの記事によくまとめられています。