티스토리 뷰
1. 문제 설명
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?
// SPDX-License-Identifier: MIT
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];
}
}
player에게 20개의 토큰이 주어지며, player가 20개보다 많은 토큰을 소유하게 된다면 문제가 해결됩니다.
2. 취약점 분석
주어진 transfer 함수는 require문으로 msg.sender의 balance에서 value를 뺀 값이 0보다 크거나 같은지 검사합니다. 언뜻 보면 msg.sender의 balance가 value보다 크거나 같은지를 검사하는 것처럼 보입니다.
하지만 uint256 자료형에서 연산을 수행했을 때 값이 0보다 더 작아진다면 underflow가 발생하여 다시 uint256의 최대값으로 돌아가게 될 것입니다. 따라서 transfer함수에 player가 가지고 있는 토큰보다 1만큼 많은 value를 전달하게 된다면 require문에서 2**256 - 1 >= 0으로 수행되어 조건문을 통과할 것이며 msg.sender의 balance는 연산 후 2**256 - 1이 될 것입니다.
(문제 설명에서 물어보았던 'odometer'의 그림입니다. 이 그림을 보신다면 integer overflow가 조금 더 이해되실 것입니다. 근데 아까는 underflow라더니 지금은 왜 overflow냐구요? C 표준이 점점 개정되면서 2000년대 초반에 단순하게 자료형의 표현 범위를 벗어나면 overflow라고 새롭게 정의했다고 합니다 😅. 참고자료)
3. 공격 수행
문제 설명에서 보았듯 공격 수행 전 player의 token은 20개입니다.
임의의 주소와 player의 잔고 + 1만큼을 transfer 함수에 전달하여 호출합니다.
player의 잔고가 2**256 - 1이 된 것을 확인할 수 있습니다. 이제 Submit instance를 눌러 문제를 완료합니다.
✌️
4. 문제 보충 설명
Overflows are very common in solidity and must be checked for with control statements such as:
if (a + c > a) {
a = a + c;
}
An easier alternative is to use OpenZeppelin's SafeMath library that automatically checks for overflows in all the mathematical operators. The resulting code looks like this:
a = a.add(c);
If there is an overflow, the code will revert.
오버플로우 취약점은 Solidity에서 매우 흔하게 발생하며 위와 같은 조건문을 사용해 확인해야 합니다. 더 쉬운 방법으로는 모든 수학 연산자에서 오버플로우를 자동으로 검사하는 OpenZeppelin의 SafeMath 라이브러리를 사용하는 것입니다. 해당 라이브러리에서는 오버플로우가 발생하면 revert를 일으켜 상태가 원래대로 돌아갑니다.
5. 보완 방법
해당 문제에서 취약점을 방지할 수 있는 방법은 여러 가지가 있을 수 있습니다.
(1) SafeMath 라이브러리 사용
문제 보충 설명에서도 적혀있듯이 OpenZeppelin에서 제공하는 SafeMath 라이브러리를 사용하면 산술 연산에서 오버플로우를 방지해줄 수 있습니다.
pragma solidity ^0.4.23;
library SafeMath {
...
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
assert(c >= a);
return c;
}
}
assert를 사용하여 조건에 맞지 않으면 revert를 일으키게 합니다. 이를 사용해 변경된 코드는 다음과 같습니다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
contract Token {
using SafeMath for uint256;
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) {
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
return true;
}
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
}
(2) Solidity 버전 업그레이드
위의 문제는 솔리디티의 버전이 ^0.6.0으로 낮은 버전이었기에 가능한 문제였으며 솔리디티 0.8.0 이상 버전부터는 오버플로우 체크가 기본적으로 활성화되어 있습니다. 따라서 최신 버전을 사용하면 별도의 라이브러리 없이도 문제가 해결됩니다. 😊
(3) 조건문 수정
사실은 SafeMath 라이브러리나 솔리디티 버전 업그레이드 없이 간단하게 require문만 수정하여도 되는 간단한 예제였습니다 ㅎㅎ..
function transfer(address _to, uint256 _value) public returns (bool) {
require(balances[msg.sender] >= _value, "Insufficient balance");
balances[msg.sender] -= _value;
balances[_to] += _value;
return true;
}
감사합니다!
'Web3 > Wargame' 카테고리의 다른 글
[Ethernaut] Level 7 Force 풀이 (0) | 2024.12.10 |
---|---|
[Ethernaut] Level 6 Delegation 풀이 (0) | 2024.12.04 |
[Ethernaut] Level 4 Telephone 풀이 (0) | 2024.11.22 |
[Ethernaut] Level 3 Coin Flip 풀이 (0) | 2024.11.19 |
[Ethernaut] Level 2 Fallout 풀이 (0) | 2024.11.19 |
- Total
- Today
- Yesterday
- Crypto
- solidity
- 티스토리챌린지
- 이더넛
- Lord of SQL injection
- Los
- 프로그래머스
- 파이썬
- blockchain
- Write up
- web3
- Python
- 웹해킹
- forensic
- CTF
- 워게임
- 디지털 포렌식
- 디스코드 봇
- 알고리즘
- Ethernaut
- misc
- 정렬
- smart contract
- 코딩테스트
- 이세돌
- YISF
- writeup
- ctftime
- Ethereum
- 오블완
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |