Open Zeppelin's Smart Contract Security Puzzles: Ethernaut Level 2 and 3 Walkthrough


Author profile picture

This is an in-depth series of Blogs around OpenZeppelin's smart contract security puzzles. The aim of blogs is to provide a detailed explanation regarding various concepts of Solidity and EVM required to solve a Puzzle. It is highly recommended to attempt to solve these puzzles before reading further.

Level 2: Fallout

The Smart Contract Code provided is :

pragma solidity ^0.5.0; import 'openzeppelin-solidity/contracts/math/SafeMath.sol'; contract Fallout { using SafeMath for uint256; mapping (address => uint) allocations; address payable public owner; /* constructor */ function Fal1out() public payable { owner = msg.sender; allocations[owner] = msg.value; } modifier onlyOwner { require( msg.sender == owner, "caller is not the owner" ); _; } function allocate() public payable { allocations[msg.sender] = allocations[msg.sender].add(msg.value); } function sendAllocation(address payable allocator) public { require(allocations[allocator] > 0); allocator.transfer(allocations[allocator]); } function collectAllocations() public onlyOwner { msg.sender.transfer(address(this).balance); } function allocatorBalance(address allocator) public view returns (uint) { return allocations[allocator]; }
}

The aim of the puzzle is to claim ownership of the contract.

Let's Solve the Puzzle:

Analyzing the code :

We want to change ownership of the contract. The only function which changes Ownership of the contract is Fal1out().

Let's call the function and claim ownership.
In the Dev Console, call the Fal1out function and submit the instance.

>await contract.Fal1out()
{tx: "0x06f7162edfa833179373d1c17f41040bc384374886c5ddfd7cf28e69d2327507", receipt: {…}, logs: Array(0)}

That's It!

Check the owner of the contract and you should see your address.

The vulnerability in the contract was the typo in name of Fal1out(). It was supposed to be a constructor, so its name was supposed to be Fallout(). Since the name is not the same as that of contract, the function behaves similar to any other function.

Level 3: Coin Flip

The Smart Contract provided is :

pragma solidity ^0.5.0; import 'openzeppelin-solidity/contracts/math/SafeMath.sol'; contract CoinFlip { using SafeMath for uint256; uint256 public consecutiveWins; uint256 lastHash; uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; constructor() public { consecutiveWins = 0; } function flip(bool _guess) public returns (bool) { uint256 blockValue = uint256(blockhash(block.number.sub(1))); if (lastHash == blockValue) { revert(); } lastHash = blockValue; uint256 coinFlip = blockValue.div(FACTOR); bool side = coinFlip == 1 ? true : false; if (side == _guess) { consecutiveWins++; return true; } else { consecutiveWins = 0; return false; } }
}

The aim of the puzzle is to guess the outcome of the flip 10 times in a row.

Analysis of Code:

Let's check the flip function.

function flip(bool _guess) public returns (bool) { uint256 blockValue = uint256(blockhash(block.number.sub(1))); if (lastHash == blockValue) { revert(); } lastHash = blockValue; uint256 coinFlip = blockValue.div(FACTOR); bool side = coinFlip == 1 ? true : false; if (side == _guess) { consecutiveWins++; return true; } else { consecutiveWins = 0; return false; } }

As we can see, the function takes one parameter which is _guess. It is boolean and it can be either true or false.block.number => stores the block number of the last block.blockhash(uint n) => returns hash of block number n.

blockValue is calculated and divided with FACTOR, the answer is stored in coinFlip. If coinFlip == 1, side = true else side = false.