CTF-safu-wallet

题目源码点击

题目要求:这是一个多签名钱包,已经有一位顾客存入钱,然后你要将它的钱永远困住在这个钱包里

首先看到合约代码:很清晰的辨别了,SafuWallet合约是代理合约,SafuWalletLibrary合约是逻辑合约,这样分开,就好理解了
对于多签名钱包,我的理解就是,不止一个管理员,至少要俩个管理员就可以管理这个钱包了。

逻辑合约里有kill函数,如果能调用,这应该是最便捷的解题方式了

function kill(address _to) onlymanyowners(sha3(msg.data)) external {
suicide(_to);
}

很遗憾的是,有个onlymanyowners限制,这也就是多签名钱包的魅力了吧。

看到代理合约中,构造函数还是有点东西。

constructor(address[] memory _owners, uint _required, uint _daylimit) {
bytes memory data = abi.encodeWithSignature(
"initWallet(address[],uint256,uint256)",
_owners,
_required,
_daylimit
);

_safuWalletLibrary.functionDelegateCall(data);
}

又是熟悉的delegatecall,代理合约委托调用了逻辑合约里的initWallet函数,执行环境是在代理合约,改变的状态变量也是在代理合约中,也就是说在代理合约中,这个所有者是可以改变的,如果只有我们自己,那么就可以成为多签名钱包的唯一所有者,然后再调用逻辑合约里的kill函数,传入代理合约的地址,销毁它,这样就不需要其他人的所有者的同意,就可以销毁,那位存钱的人,想要取钱,是不行的。

还有一种情况就是把所有者列表都设置为空,然后再调用kill函数时就没有任何所有者确认了。

攻击思路:
改变所有者的列表,将他们设置为空。调用safuWalletLibrary.initWallet(addresses(0), 1, type(uint).max),设置要求,只需要一个确认就可以执行操作。最后调用kill函数,即可。

个人认为:就是逻辑合约里的kill函数没有任何限制,只要所有者列表为空,或者攻击者成为了所有者,都可以调用。