Smart Contract 취약점 [5] Denial of Service

Denial of Service(DoS)

Denial of Service는 아마 대부분의 사람들, 심지어 비전공자들도 들어봤을만한 취약점이다.

간단하게 도스 취약점이라고 부르기도 하는데, 정상적인 실행을 방해하는 행위라고 생각하면 될 것이다.

 

다른 분야(시스템 해킹 / 웹 해킹 / etc ...)에도 항상 등장하는 DoS 취약점은 역시나 Smart Contract에서도 발생하는 취약점이다. 하지만 그 영향력은 다른 곳에서의 DoS 취약점과는 차원이 다를 정도로 강력하다.

 

Smart Contract에서 DoS 취약점이 발생한다면, "복구가 불가능하다"는 특징이 있다. 다른 분야에서는 복구 후 서버를 재가동하는 등의 행위가 가능하겠지만, Smart Contract는 한 번 실행시키고 난 뒤에는 그 누구도 조작할 수 없기 때문에 Offline 상태로 돌입하게 된다.

 

Smart Contract에서 DoS 취약점이 발생하는 원인은 정말 다양하다.

  • Transaction을 받는 과정에서의 Malicious한 행위
  • gas 사용량을 엄청나게 증가시키는 행위
  • Access Control을 통해 private component에 접근하는 행위
  • 혼란 및 남용 등의 행위

"Smart Contract 취약점 [2] Access Control"에서 설명했던 Parity Multi-sig Wallet이 kill된 것도 DoS 공격의 일종으로 볼 수 있다.

 

코드 예시를 통해 살펴보도록 하자

function becomePresident() payable {
    require(msg.value >= price); // must pay the price to become president
    president.transfer(price);   // we pay the previous president
    president = msg.sender;      // we crown the new president
    price = price * 2;           // we double the price to become president
}

위와 같은 함수가 존재하는 Smart Contract가 있으며, becomePresident 함수를 호출한 사람이 주어진 크기의 돈을 내고 Contract 내부의 president가 되는 상황이다.

해당 함수는 Malicious Contract가 president가 될 때, 문제가 발생한다.

Malicious Contract의 특징은, 본인이 원하는 형태의 Fallback함수를 만들 수 있다는 것이다.

만약 Malicious Contract의 Fallback 함수에서 revert 함수를 호출 시킨다면 어떻게 될까?

revert 함수는 Exception Handler의 형태라고 생각하면 되는데, 내부에서 문제가 발생했을 때 함수 호출을 중지시키고 뒤로 되돌리는 역할을 한다.

 

* 이후에 추가적으로 Solidity의 Error Handler에 대해서도 한번 다뤄보도록 하겠다.

 

다시 위의 상황으로 돌아가서 살펴보면, Malicious Contract가 president인 상황에서 다른 사용자가 becomePresident 함수를 호출했다고 생각해보자. 그러면 president.transfer(price) 부분에서 돈을 Malicious Contract로 보내지만, Fallback 함수에서 revert가 호출되기 때문에 그 다음줄인 president = msg.sender 함수는 절대 실행될 수 없게 된다.

 

즉, 해당 Smart Contract의 president는 이제 쭉 Malicious Contract로 고정되게 되는 것이다.

이것이 DoS의 한 예시이다.

 

또다른 코드 예시를 살펴보자.

function selectNextWinners(uint256 _largestWinner) {
	for(uint256 i = 0; i < largestWinner, i++) {
		// heavy code
	}
	largestWinner = _largestWinner;
}

위의 코드의 경우, 인자를 이용하여 "다음 번의 largestWinner"를 선정할 수 있다. 만약 이를 엄청 큰 수로 세팅한다면, 다음 번의 selectNextWinners 함수 호출 시점에서 엄청난 수의 반복이 일어날 것이다.

이더리움의 경우 사용 가능한 gas의 제한이 존재하기 때문에 해당 함수를 이용하면 gas 사용량을 제한 사용량에 도달하게 만들어서 Contract를 사용할 수 없는 상태로 만드는 것이 가능하다.

 

 

참고

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

  Comments,     Trackbacks