2017/11/08にParityチームが告知したセキュリティーアラートについて興味があったのでちょっと調べてみました。
こちらのブログの記事の内容を読んだことをまとめています。
medium.com
Ethereumについてまだ全然詳しくないので間違っているところがあれば教えてくださると幸いです。
ブログ記事の内容を簡単にまとめると
- Parity Multisig Wallet は全てこのライブラリを利用していた
- どうやら初期化がされていなくて、誰でもinitWalletが呼べる状態のままだった
- とあるユーザーがTransactionを作ってinitWalletをコールしこのライブラリのオーナー権限を取得してしまった。https://etherscan.io/tx/0x05f71e1b2cb4f03e547739db15d080fd30c989eda04d37ce6264c5686e0722c9
- 続いてkillをコールしてこのライブラリが削除されてしまった。https://etherscan.io/tx/0x47f7cff7a5e671884629c93b368cb18f58a993f4b19c2a53a8662e3f1482f690
- ライブラリが削除されたので、Parity Multisig Walletが全て利用できなくなった。
という流れだそうです。
ライブラリが削除されたトリガーとなったkillメソッドはこちら
// kills the contract sending everything to `_to`. function kill(address _to) onlymanyowners(sha3(msg.data)) external { suicide(_to); }
中身はsuicide(address)を呼び出してるだけです。で、このsuicideを呼び出すとコントラクトがselfdestruction(自殺)します。
実際にsuicideを実行するとどうなるのか興味があったので試してみました。
まずはこんなコントラクトを作りました。
pragma solidity ^0.4.0; contract MyToken { /* This creates an array with all balances */ mapping (address => uint256) public balanceOf; string public message = "this is test message"; /* Initializes contract with initial supply tokens to the creator of the contract */ function setMessage(string _msg) public { message = _msg; } function kill() public { suicide(msg.sender); } }
メッセージの登録と呼び出しができるだけの簡単なコントラクトです。
で、このコントラクトのkillを呼び出した後の動きがどうなるか確認してみました。以下はbrowser-solidityで確認したログです。
// コントラクトを作成 creation of browser/MyToken.sol:MyToken pending... [block:423 txIndex:0] from:0x858...2b82e, to:browser/MyToken.sol:MyToken.(constructor), value:0 wei, 0 logs, data:0x606...a0029, hash:0x8ff...c316d # messageをcall [call] from: - , to:browser/MyToken.sol:MyToken.message(), data:e21f3...f37ce, return: { "0": "string: this is test message" } // setMessage("hoge")をcall call to browser/MyToken.sol:MyToken.message [call] from: - , to:browser/MyToken.sol:MyToken.message(), data:e21f3...f37ce, return: { "0": "string: this is test message" } transact to browser/MyToken.sol:MyToken.setMessage pending ... [block:432 txIndex:0] from:0x858...2b82e, to:browser/MyToken.sol:MyToken.setMessage(string) 0xb41...a6b4c, value:0 wei, 0 logs, data:0x368...00000, hash:0x56e...1951d // messageをcall "hoge"に変わってるのを確認 call to browser/MyToken.sol:MyToken.message [call] from: - , to:browser/MyToken.sol:MyToken.message(), data:e21f3...f37ce, return: { "0": "string: hoge" } // killをcall transact to browser/MyToken.sol:MyToken.kill pending ... [block:436 txIndex:0] from:0x858...2b82e, to:browser/MyToken.sol:MyToken.kill() 0xb41...a6b4c, value:0 wei, 0 logs, data:0x41c...0e1b5, hash:0x02a...46bb8 // messageをcall 0が帰ってくる call to browser/MyToken.sol:MyToken.message [call] from: - , to:browser/MyToken.sol:MyToken.message(), data:e21f3...f37ce, return: { "0": "string: " } // setMessage("hoge")をcall transact to browser/MyToken.sol:MyToken.setMessage pending ... [block:442 txIndex:0] from:0x858...2b82e, to:browser/MyToken.sol:MyToken.setMessage(string) 0xb41...a6b4c, value:0 wei, 0 logs, data:0x368...00000, hash:0xc07...94305 // messageをcallしてmessageの内容を確認したけどやっぱり0が帰ってくる call to browser/MyToken.sol:MyToken.message [call] from: - , to:browser/MyToken.sol:MyToken.message(), data:e21f3...f37ce, return: { "0": "string: " }
という感じでした。どうやらselfdestructしたコントラクトはどのメソッドを呼び出しても0が帰ってくるようです。
また、selfdestructしたコントラクトに対してトランザクションを送ってもエラーになるわけではないみたいです。(Gasも消費されてました)
今回の問題はそもそもParityチームがコントラクトを作成した後にinitWalletをcallし忘れたのが原因と上記ブログで書いていたので、それが本当なのか確認したいと思っています。が、時間がかかるので今回はここまでです。
~ 追記 ~
詳しい方に教えてもらったところ、このコントラクトはライブラリ的に利用する(このコントラクト事態をコールするのではなく、ほかのコントラクトから参照しているだけ。なのでデータも持っていない)ものとして作ってたので、ParityチームもinitWalletを呼び出すのを忘れていたんじゃないか?ということでした。コントラクトのライブラリとしての利用っていうのがよくわかってないので、次はこの辺りを勉強しよう。。。