[Ethernaut CTF] Token

0xDave·2022년 10월 3일
0

Ethereum

목록 보기
26/112
post-thumbnail

소스코드


// 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 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?

더 많은 토큰을 얻으면 된다.

해결과정


코드를 처음 봤을 때 드는 생각은 유명한 더 다오 해킹사건이다. 무한 루프를 통해 이더리움을 계속 환전해간 사건. 현재 transfer() 함수는 인출하고자 하는 _value 값을 송금하기 전에 0으로 만들지 않기 때문에 반복적으로 transfer() 함수를 호출하면 되지 않을까?

현재 내가 가지고 있는 토큰의 양은 20개.

총 발행량은 2100만개다.

반복문을 돌려서 발행된 토큰을 모두 가져오게 했다.(다 가져오는 건 너무했나..?) attack 함수를 호출한 뒤 다시 balance를 확인했더니 부자가 돼있었다. 다시 확인해보니 0을 하나 빠트려서 총 발행량의 10%만 가져왔다. 그래도 통과!


다른 접근


그런데 내가 생각했던 게 출제의도와는 다를 수 있다. 다른 사람은 어떻게 해결했는지 알아보자. 해결 과제 마지막 부분에 odometer는 계기판을 의미한다. 이와 관련해서 batchOverflow 공격이 있는데, 0000을 가리키는 계기판에서 1을 더하면 0001이 되지만 반대로 1을 빼면 1111이 된다. 즉, 어떤 주소의 balance를 저장하는 mapping에서 1을 빼준다면 해당 주소는 엄청난 양의 토큰을 갖는 것으로 저장된다.

    balances[msg.sender] -= _value;
    balances[_to] += _value;

처음 상황을 가정했을 때, 위 코드에서 우리 주소의 balances는 20개다. 여기서 20개를 transfer 요청한다면 당연히 balances는 0개로 될 것이다. 그런데 처음부터 20개가 아니라 21개를 요청한다면 balances 상에서 마이너스가 되는 게 아니라 엄청난 크기의 양수로 변하게 된다. 따라서 아래 코드를 입력하고 balances를 확인하면 토큰을 쓸어담을 수 있다.

await contract.transfer("메타마스크주소", 20 +1)
profile
Just BUIDL :)

0개의 댓글