본문으로 바로가기

SMT 토큰 해킹사건

category 이더리움/DApp 프로그래밍 2018. 4. 26. 22:49

어제 SMT(SmartMesh) 라는 이더리움 ERC20 토큰의 해킹사건이 발생되어 해당 거래소의 거래가 중지되는 사건이 발생하였습니다.

거래소의 주소로 1경개의 토큰이 전송되고 이렇게 전송된 토큰을 판매하여 현금으로 인출한 대형사건이 발생한것입니다.

이문제로 인해 일각에서는 이더리움 ERC20 토큰의 구조적인 문제라고도 하는데 온더의 정순형님께서 이 문제는 이더리움의 구조적인 문제가 아닌 해당 ERC20 토큰의 SmartContract 코드의 문제라고 분석의 글을 올려주셨습니다. 정순형님의 분석글을 한번 읽어보시면 어떤 문제로 인해서 발생한것인지 자세하게 알수가 있습니다.

이 문제에 대해서 여기서 한번 정리를 해보자면.


먼저 해킹이 일어난 트랜잭션은 다음과 같습니다.

하기의 정보를 보면 해커는 다음과 같이 transferProxy 라는 ERC20 표준함수가 아닌 SMT 토큰의 자체구현함수를 통해서 존재하지않았던 엄청난 갯수의 토큰을 발생시켜 자신의 주소로 보내도록 만든것입니다.




문제가 발생한 SMT 토큰의 스마트컨트랙트 소스는 다음과 같습니다.


function transferProxy(address _from, address _to, uint256 _value, uint256 _feeSmt,
uint8 _v,bytes32 _r, bytes32 _s) public transferAllowed(_from) returns (bool){
if(balances[_from] < _feeSmt + _value) revert();
uint256 nonce = nonces[_from];
bytes32 h = keccak256(_from,_to,_value,_feeSmt,nonce);
if(_from != ecrecover(h,_v,_r,_s)) revert();
if(balances[_to] + _value < balances[_to]
|| balances[msg.sender] + _feeSmt < balances[msg.sender]) revert();
balances[_to] += _value;
Transfer(_from, _to, _value);
balances[msg.sender] += _feeSmt;
Transfer(_from, msg.sender, _feeSmt);
balances[_from] -= _value + _feeSmt;
nonces[_from] = nonce + 1;
return true;
}


문제가 발생한 부분은 위의 붉은색으로 표시가 된 부분입니다.

transferProxy 이더가 없는 사용자가 다른사용자를 통해서 SMT 토큰을 수수료로 전달하고 SMT 토큰을 전송하는 함수입니다.

즉, 함수 호출시에 from 주소에서 to 주소로 _value 만큼의 SMT 토큰을 전송하고, _feeSmt만큼의 토큰을 수수료로 함수를 호출한 주소로 전송하는 함수입니다.

문제는 위의 코드와 같이 보내고자 하는 토큰의 갯수와 현재 잔액을 비교하는 부분에서 오버플로우가 발생해서 의의 조건문이 통과되어 존재하지않는 토큰이 특정주소로 전송이 된것입니다.


위의 트랜잭션 정보에서 보는것처럼 다음의 2번값이 _value이고, 3번값이 _feeSmt 에 해당됩니다.

[2]:  8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

[3]:  7000000000000000000000000000000000000000000000000000000000000001


위의 두값을 조건문에서 비교하고자 더했을때 더해진값이 데이타타입인 uint256을 넘어가기 떄문에 오버플로우가 발생하여 0으로 되버렸습니다.

즉 조건문을 통과하여, 현재 잔액이 0원인 상태였지만, 잔액비교 조건문을 통과하여 존재하지않았던 엄청난수의 토큰을 특정주소로 보내고, 함수를 호출한 주소로 또한 엄청난수의 토큰을 수수료로 전달되버린것입니다.

해커는 이렇개 생성된 토큰중 1경개의 토큰을 거래소 주소로 보낸후에 현금화를 한것입니다.


일반적으로 위와같은 문제를 방지하기 위해서 이전부터 SafeMath 함수를 정의하여 연산을 수행했다고 하며, OpenZeppelin에서 정의되어 있습니다.

즉, 위의 코드는 다음과 같이 작성이 되었어야 합니다. (정순형님 분석글)


if(balances[_from] < _feeSmt.add(_value) ) revert();


이 문제는 현재 일부 무분별하게 진행되는 여러 ICO들에 경각심을 일으킨만한 사건이며 스마트컨트랙트 작성에 얼마나 주의가 필요한지 각인시켜줄만한 사건으로 생각됩니다.