Skip to content

The Ethernaut writeups: 15 - Naught Coin

Posted on:July 1, 2018

Update 2022 Feb: Bài viết đã được update để phù hợp với ethernaut & solidity version mới.

15. Naught Coin

Nhiệm vụ: bạn có rất nhiều tiền, nhưng phải đợi tới tận 10 năm sau mới được tiêu số tiền đó. Bằng cách nào đó hãy tiêu hết số tiền đó mà ko cần phải chờ tới 10 năm nữa.

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

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';

 contract NaughtCoin is ERC20 {

  // string public constant name = 'NaughtCoin';
  // string public constant symbol = '0x0';
  // uint public constant decimals = 18;
  uint public timeLock = now + 10 * 365 days;
  uint256 public INITIAL_SUPPLY;
  address public player;

  constructor(address _player)
  ERC20('NaughtCoin', '0x0')
  public {
    player = _player;
    INITIAL_SUPPLY = 1000000 * (10**uint256(decimals()));
    // _totalSupply = INITIAL_SUPPLY;
    // _balances[player] = INITIAL_SUPPLY;
    _mint(player, INITIAL_SUPPLY);
    emit Transfer(address(0), player, INITIAL_SUPPLY);
  }

  function transfer(address _to, uint256 _value) override public lockTokens returns(bool) {
    super.transfer(_to, _value);
  }

  // Prevent the initial owner from transferring tokens until the timelock has passed
  modifier lockTokens() {
    if (msg.sender == player) {
      require(now > timeLock);
      _;
    } else {
     _;
    }
  }
}

Phân tích & Solution

Contract này được thiết kế theo chuẩn ERC20, nên ta hãy xem qua một lượt interface của chuẩn này:

// ----------------------------------------------------------------------------
// ERC Token Standard #20 Interface
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
// ----------------------------------------------------------------------------
contract ERC20Interface {
    function totalSupply() public constant returns (uint);
    function balanceOf(address tokenOwner) public constant returns (uint balance);
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

Ta dễ dàng nhận thấy, ngoài transfer ra thì trong ERC20 còn có transferFrom nữa. Để chuyển token trong ERC20, chúng ta có 2 cách

function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);

Do đó ta sẽ khai thác như sau: Đầu tiên paste code lên Remix và load contract của ta ra, nhớ là sử dụng account player hiện tại nhé.

Bước 1: Trên chrome console kiểm tra balance hiện tại

(await contract.blanceof(player)).toString();
> 1000000000000000000000000;

Bước 2: Trên Remix, tiến hành approve cho một tài khoản khác với số lượng token là 1000000000000000000000000, ở đây mình sử dụng một tài khoản khác của mình là 0xc3A5e98871B9Bbb045A700c28b53C017D15Bd288, các bạn hãy thay bằng tài khoản khác của các bạn.

png

Bước 3: Switch qua account 0xc3A5e98871B9Bbb045A700c28b53C017D15Bd288 và tiến hành rút toàn bộ tiền tiền bằng transferFrom

png

Bước 4: Switch lại tài khoản player, trên chrome console check lại balance

(await contract.blanceof(player)).toString();
0;

Vậy là balance đã về 0. Submit && all done!

completed