Smart Contract 취약점 [3] Arithmetic Issues

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

 

 

참고

https://dasp.co/#item-3

  Comments,     Trackbacks