The Ethernaut-Solutions repository contains the solutions using Foundry and Hardhat.
This level shows a weakness in pseudo-randomness in smart contracts.
Objectives
Guess the correct outcome of a coin flip 10 times in a row
If the guess is wrong, the counter resets. To exploit the contract, we have to look at the source of randomness used in flipping the coin.
Analysis
Randomness in computer systems, and especially in Ethereum, is difficult to achieve. Ethereum Blockchain is a deterministic Turing machine, meaning there is no randomness involved. To generate randomness, developers often rely on block properties such as block number, blockhash, timestamp, etc. These variables may look random, but they can be exploited if the inputs are known. This is very much possible due to the public nature of blockchain. Also, miners and validators can, to some extent, manipulate these values, especially the timestamp.
Take a look at the level 3 contract CoinFlip
. There is only one function, flip(), that accepts your guess as a boolean. The contract keeps track of consecutive guesses in the consecutiveWins
state variable. consecutiveWins
is increased by 1 for each correct guess and resets to zero if your guess is wrong.
Let's go through the flip()
function and evaluate how the side is determined.
The first line is the source of randomness that uses blockhash
and block.number
.
uint256 blockValue = uint256(blockhash(block.number - 1));
blockValue
is calculated using block.number-1
i.e. previous blockhash. This is deterministic. Information about the block can be easily accessed.
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;
blockValue
is divided by FACTOR
to get coinFlip
. If the value of coinFlip
is 1
, then side
will be set to true
; otherwise, it will be set to false
. The value of FACTOR
is available in the contract. Since we already have all the required inputs, we can calculate the value of side
easily. We can create a contract that will do the same calculation and guess the value of side
.
Exploit
Let's create a contract that will hack the level.
Go to the Remix IDE and create the following contract.
On line 16, put the instance address in place of PUT_INSTANCE_ADDRESS_HERE. You can get the instance address by typing instance
in the browser console. The contract simulates the exact coin flipping logic.
Call hack()
10 times. The function will submit the calculated guess directly to the level's instance. This will always increase consecutveWins
counter in the level's instance because the function sends only correct guesses.
Make sure to call the function at least 10 times.
Submit the instance.
Level passed!!!๐
Key Takeaways
Generating randomness on Ethereum is difficult. All the data on Ethereum is public so anyone can easily guess the random value and hack your contract.
You can use Oracles as the source of randomness. Oracles are applications that source, verify, and transmit external information (i.e. information stored off-chain) to smart contracts running on the blockchain.
The following are common use-cases for oracles in Ethereum:
Retrieving financial data
Generating verifiable randomness
Getting outcomes for events
Automating smart contracts
The Ethernaut-Solutions repository contains the solutions using Foundry and Hardhat.
Solution using Foundry:-
Solution using Hardhat:-
More Levels