How to Solve the Ethernaut Game's Level 5: Token

Written by kamilpolak | Published 2022/01/27
Tech Story Tags: solidity | smart-contracts-solidity | solidity-hack | hack-solidity | smart-contracts | smart-contract-security | ethereum | ethernaut-game

TLDRIn challenge 5 you are given 20 tokens to start with and you will beat the level if you somehow manage to get your hands on any additional tokens. To win this challenge we need to trigger an overflow or underflow. The overflow is a situation when an integer (signed integer) reaches its byte size. The next element added will return the first variable element. If you subtract 1 from a variable that is 0, it will change the value to 255. There are two options to use Openppelin to use all the mathematical operators.via the TL;DR App

In challenge 5 you are given 20 tokens to start with and you will beat the level if you somehow manage to get your hands on any additional tokens. Preferably a very large amount of tokens.

Let's first look at the given smart contract.

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Token {

  mapping(address => uint) balances;
  uint public totalSupply;

  constructor(uint _initialSupply) public {
    balances[msg.sender] = totalSupply = _initialSupply;
  }

  function transfer(address _to, uint _value) public returns (bool) {
    require(balances[msg.sender] - _value >= 0);
    balances[msg.sender] -= _value;
    balances[_to] += _value;
    return true;
  }

  function balanceOf(address _owner) public view returns (uint balance) {
    return balances[_owner];
  }
}

This is a simple contract that allows to transfer and check the current balance of the contract. In the constructor, we can specify the total supply and assign it to a person who deployed the contract.

To hack this contract first you need to understand the concept of integer underflow and overflow. The overflow is a situation when uint (unsigned integer) reaches its byte size. Then the next element added will return the first variable element.

In the case of underflow, if you subtract 1 from a uint8 that is 0, it will change the value to 255.

Let me explain with an example. Note: The hardhat console library is necessary to log to the console in Remix.

pragma solidity ^ 0.6.0 ;

import "hardhat/console.sol";

contract Test {
    constructor () public  {
        uint8 small = 0;
        decrease--;

        uint8 large = 255;
        increase++;
        console.log(small); // prints 255
        console.log(large); // prints 0
    }
}

If you run this code in Remix, be sure to use compiler version 0.6. As you can see we have a variable named decrease with value a 0. If we subtract 1 from that variable we end up with 255.

If you want to know more about integer underflow and overflow I have an additional article you can read: Integer Overflow and Underflow in Solidity

Let's move to our smart contract. To win this challenge we need to trigger an overflow or underflow. Keep in mind that we received 20 tokens.

All we need to do is:

  • transfer 21 (more than 20) tokens to another address,
  • this will cause an underflow, setting our balance to 255

To execute transfer the function you need to take some real contract address. You can pick a one from Etherscan.io

How to prevent underflow and overflow in Solidity?

There are two options. The first is to use OpenZeppelin's SafeMath library that automatically checks for overflows in all the mathematical operators. The second one is to use 0.8 Solidity version where overflow and underflow will cause a revert.


Also Published Here


Written by kamilpolak | I am a huge enthusiast of cryptocurrency and blockchain technology.
Published by HackerNoon on 2022/01/27