还记得2017年7月Parity钱包合约被找到漏洞,结果骇客偷走了将近150,000个以太币,会发生是因为智能合约的callback里使用了delegatecall(msg.data),这个函数会呼叫data中的函数并将msg.sender设为原呼叫函数的地址,骇客利用这一点呼叫了initWallet,这时你们可能会以为Parity应该有在initWallet设置条件阻挡骇客就不能呼叫,结果竟然是没有!所以骇客就成功并改变合约的拥有者,最后再把以太币转走,细节的部分可以看这篇文章。
Parity也在几天后修复了这个问题,修复的方式就是在init*函数加上only_uninitialized modifier判断,当m_numOwners > 0时这个函数就不能使用,这时应该会想说不会再有漏洞了吧,毕竟智能合约能操作以太币,谁知道……
就在前几天有人发布了新issue说他不小心删除了钱包的合约,因为只有合约的拥有者可以删除,那么他到底怎么删除合约?可以看到钱包合约被删除前共有两笔交易,第一笔交易呼叫initWallet并将合约的拥有者设为自己。
function initWallet(address[] _owners, uint _required, uint _daylimit) only_uninitialized {
initDaylimit(_daylimit);
initMultiowned(_owners, _required);
}是、soli
交易结果:
Function: initWallet(address[] _owners, uint256 _required, uint256 _daylimit)
MethodID: 0xe46dcfeb
[0]:0000000000000000000000000000000000000000000000000000000000000060
[1]:0000000000000000000000000000000000000000000000000000000000000000
[2]:0000000000000000000000000000000000000000000000000000000000000000
[3]:0000000000000000000000000000000000000000000000000000000000000001
[4]:000000000000000000000000ae7168deb525862f4fee37d987a971b385b96952
第二笔交易呼叫了kill函数,而kill函数呼叫了suicide ( selfdestruct函数的别称,功能是将合约程式从区块链移除,并将合约剩余的以太币转给参数的位置),钱包的程式就从区块链上移除了。
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
suicide(_to);
}
交易结果:
Function: kill(address _to)
MethodID: 0xcbf0b0c0
[0]:000000000000000000000000ae7168deb525862f4fee37d987a971b385b96952
因为很多使用这个钱包的合约引入位置都写死导致很多合约不能运作,在Polkadot里第451行就将钱包合约地址写死:
address constant _walletLibrary = 0x863df6bfa4469f3ead0be8f9f2aae51c91a907b4;
因为所有逻辑判断都在钱包合约中,所以其他相依于钱包合约的现在以太币都被冻结,且看起来像这样(无法提款):
contract Wallet {
function () payable {
Deposit(...)
}
}
Parity官方还在了解可行的解决方案,如果想查询自己有没有被影响可以到这个网站。
如何预防
这次的事件Parity说明有两种预防方式。一种是智能合约不该有自杀的函数,这样即便黑客获得了权限也无法把合约移除。一种是有新的建议及改善时,要及时更新线上的合约或是找寻线上合约可能的漏洞,因为在这问题发生前,就有网友提议在合约部属时要自动呼叫initWallet( pr )加强合约的安全。
如何取得冻结的资金
目前没有其他方式可以取得冻结的资金,只能期待未来有相关的改善建议实作在以太坊,或是以太坊实行硬分叉回复状态到钱包合约删除之前。