Ethernaut Lvl 3 Coin Flip Walkthrough: how to abuse psuedo randomness in smart contracts

This is a in-depth series around Zeppelin team’s smart contract security puzzles. I’ll give you the direct resources and key concepts you’ll need to solve the puzzles 100% on your own.

Nicole Zhu
3 min readAug 18, 2018

This levels requires you to correctly guess the outcome of a coin flip, ten times in a row.

How Ethereum generate “randomness”

There’s no true randomness on Ethereum blockchain, only random generators that are considered “good enough”.

Developers currently create psuedo-randomness in Ethereum by hashing variables that are unique, or difficult to tamper with. Examples of such variables include transaction timestamp, sender address, block height, etc.

Ethereum then offers two main cryptographic hashing functions, namely, SHA-3 and the newer KECCAK256, which hash the concatenation string of these input variables.

This generated hash is finally converted into a large integer, and then mod’ed by n. This is to get a discrete set of probability integers, inside the desired range of 0 to n.

Notice that in our Ethernaut exercise, n=2 to represent the two sides of a coin flip.

Example of input variables that are often cryptographically hashed

This method of deriving pseudo-randomness in smart contracts makes them vulnerable to attack. Adversaries who know the input, can thus guess the “random” outcome.

This is the key to solving your CoinFlip level. Here, the input variables that determine the coin flip are publicly available to you.

Detailed Walkthrough

Let’s create a malicious contract that checks the outcome of the coin flip.

Only when you’ve correctly guessed the outcome, should you invoke the real contract’s flip(bool _guess) function.

  1. Inside Remix IDE, create a malicious contract that closely mirrors CoinFlip.sol:

2. Implement a hackFlip() function that predicts the flip outcome, using the same logic and input variables as the original contract. Since you also know blockhash and block.number, you’re able to accurately predict the correct _guess.

Your function should only invoke the originalContract.flip() with the correct _guess.

3. Call your hackFlip() function 10 times. The original contract’s consecutiveWins counter should steadily increase, as you are only making correct guesses.

Key Security Takeaways

  • There’s no such thing as true randomness
  • Be careful when calculating “randomness” in your contract (or even when inheriting from an existing random numbers library). In cases where you use randomness to determine contest winners, remember that adversaries can easily guess the random outcome and hack your game!

More Levels

--

--