イーサリアム・セキュリティの雰囲気と実際

仮想通貨の中でも、Ethereumは非常によくサイバー犯罪者の標的にされます。なぜEthereumが狙われるのか、どうやって攻撃するのか基礎からコードまで詳しく説明します。

#1 基礎を整理

ICOするとき、Ethereumでコーダーは何をしてる?

①コーダーはまず、残高データがあるコントラクトを作ってネットワーク全員に公開します

②新しいコインが欲しい人は、コントラクトに仮想通貨Etherを送ると、代わりにコントラクトの中にある新しいコインの残高データを増やしてもらえます。

③その後は、ICOの各々のホワイトペーパーに書いてあるような仕組みで、コントラクトにデータと仮想通貨Etherを一緒に送ると、コントラクトの中のコーダーが意図した「規約」が実行されます。

サイバー犯罪者は何をする?

サイバー犯罪者(以下「ハッカー」で略)は大きく分けて、次の2つの方法で、不当な利益を得ます

(1)人を騙す

上の②仮想通貨Etherを送るとき、多くの人は自分がどういう内容のコントラクトにお金を送ろうとしているのか詳しく知りません。つまり、悪い人のアドレスにお金を送ろうとしているのか、意図したものに送ろうとしているのか知らないのです。

よって攻撃手順は

①ICOへの送金コードが書いてあるウェブサイトにそっくりな偽サイトを作る、あるいは攻撃して書き換える

②参加者に、ICOへの送金と思わせながら、自分のアドレスに仮想通貨を送らせる

もっとも簡単であり、これは仮想通貨だけでなく全ての送金システムに対して使われる方法です。

(2)コントラクトを騙す

よくコンピューターゲームで、チートと呼ばれるズルい方法をつかってクリアする人がいることをご存知でしょうか?スーパーマリオ64を5分でラスボスのクッパまでクリアしたりする非常識な遊びです。

これは特定のデータの入力の仕方をして、ゲームプログラムを騙すことで可能となりますが、同様にコントラクトを騙すことで、EtherやICOコインの残高を増やすことができます。Ethereumの場合、以下のように攻撃します。

①攻撃用のコントラクトを設置する(ない場合もある)

②特定のデータとEtherを送信してコントラクトを騙す

③コントラクトの中にある残高データを「規約」にしたがって増やしてもらう

これは仮想通貨特有の問題といって良いでしょう。この「規約」は事実上、マイニングが実装される全ての仮想通貨に存在しますが、Ethereumの場合、特に多くの種類の「規約」をコントラクトで作ることができます。

#2 実際の攻撃

今までにハックされたイーサリアムでの脆弱性とはどのようなものだったのでしょう?

Ethereumハックを理解するためには、次の4つの事例が参考になります。

(1) batchOverflow攻撃

コントラクトを騙した攻撃でした。
攻撃されたコードをご覧ください。

uint256 amount = uint256(cnt) * _value;    (中略)require(_value > 0 && balances[msg.sender] >= amount); 

これは送金を行うコードで、amountって名付けられた整数データがこれから送金する額です。
ここにuint256と書いてありますが、つまりこのデータは2の256乗未満の整数を格納できるという意味であり、これを超える巨大な額を入力すると0に戻ります。これを利用してrequireに書いてある制限、「残高にある額より大きい額を送金できない」を見かけ上クリアします。

その後、先ほどの巨大な額が残高に刻まれてしまいます。ハッカーの複数の残高には、合計すると通貨全体の総額と比べ物にならないほどの数字が刻まれました。

このようなオーバーフローは基本的にハッキングしたい人が最初に考えてみることであり、システムを作る側は必ず知っておく必要があります。

この際、「ERC20に脆弱性がある」という表現で報道がされ、大混乱となりました。ERCは関数の名前などを揃えるだけですので、ERC-〇〇トークンに脆弱性があることは、ほぼあり得ないと言えます。

(2) The DAO事件 〜Reentrancy攻撃〜

コントラクトを騙した攻撃でした。

