Arithmetic Issues의 경우, Integer Overflow / Integer Underflow를 생각하면 된다.
Smart Contract에서는 unsinged가 물론 존재하지만, 많은 개발자들이 편의를 위해 int형을 많이 사용하곤 한다. 만약 overflow가 발생한다면, 이는 거의 돈을 탈취하는 공격이나 DOS 공격으로 이어질 수 있는 심각한 벡터로 사용된다.
예시 코드를 한번 살펴보자
function withdraw(uint _amount) {
require(balances[msg.sender] - _amount > 0);
msg.sender.transfer(_amount);
balances[msg.sender] -= _amount;
}
위 코드에서는 돈을 송금하는 withdraw함수가 구현되어 있는데, 돈을 전송하기 전에 balances[msg.sender] - _amount가 0보다 큰 값인지 우선 확인한다. 이 때, Integer Underflow 검사를 따로 하지 않기 때문에 엄청난 양의 돈을 빼내는 것이 가능하다.
2번째 예시 코드도 한번 살펴보자
function popArrayOfThings() {
require(arrayOfThings.length >= 0);
arrayOfThings.length--;
}
해당 함수의 경우, require문에 오류가 존재한다. 만약 arrayOfThings.length == 0인 상황에 popArrayOfThings 함수를 호출한다면, require문을 통과하고 1을 감소 시키면서 arrayOfThings.length가 -1이 되고, Off By One 취약점으로 이어질 수 있다.
아래는 3번째 예시 코드이다.
function votes(uint postId, uint upvote, uint downvotes) {
if (upvote - downvote < 0) {
deletePost(postId)
}
}
여기서 핵심은 unsigned와 unsigned와의 연산 결과는 unsigneㅇ라는 것이다. upvote와 downvote의 값과 관계없이 무조건 0보다 크거나 같은 결과가 되면서 의도하지 않은 행위가 가능해진다.
즉, 위 코드에서는 if문이 아무런 역할을 하지 않는다고 보면 된다.
마지막 예시 코드이다.
for (var i = 0; i < somethingLarge; i ++) {
// ...
}
Solidity에서 var이라는 자료형을 사용하게 되면, 시작 값을 가질 수 있는 가장 작은 자료형으로 인식된다고 한다.
즉, i의 초기값이 0이기 때문에 var은 1byte 크기의 변수(uint8)가 될 것이다. 만약 somethingLarge의 값이 256 이상의 값이라면, 해당 반복문은 무한하게 반복될 것이다.
위에서 언급한 것처럼, 실제로 Smart Contract에서는 underflow 및 overflow 관련 취약점이 많이 등장한다. 이를 해결하기 위해 만들어진 것이 바로 SafeMath이다. SafeMath에서는 기본적인 연산들을 add, sub, mul, div와 같은 함수로 구현하여 Integer Overflow / Underflow를 방지하게 해준다.
SafeMath code :
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol
참고
'Blockchain' 카테고리의 다른 글
Smart Contract 취약점 [5] Denial of Service (0) | 2022.04.04 |
---|---|
Smart Contract 취약점 [4] Unchecked Return Values For Low Level Calls (0) | 2022.03.29 |
Smart Contract 취약점 [2] Access Control (0) | 2022.03.28 |
Smart Contract 취약점 [1] Re-entrancy (0) | 2022.03.28 |
Blockchain for CTF (0) | 2022.03.01 |