【Ethereum】ERC20トークンで発生した脆弱性「batchOverflow」について
つい最近話題になった「batchOverflow」についてホットなうちにまとめる。
参考
イーサリアム基盤ERC20トークン“重大バグ”発見|ポロニエックスほか複数の大手取引所取引停止 | 仮想通貨まとめ
まぁ重大バグではないが。。
要約
原因となったメソッド「batchTransfer」
早速原因となったメソッドを見ていきます。
function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool) { uint cnt = _receivers.length; uint256 amount = uint256(cnt) * _value; require(cnt > 0 && cnt <= 20); require(_value > 0 && balances[msg.sender] >= amount); balances[msg.sender] = balances[msg.sender].sub(amount); for (uint i = 0; i < cnt; i++) { balances[_receivers[i]] = balances[_receivers[i]].add(_value); Transfer(msg.sender, _receivers[i], _value); } return true; }
とてもシンプルなメソッドで、複数のアドレスに同時に支払いを行うもので、払い戻し等に使うことが予想される。
具体的に言えば、1つ目の引数に指定したアドレスのリスト_receivers
に対して、2つ目の引数である_value
を送金する関数。
最初のところから。
uint cnt = _receivers.length; //送金先のアドレスがいくつあるかを取得 uint256 amount = uint256(cnt) * _value; //送金量の合計を計算
はい、いきなり原因が存在する。2行目です。
悪意のあるユーザが、この関数を呼び出すときに、_value
を巨大な値にしたとすると、
cnt*_value
は非常に大きな値になり、amount
が桁あふれ(オーバーフロー)を起こしてしまう。
桁あふれ(オーバーフロー)については、こちら など調べたらいろいろ出てくるはず。
要するに3桁しか管理できないところに1000を入れても、桁が溢れて0となってしまう。
悪意あるものによってオーバーフローされると、amount
はほぼ0になってしまう。
その前提で次の2行。
require(cnt > 0 && cnt <= 20); //cntが0より大きく20以下であること require(_value > 0 && balances[msg.sender] >= amount); //_valueが0より大きく、送金元にamount以上が存在すること
1行目で送金先のアドレスの数を制限している。
2行目では、送金が0より大きく、送金元に十分資金があるように制限している。
ここで本来は、_value
が大きいとamount
も大きくなり、送金元の保持する資金を超えてしまったらエラーになるはず。
しかし、amount = uint256(cnt) * _value
によるオーバーフローにより、_value
が大きくてもamount
が小さくなってしまう。
結果、送金元に十分資金があることのチェックが出来ず、悪意あるものの攻撃に対して、条件チェックで防げなくなってしまった。
あとは残り。
balances[msg.sender] = balances[msg.sender].sub(amount); //送金元の資金からamountを引く for (uint i = 0; i < cnt; i++) { //送金先アドレスの数だけループ balances[_receivers[i]] = balances[_receivers[i]].add(_value); //送金先のアドレスの資金に_valueを足す Transfer(msg.sender, _receivers[i], _value); //実際に送金先アドレスに、_valueを送る }
前の通り、悪意あるものが攻撃した前提で、_value
は大きく、amount
は0に近い状況を考える。
1行目で送金元資金から、amount
を引いていますが、amount
はオーバーフローにより小さいため、送金元資金はほとんど減らない。
(ここはあまり関係ないかも?)
そして、for文に入り、送金先アドレスに、大きい値である_value
を送金。。といった感じ。
プログラムを見ると、かなりシンプルな脆弱性であり、わかりやすいです。
そしてオーバーフローによる攻撃自体は、元々Solidityとして注意すべきものとして挙げられているので、
恐らくERCトークンに標準搭載しているメソッドであれば、誰か気づいたと思う。
また、今回の攻撃自体もスマートコントラクトのセキュリティをしっかり勉強しておけば防げると思う。
なので、セキュリティ対策はしっかり勉強していきたいですね!