이더리움(Ethereum) DAO 재귀 함수 해킹 사례
나동빈
이더리움 DAO 재귀적 이더리움 전송 공격
2016년 6월에 이더리움 DAO 계약 플랫폼에 존재하는 취약점을 이용해 360만 개의 이더리움(ETH)이 해킹당하는 사고가 발생했습니다. 이 사건 때문에 이더리움의 가격은 폭락하였고, 궁극적으로 전체 이더리움의 10%에 달하는 이더리움이 해킹당하는 결과를 낳았습니다.
이더리움 DAO 플랫폼은 당연히 코드를 기반으로 작동하며 내부적으로 나누기(Split) 기능이 사용됩니다. splitDAO() 함수는 특정 계정의 잔액과 합계 금액을 갱신하는 함수입니다. 따라서 splitDAO가 다시 호출되기 이전에 어떠한 함수의 호출이라도 발생시킬 수 있다면 무한 재귀 호출(Infinite Recursion)을 발생시켜 원하는 만큼 자금을 이동시킬 수 있습니다.
다시 말해 이러한 해킹 기법의 기본적인 원리는 나누기(Split) 함수를 실행한 뒤에 DAO가 보상(Reward)를 인출하려고 할 때, 인출이 끝나기 전에 나누기 함수를 다시 실행하는 것입니다. 따라서 잔액을 업데이트하지 않고 다시 실행하여 계속해서 이더리움을 가져올 수 있게 됩니다. 쉽게 말해 DAO 토큰을 환불 받고도 DAO 토큰이 계속 남아있어 무한정 인출할 수 있는 것입니다. 취약한 소스코드는 구체적으로 다음과 같습니다.
function splitDAO(
uint _proposalID,
address _newCurator
) noEther onlyTokenholders returns (bool _success) {
...
// 이더리움을 이동시키고 새로운 토큰을 지정 받습니다.
uint fundsToBeMoved =
(balances[msg.sender] * p.splitData[0].splitBalance) / p.splitData[0].totalSupply;
if (p.splitData[0].newDAO.createTokenProxy.value(fundsToBeMoved)(msg.sender) == false)
throw;
// 공격자가 위 IF문을 재귀적으로 무한정 수행하도록 만들어 부당이익을 취합니다.
...
// DAO 토큰을 삭제합니다.
Transfer(msg.sender, 0, balances[msg.sender]);
withdrawRewardFor(msg.sender); // 결과적으로 이더리움을 받게 됩니다.
...
return true;
}
따라서 원래는 무한정 수행되지 않도록 설계된 소스코드를 무한정 수행하여 막대한 양의 이더리움을 해킹할 수 있었던 것입니다. 구체적인 공격 순서는 다음과 같습니다.
① 나누기(Split)를 요청하고 투표 기간이 만료될 때까지 기다립니다.
② splitDAO() 함수를 이용해 나누기를 수행합니다.
③ DAO 플랫폼은 createTokenProxy()함수를 실행해 적절한 토큰을 보내줍니다.
④ 3번 과정 이후에 withdrawRewardFor() 함수를 실행해 잔고가 업데이트 완료되기 전에 보상(Reward)을 받을 수 있도록 합니다.
⑤ DAO 플랫폼이 4번 과정을 수행하고 있는 동안에 동일한 파라미터로 2번 과정의 splitDAO() 함수를 다시 실행합니다.
⑥ 결과적으로 DAO 플랫폼이 더 많은 자식 토큰(Child Token)을 지급해주며, 잔고가 업데이트되기 전에 보상을 받습니다.
⑦ 5번 과정으로 돌아갑니다.
⑧ DAO가 잔고를 업데이트하도록 둡니다. 7번 과정에서 5번 과정으로 돌아간다는 점에서 이러한 재귀적 과정은 끝나지 않습니다.
기본적으로 많은 양의 수수료(Fee)를 발생시키지 않기 때으므로 사실상 무한정 공격을 수행할 수 있었습니다. 즉 앞서 언급했던 이더리움의 수수료 방어 기법은 이러한 공격 기법에 대한 해결책을 제시해줄 수 없는 것입니다. 결과적으로 악의적인 의도를 가진 공격자는 위 해킹 기법으로 자식 DAO 지갑으로 엄청난 양의 이더리움을 수금(Receive)할 수 있었습니다.
▶ 실제 공격자의 자식 DAO 지갑: https://www.etherchain.org/account/0x304a554a310c7e546dfe434669c62820b7d83490#history
실제 공격자의 지갑을 살펴보면 2016년 6월에 무려 360만 개의 이더리움을 소유한 것을 알 수 있습니다. 실로 어마어마한 금액으로 이더리움 개발팀의 빠른 대책이 필요한 상황이었습니다.
물론 취약점은 DAO 플랫폼 자체가 가지는 취약점이며 이더리움 개발팀은 이를 개선하기 위해서 다양한 해결법을 제시했습니다. 다만 블록체인 시스템은 중앙 기관이 없다는 점에서 무분별하게 이더리움 개발팀이 개입하는 경우 그 사회적 파장이 커질 수 있다는 점도 감안해야 했습니다. 또한 공격자의 개인키를 알아낼 수 없다는 점에서 거래를 무효 처리할 수도 없는 상황이었습니다.
그래서 DAO 프로젝트의 책임 개발자는 소프트 포크(Soft Fork)와 하드 포크(Hard Fork)로 크게 두 가지 해결책을 제시했습니다. 소프트 포크를 이용한 방법은 DAO와 해당 공격자의 자식 DAO 지갑의 사용을 정지하는 형태의 소프트 포크입니다. 또한 하드 포크는 DAO 토큰 보유자들이 이더리움을 돌려받도록 하는 형태의 하드 포크입니다.
결과적으로 이더리움 재단이 다양한 관계자들과 협의하여 소프트 포크 기술을 적용하도록 의사결정이 이루어졌습니다. 하지만 네트워크 불안정 문제가 발생하여 재협의를 진행하게 되었고, 하드 포크를 수행하는 방향으로 다시 의사결정이 이루어졌습니다. 따라서 1,920,000번째 블록을 기준으로 하드 포크가 성공적으로 진행되었습니다.
다만 하드 포크가 진행됨에 되어 기존의 기술을 따르는 이더리움 클래식(Ethereum Classic)과 새롭게 탄생한 이더리움(Ethereum)으로 이더리움 블록체인 플랫폼이 나뉘게 되었습니다. 이더리움 클래식은 하드 포크를 수행하기 전의 블록체인을 기반으로 상장된 가상화폐입니다. 다시 말해 기술적인 개선이 사실상 이루어지지 않았다는 점에서 가치가 평가절하 되고 있습니다.
이더리움 클래식도 이더리움과 동일하게 PoW 방식을 채택하고 있으며 추가적인 하드포크도 주기적으로 진행하고 있는 상황이라는 점에서 이목이 집중되고 있습니다. 현재 공식 이더리움 블록체인은 이더리움(ETH)이며 이더리움 클래식은 잔류 기술로 남아있는 상황입니다.
이러한 블록체인 보안 침해사고 발생의 가장 근본적인 이유는 기술 인증 제도가 명확히 잡혀있지 ISMS, PIMS 등과 같이 기술 인증 제도가 블록체인 플랫폼에도 적절히 적용될 수 있도록 해야 합니다. 더불어 보안 검증을 받은 블록체인 기술만 살아남을 수 있도록 하는, 시장 전체에 걸친 분위기가 형성되어야 한다고 생각합니다.
또한 일정 수준 이상의 시장 규모가 있는 블록체인 개발팀의 경우, 자체적으로 자문 능력이 뛰어난 컨설팅 팀과 보안 전문가 팀을 구성하도록 하여 미연에 침해사고를 방지할 필요가 있습니다. 2018년 이후 앞으로도 계속해서 블록체인 기술과 산업은 호황기를 이룰 것입니다. 빠른 사업화와 개발 능력 또한 물론 중요한 요소이지만 안정적인 시스템을 구축하기 위한 지속적인 노력이 필요한 시점이라고 생각합니다.
'블록체인' 카테고리의 다른 글
Truffle로 처음 시작하는 Solidity 개발 환경 구축 (0) | 2019.07.10 |
---|---|
이더리움(Ethereum) 도스(DoS) 공격 사례 (0) | 2018.04.09 |
이더리움(Ethereum)의 개요 (0) | 2018.04.09 |