Gunosy Blockchain Blog

Gunosyの開発メンバーが知見を共有するブログです。

ERC20トークンに起きた脆弱性問題について (BatchOverFlow)

はじめに

こんにちは。新規事業開発室の山口 (@yamarkz) です。

ERC20に準拠したトークンコントラクトの脆弱性問題について4/25辺りからSNS上で大きな話題になりました。

今回はこの脆弱性問題について技術的な観点からどういった問題なのかを解説していきたいと思います。

ERC20やトークンコントラクトの解説は下記記事で行なっています。参考にして見てください

blockchain.gunosy.io

BatchOverFlowと呼ばれる脆弱性問題に対する結論

まず、結論を先に述べます。

このBatchOverFlowと呼ばれる脆弱性問題はERC20に準拠した処理で発生したものではなく、トークンコントラクトに独自実装された batchTransferと呼ばれる関数で発生した脆弱性問題です。

なので、この batchTransferと呼ばれる処理が含まれていないERC20に準拠したトークンでは何ら問題はなく、batchTransferの処理が含まれたトークンコントラクトが問題となっています。

SNS上で情報を追っていると、「ERC20準拠のトークンに問題があり、Ethereum史上最大の事件だ!」という様な情報が見受けられました。

この文脈から、あたかもERC20準拠トークン全てに問題が起きている様な状況が想像されますが。この問題はERC20に準拠したトークンの中で、batchTransferという機能を含んだトークンコントラクトで脆弱性が見つかったというのが正しい認識です。なので、Ethereum全体から見ると問題自体は局所的で、ERC20に準拠したトークン全ての価値が脅かされるという問題ではありません。

f:id:yamarkz:20180427000846p:plain:w400

とはいえ、この問題によりいくつかのトークン価値が失われており、資産価値的観点から見ると大きな損失が生まれてしまっているのは事実です。

この問題について技術的観点からより深く仕組みを見ていきたいと思います。

BatchOverFlowの仕組み

今回話題になったトークンコントラクトのバグは「BatchOverFlow」と呼ばれるもので、この問題に対する詳細な解説は4/23にMedium上で紹介されています。(1次情報は下記リンクを参照)

medium.com

このバグはBeutyChainと呼ばれるプロジェクトのERC20に準拠したトークンコントラクトで見つかりました。 実際にライブネットにデプロイされたソースコードはこちらから確認することができます。

トークンホルダーのリストを見ると特定のアドレスが保有するトークン量がものすごい額になっています。これが今回の問題で発生した現象です。参考

f:id:yamarkz:20180427005714p:plain:w400

脆弱性があった batchTransfer という関数は以下です。

この関数は、関数を呼び出したアカウントから複数の送信先アカウントに対して同一量のトークンを1度に送信するための処理です。

関数の引数は2つ

  • _receivers 送信先アカウントアドレスの配列
  • _value 送信トークン量

上記の処理ではamount変数に送信先アドレス数(receivers) * 送信トークン数(value)で算出した値を代入しています。 次に、「amount額を送信主が保有するトークンより超えないか」「0より大きい額で20より小さい額のトークンを送信しているか」を判定しています。 2つの判定をrequireでバリデート処理し、失敗した場合例外エラーが発生処理が終了します。成功した場合、処理実行が続きます。

そして、実際にアカウントが保有するトークン量をシフトチェンジする処理です。 amout額を送信元のアカウントから差し引き(.sub)、for文で_valueの額をreciversのアカウントアドレスに追加(.add)します。 送信先アカウントに対して送信が完了した事を伝えるEvent(Transfer)を発行し、処理が終了した事を伝えるtrueを返して処理が終了します。

この処理の流れで今回脆弱性の問題となったのは uint256 amount = uint256(cnt) * _value; の部分で、送信元アカウントからの送信総額を算出処理です。

valueはuint256型です。仮に送信者が5トークン保有しており、receiverに2つ以上のアドレスと_valueに2以上で乗算し5以下のになる大きな数を渡したとします。

2つ以上のアドレスとvalueに乗算した結果、オーバーフローが発生し数が5以下0以上になる。このオーバーフローの発生により数字が変わることでバリデーションが通ることになります。その結果、本来想定していない大きな額(_value)がreciverのアドレスに付与されます。

※ オーバーフロー問題に関するわかりやすい解説はこちらを参考。

これにより攻撃者は特定のアカウントが所有するトークン量を簡単に増やすことができるようになり。結果、トークン全体の供給量が壊れてしまいトークンの価値がなくなってしまいます。

価値がなくなる事で、トークンを購入し保有していたユーザーは損失が生まれることになります。また、この脆弱性を含んだトークンが取引所に上場していた場合も問題があります。

トークンの供給量が壊れている状況で、仮に取引所が取引許可していた場合。上記の仕組みを利用して、悪意のあるユーザーが不正に供給したトークンを取引所に空売りすることが可能になります。トークンの空売りにより取引所からは多額の資金が流出する自体が起こることが想定されます。

問題による影響

上記で示した状況が想定されたため、今回の問題が発生したトークンを取り扱っていた取引所は急遽、この事態が収束するまでERC20の取引を一時停止するという対応を行なっています。

ERC20トークンの取引を一時停止している主要な取引所

↓okexの取引アナウンス

f:id:yamarkz:20180426143332p:plain:w400

問題に対する解決策

この脆弱性問題に対する解決策はオーバーフローが起きる算出処理が行われた場合に、処理を中断し例外エラーを発生させることです。

算出処理よるオーバーフローは既知の問題で、解決アプローチも明確にされています。

算出処理はzeppelin-solidityというコントラクト開発ライブラリ群のSafeMath.sol を利用することが定石となっています。

今回のBeutyChainのトークンコントラクトを見ると全体的に、このSafeMathのような算出処理関数を利用していました。(.add / .sub etc..) しかし、なぜかbatchTransactionの乗算処理ではmul関数が使われていませんでした。

このmul関数を採用しなかった背景は調べても見つけることができませんでした。。。 >< おそらく、レビューで抜け漏れていたのではないかと想像します。

問題に対する有識者の反応

この問題に対して、正確に問題を理解し正しい意見を述べている方々の意見を紹介しておきます。

ALIS CTOのIshiiさん

Solidity開発Tips イチローのnakajoさん

Hi-Ether Organizerのamachinoさん

自分もつぶやきました

まとめ

  • バグが生まれない様、質が担保されたコードを採用する (SafeMathなど)
  • 多額の資産が絡むコードは必ず外部の厳密なコードレビューを入れよう
  • できればバグバウンティなども実施して事前にバグを発見し潰す努力を

SafeMathを使うことが定石であるということが、今回の脆弱性問題を実際に分析し学ぶことができました。 トークンホルダーからすると痛い代償ではあるもの、Ethereum全体としての教訓は得られたのかなと個人的には思っています。

宣伝

Gunosyではブロックチェーンを始めとしてスマートスピーカー、VR/ARと言った新規領域での研究・開発を進めており、メンバーを募集しています。

少し話を聞いてみたい!という方も歓迎していますので、下記のリンクからの応募お待ちしております。

www.wantedly.com

また弊社Gunosyでは、blockchain.tokyo を主催するなど、ブロックチェーンや仮想通貨に関する調査を行っています。

次回は来月に開催する予定です。イベントグループへメンバー登録をしていただくとイベント参加者募集の通知が来るので、こちらもぜひ登録してみてください。

blockchain-tokyo.connpass.com