[Devcon5] Inner Workings of a Smart Contract Decompiler
이번 Devcon5의 세션 발표가 이어지는 공간들이 작은편이었습니다. 인기 있는 세션을 듣기 위해서는 이전 세션부터 자리를 맡아두어야 겨우 들을 수 있었습니다. 그래서 본의 아니게 미리 들어갔던 세션에서 디컴파일러에 대한 내용을 발표하고 있었습니다. “The Inner Workings of a Smart Contract Decompiler” 제목의 세션으로 컨트랙트 분석에 도움이 될 만한 내용을 담고 있어 포스팅을 하기로 하였습니다.
디컴파일러(Decompiler)를 통해 스마트 컨트렉트(Smart Contract)의 바이트 코드(Bytes code)를 분석하고 보안 취약점 발견을 도와 줄 수 있습니다. 개인적으로 디컴파일러를 통해서 메이커다오의 Arbitrage Keeper로 활동하고 있는 컨트렉트를 분해(?)해보고싶어 해당 세션에 참석하였습니다.
발표자인 “Tomasz Kolinko”은 이더리움(Ethereum) 스마트 컨트렉트 디컴파일러인 eveem 프로젝트를 진행하고 있습니다. 참고로 eveem은 python으로 작성되었습니다. 해당 프로젝트는 오픈소스로 https://github.com/eveem-org/panoramix 에서 확인해 볼 수 있습니다.
또는 웹버전으로 즉시 사용해볼 수 도 있습니다. https://eveem.org 에 접속하여 메인넷에 배포되어 있는 컨트렉트 주소만 입력하면 디컴파일러 결과를 볼 수 있습니다. :O
Decompiler로 무엇을 할수 있는가?
Kolinko가 다른 세션에서 발표한 내용 중에 힌트를 찾을 수 있습니다.
아래와 같은 이더리움 스마트 컨트렉트 소스코드를 감사한다고 하였을때, 함수의 선언을 따라가면서 취약점을 찾는 것보다 디컴파일러를 사용하여 재구성된 코드를 보면 조금 더 쉽게 찾을 수 있습니다.
위 solidity 코드를 컴파일 한 후 다시 eveem을 통해 decompile 해보면 아래와 같은 결과를 볼 수있습니다. (solidity contract code → compiled bytes code → decompiled pseudocde)
이처럼 디컴파일러를 통해 재구성된 pseudo code로 취약점을 쉽게 발견 할 수 있습니다. transfer 함수를 살펴보면 caller 가 보유하고 있는 토큰 수량인 _value 를 체크 하는 부분이 없는 것을 알 수 있습니다.
아래처럼 require 문을 추가함으로써 caller 의 토큰 보유 수량이 적음에도 토큰이 전송되는 버그를 방지 할 수 있습니다.
Function Identifier
MethodID 또는 Function Selector라고 불리는 이것은, 이더리움 가상머신(EVM)내에서 컨트렉트가 트랜잭션(Tx)에 의해 실행될때 사용하는 함수의 식별자입니다. 이 식별자는 컴파일(compile)과정에서 생성되며 각 함수는 4bytes로 구분되어 집니다. 예를들어 function baz(uint32 a, bool b) .. 이라는 함수가 컨트렉트내에 존재한다고 할때, 이를 함수이름 입력인자타입의 string인 baz(uint32,bool) 을 keccak256 함수로 입력, 그 결과의 첫 4바이트를 해당 식별자로 사용하게 됩니다. 0xcdcd77c0 == keccak256(baz(uint32,bool))
초기 Etherscan에서 컨트렉트 배포자 또는 사용자가 Etherscan에 Verify한 컨트렉트만 해당 컨트렉트에서 발생한 Tx의 methodID를 보여주었던 반면, 현재는 Verified되지 않은 컨트렉트들 또한 4bytes의 값만 읽어서 해당 함수의 이름과 입력 인자 타입를 보여주고 있습니다.
발표자인 Kolinko는 자체적으로 MethodID 데이터베이스를 구축했고, http://eveem.org 서비스에 적용하여 Decompiled 된 결과를 보여줄때 함수 이름을 보여주고 있습니다.
Etherscan Unverified contract : https://etherscan.io/address/0xeFBA01e5A9Dd9eB154bfB0aec5b0c03b468626e3#code
eveem decompiled contract : https://www.eveem.org/code/0xeFBA01e5A9Dd9eB154bfB0aec5b0c03b468626e3
Etherscan의 경우 해당 컨트렉트의 함수를 실행시키는 Tx가 있어야지만 확인이 가능하지만, eveem에서는 컨트렉트 주소만으로도 쉽게 확인 할 수 있습니다. ( 언제 쓸지모르지만 좋아보여… ) 위 예시처럼 eveem에서 가지고 있지 않은 methodID는 “unknown” 과 함께 4bytes 의 Function identifier가 출력됩니다.
Function Identifier가 EVM 내에서 어떻게 작동하는지 궁금하시다면, 온더의 Thomas님이 작성하신 “Deconstructing a Solidity Contract” 의 24페이지 을 참고하시면 좋을 것 같습니다.
New tool for Auditing
이더리움 스마트 컨트렉트 Auditing Tool 로 사용하고 있는 Mythril 과 Manticore 와 더불어 symbolic execution을 지원하는 새로운 툴이 생겨 이더리움 개발자 생태계가 더 풍성해지는 것 같습니다.
온더의 스마트 컨트렉트 감사에도 eveem을 도입하여 활용 해 보면 좋을 것 같습니다.
참고로 기존 트랜잭션도 eveem.org 통해서 Tx에 의해 실행되는 콘트랙트 부분을 디컴파일된 코드와 함께 확인해볼 수 있어서 편리한 것 같습니다. (tx hash를 입력하여 결과를 조회하는데까지는 시간이 다소 걸립니다..)
Closing
세션 진행 중에서 가장 인상깊었던 부분이 있었습니다. 이 글에서도 다루었던 function identifier에 대해발표할때 참석자 한분이 database에 대한 질문을 이어가면서 발표자와 참석자간 서로 db를 공유하자는 이야기를 한 것이었습니다. 개인적으로 이더리움 개발자들간 활발한 교류현장에 있었던 것 만으로도 큰 감동을 받았던 순간이었습니다.
마지막으로 흔퀘히 자신의 발표 자료를 공유해주었던 Tomasz Kolinko 에게 감사를드립니다.
Specially Thanks to Tomasz Kolinko for sharing your presentation material.