contract DAO is DAOInterface{
(中略)
function splitDAO(
(中略)
if (!rewardAccount.payOut(_account, reward)) {...
payOutはFallback(後述)
}
function payOut{...}<-これが設計者がさせたかったpayOut
}//ここまでがDAOコントラクト//ここからが攻撃者が外につくったコントラクト
contract Attack{
function payOut{後述の①...}<-これが攻撃者が実行したpayOut
(中略)
}

攻撃手法は

①TheDAOのコントラクトにある、残高が0でなければEtherを引き出せるコードに対し、トランザクションを送るコントラクトを設置

②残高データを0から増やすために、Etherを送金

③TheDAOのコントラクトが残高を減らす処理をする前に、①で設置したコードを実行する処理を繰り返すループを起動

この③において、Fallback関数と呼ばれる「規約」が勝手に設置したコントラクトでも定義・実行されるのを利用してループを作っています。

重要な点は、このコントラクト設計者がこの処理にトランザクションを送るのを人だけだと想定していた点です。コントラクトからコントラクトへのトランザクションも存在し、その場合Fallbackの処理が人から送られた時と違うのです。

ICOトークンだったTheDAOのコントラクトから65億円相当のEtherが盗み出されました。犯人はその後、自分は「約束」にしたがってお金を得ただけだと主張しましたが、創始者Vitalik Buterinの主導でEther残高データを元に戻す処置が多くのノードの意志で行われました。

(3) CoinDash ICOハック

人を騙した攻撃でした。

CoinDashがICOエントリー用のWebサイトを用意しましたが、コントラクト・アドレスが書き換えられ、多くの人が攻撃者のアドレスにEtherを送りました。

(4) HoneyPot

コントラクトを使って人(ハッカー)を騙した攻撃でした。

先ほどの(2)The DAOで使われ有名となったReentrancy攻撃ができることが丸わかりなコントラクトを設置。その際に、攻撃する前準備(2)②で必要なEtherをハッカーが送ったら、それを自分のアドレスに送金できるようにコントラクトコードが仕組まれていました。

このようなハッカーに対するトラップをHoneyPotと呼ぶことがあります。これに引っかかったハッカーは嬉しそうに、この攻撃を報告していました。

この方法は、コントラクトを省略して簡単にすると次のようになります。
①Ether残高を0にしGas代を払えない状態に、ERC20トークンの残高を盗みたくなる程度にしておく
②Ethereumの秘密鍵を意図的に流出させる
③送金トランザクションをブロードキャストし続ける
④ERC20トークンを盗むために送られてきたEtherを③で自分にリダイレクトする

#3 より深いEthereumのセキュリティ・トレンド

(1) 「完全」なスマートコントラクト

上述のReentrancyなどでは、攻撃用コードはそもそも攻撃以外には使わなさそうな動きを記述しています。コントラクト間でのループなど、基本的にはGasがかかりすぎるので、する意味がありません。

イーサリアムではどんなプログラムでも実行可能である「チューリング完全」をコントラクト / プロトコルで成立させることをコンセプトの根幹にしていますが、攻撃者の側にもどんなプログラムでも組める自由を提供していることにもなります。

これに対し、ビットコイン開発者Jimmy Song氏からは「結局チューリング完全でなく、チューリング脆弱だ」という、やや強い批判がされていました。

創始者Vitalikは、さらにプログラム実行上の自由度を上げるため、コード中からコードを読み込んで実行する提案、EIP-726を行いました。これは、”Eval”と呼ばれる機能でもあり、セキュリティ上の議論がもっとも多い機能の1つです。

この状況は、Ethereumは安全なアセットを作るものでなく、社会に機能を提供することを指向していることを語っているとも言えます。

(2) デコンパイリングSolidity

イーサリアムの開発はSolidityで行われるケースが多いですが、SolidityというのはEVMバイトコードを作るものであり、究極的には(強調)、バイトコードを直打ちしてブロック内のコントラクトのデータを変更するように命令できれば不必要なものなのです。つまり、スマートコントラクトの空間にはSolidityは存在しません

先ほど挙げた攻撃のほとんどは、被害者がSolidityコードを公開していたため、可能だったと言えます。ハッカーにとってバイトコードを読んで攻撃することは、先ほどの攻撃より格段に難しい技術なのです。よって多くのコントラクトは脆弱性を隠して佇んでいます。

しかしながら、我々はEthereumの言語処理系と凄く良く似た仕組みを知っているはずなのです。それはJavaJVMです。

JVMは簡単なスタックと少ないレジスタで構成された単純な仮想マシンであり、Javaコードから作られたバイトコードが実行されます。この際、JVMの仕様はその単純・汎用性の代償に、Javaコードへの戻し(デコンパイル)が容易という性質を持ちます。
これは、Javaで書かれた製品の知的財産権等の話に発展しやすいですが、セキュリティの問題にならずに済むのは、特に実行環境を共有していないからでしょう。

多くのノードで実行環境を共有、さらには同期しているEthereumはどうでしょう?もっと言えば、EVMはJVMよりも命令セットが少ないため、さらに単純です。

Solidityのデコンパイラ開発はJavaより速いと言えます。Javaはデコンパイラの登場まで10年ほどかかりましたが、Solidityは登場まで3年ほどでした。

まだこのデコンパイラは多少実用に充分でないようですが、メインネットに佇むコントラクトがSolidityに戻されるのは時間の問題でしょう。

スマートコントラクトのセキュリティ攻防が激しくなるのはこれからだと考えられます。PlasmaやeWASMなどの拡張で複雑さを増すであろうEthereumについて、セキュリティオタクはアレコレ考えずにいられないでしょう。

Author

分散型システムの欠点をセキュリティの面とインセンティブの面から考えています。1年半ほど色々なプラットフォームを調べ、今はEthereumでの開発に注力しています。

EthereumTech Lab.ではブロックチェーン、特にイーサリアムに対して強い興味を持った学生または社会人のメンバーを募集しています。
発信はMedium上で行い、本格的な開発状況に関する分析、または開発者それぞれの思想をリサーチし、日本語にて発信するなど様々な方向性の記事を作成しています。

EthereumTech Lab.はイーサリアムのモバイルウォレットを開発している”Wei Wallet Team”(*https://github.com/popshootjapan/WeiWallet-iOS) が中心となり、活動を行っています。そのため、オフラインでのイベント/ミートアップ実施の機会も多く開催していく予定です。

ご興味のある方は、こちらよりご応募お待ちしております!!

EthereumTech Lab | イーサリアム研究所

今後の記事更新のお知らせ・イベント情報・最新ニュースはFacebookグループへの参加がおすすめです。

--

--