I am confused by this simple contract. The main purpose is to bypass the OnlyContract()
modifier.
Victim contract.
pragma solidity 0.7.6;
contract Victim {
modifier OnlyContract() {
require(msg.sender == address(this), "You are not this contract");
_;
}
function withdraw(uint256 amount) external {
(bool success,) = msg.sender.call{value : amount}("");
require(success, "Withdraw failed");
}
function ContractWithdraw(address payable to_, uint256 value) external OnlyContract {
require(address(this).balance >= value, "No funds");
(bool success,) = to_.call{value : value}("");
require(success, "Contract Withdraw failed");
}
}
Malicious Contract
pragma solidity 0.7.6;
contract Malicious {
address payable owner;
constructor() public {
owner = msg.sender;
}
receive() external payable {
address x = address(0x0000000000);
(bool success, bytes memory data) = x.delegatecall(abi.encodeWithSignature("ContractWithdraw(address,uint256)", address(this), 1000000000000000000));
}
function Attack() public {
x.withdraw();
}
function withdraw_all() public {
owner.transfer(address(this).balance);
}
}
The flow is like this:
- Attacker call
Attack()
on Malicious contract. - Victim contract send ETH to Malicious contract.
- Victim contract go into Malicious fallback contract and delegatecall to his own contract.
- From here,
msg.sender
delegatecall has changed to Victim contract.
But in reality, the delegate call transaction reverted.
I think I've bypassed the OnlyContract()
modifier.
Why did the attack fail?