[Protocol Invariant] Wormhole
본 게시글은 “Protocol Invariant 설정을 통한 컨트랙트 보안성 향상 방법론 제시”에서 다룬 3번째 사례인 크로스체인 브릿지에서의 Protocol Invariant 와 웜홀 해킹사례를 통해 살펴본 Protocol Invariant 의 중요한 점을 자세히 설명한 글입니다.
Author
Beomki Kim
Reviewed By (디사이퍼 미디어팀)
목차
- 크로스 체인 브릿지란
- 크로스 체인 브릿지의 공격 종류
- 웜홀 해킹 사례와 Protocol Invariant
- Protocol Invariant 를 통한 피해 예방
1. 크로스 체인 브릿지란
웜홀은 솔라나 체인과 이더리움 체인을 이어주는 크로스 체인 브릿지의 한 종류입니다. 웜홀 사례를 깊게 다루기 전에 크로스 체인 브릿지에 관해서 먼저 알아보도록 하겠습니다.
A 체인(Source)에서의 자산을 B 체인(Destination)으로 옮기고 싶다면 어떡해야 할까요?
한 체인 내에서 송금하는 것처럼 했다간 자산을 평생 잃어버리게 될 수도 있습니다. 이런 상황에서 사용하는 것이 크로스 체인 브릿지입니다.
사용자는 A 체인에서 크로스 체인 브릿지에 자산을 예치하면 이 자산이 묶이고 B 체인에서 debt token 의 형태로 발행이 되어 B체인에서 자산이 있는 것처럼 사용할 수 있게 됩니다. 반대로 B 체인에서 A 체인으로 다시 돌아오고 싶다면 debt token 을 소각하고 A 체인에서 다시 돌려 받을 수 있습니다.
위 설명이 간단해보이지만, 실제로 구현하려면 3가지의 구성요소가 유기적으로 작동되어야 합니다. 첫 번째 구성요소는 Custodian 입니다. Source chain 에서 유저가 자산을 예치하면 이 자산을 묶고 (lock) 해당 기능이 잘 되었다는 이벤트를 내보냅니다. 두 번째 구성요소는 Debt Issuer 입니다. Source chain 에서 묶인 자산을 Destination chain 에서 대표할 수 있는 debt token 을 발행해줍니다. 마지막 세 번째 구성요소는 Communicator 입니다. 서로 다른 두 체인끼리는 폐쇄적이기 때문에 각 체인에서 일어난 트랜잭션을 보고, 두 체인 사이의 메세징을 담당합니다.
이 3가지가 모두 각자의 역할을 완벽히 수행해야 브릿지가 잘 작동할 수 있습니다. 그말인 즉슨, 하나라도 어긋나게 되면 다양한 곳에서 취약점이 생길 수도 있습니다. 이제부터 이 3가지 구성요소 각각의 역할을 생각하면서 글을 읽으시면 이해하기 훨씬 쉬울 것입니다.
2. 크로스 체인 브릿지 공격 종류
Debt Issuer 는 Communicator 에서 생성된 서명에 의존해 debt token 을 발행할 수 밖에 없어서 이 서명의 무결성과 정합성이 매우 중요합니다. 그래서 대부분의 크로스 체인 브릿지 공격은 Custodial 을 속여 서명을 발생시키거나 Debt Issuer 를 속여 서명을 받았다고 착각하게 만드는 방법입니다. 각각의 경우를 사례를 통해 살펴보도록 하겠습니다.
Custodian Attacks
Custodian이 잘못 행동하게 만드는 것을 Custodian Attack 이라 합니다.
첫 번째 방법으로는 예치가 되었다는 사실을 속이는 것입니다.
Meter Hacking (https://blog.chainsafe.io/breaking-down-the-meter-io-hack-a46a389e7ae4) 이 그 사례입니다. 이 브릿지에서는 토큰을 wrapped 된 형태로 받은 후 deposit 되었다는 event 를 내보냅니다. 여기서 주의해야 할 점이 wrapped 되지 않은 형태의 토큰을 전송해도 특별한 조치를 취해주지 않으면 똑같은 형태의 event 를 내보내게 된다는 것입니다. Meter의 경우, 브릿지 컨트랙트 코드 상 wrapped 되지 않은 토큰은 예치가 불가능해 트랜잭션이 revert 되는데 예치 이벤트는 똑같이 발생되어 해킹이 되었습니다.
두 번째 방법으로는 소각 여부를 착각하게 만드는 것입니다.
Plasma Hacking (https://medium.com/immunefi/polygon-double-spend-bug-fix-postmortem-2m-bounty-5a1db09db7f1) 이 그 사례입니다. 위에서 설명드렸듯이 다시 Source chain 으로 돌아오려면 Destination chain 에서 debt token 을 소각하고 마찬가지로 Communicator 가 서명을 전달하면 Custodial 이 다시 돌려주는 방식입니다. 이 사건의 경우에 proof of burn 을 증명하는 과정에 문제가 있었고, 공격자는 하나의 burn으로 224개의 증명을 만들어낼 수 있었습니다. 이것을 활용해서 1번의 정상 거래로 224배의 자산을 만들 수 있었던 것입니다.
Debt Issuer Attacks
Debt Issuer 쪽의 취약점을 공격하는 것을 Debt Issuer Attack 이라 합니다. 원인 제공이 복합적이기 때문에 Custodian Attack 과 경계가 모호하다고 생각할 수 있지만, 결과적으로 어떤 구성요소가 잘못 행동하게 되었냐에 따라 구분합니다.
다시 생각해보면, Debt Issuer 도 결국 Custodian 역할에 반대입니다. Communicator 로 부터 검증된 서명을 통해 debt token 을 발행해주고, debt token 을 소각하면 소각 이벤트를 내보내 Custodian 에 묶어놨던 자산이 풀리게 해줍니다.
그래서 Custodian에서 온 서명을 속이거나 우회할 수 있다면 쉽게 debt token 을 발행할 수 있는 것입니다. 웜홀 해킹이 이 사례입니다. 다음 섹션에서 크로스 체인 브릿지의 Protocol Invariant 와 해킹 로직, 보완 방법을 자세히 살펴 보겠습니다.
3. 웜홀 해킹 사례와 Protocol Invariant
위에서 살펴본 다양한 크로스 체인 브릿지 해킹 사례에서 공통적으로 유지되었어야 할 Protocol Invariant 가 무엇이었을까요?
Communicator 에 의해 생성된 서명을 틀려서는 안 된다.
위에서처럼 공격 주체를 여러 개로 나눠서 설명할 수도 있지만, 결국에는 서로 다른 두 체인에서의 메세징을 담당하는 서명이 틀렸기 때문에 일어난 일들입니다. 따라서 모든 크로스 체인 브릿지에서는 Custodian 과 Debt Issuer 의 행동 근거가 되는 서명이 틀려서는 안 됩니다.
그렇다면, 웜홀은 어떤 부분이 잘못 됐었을까요?
브릿지의 3가지 구성요소의 역할을 중심으로 사건의 개요를 보면 아래와 같습니다.
Custodial 에 자산이 올바르게 예치되면 sysvar_account 에 암호화 되어 트랜잭션이 저장됩니다. (여기서 sysvar_account 란, 블록체인에서 dynamic 하게 업데이트 되는 트랜잭션 기록들이 저장되는 계정입니다.)
Communicator 는 sysvar_account 로부터 암호화된 정보를 불러와서 검증하는 과정을 거치고 서명을 생성합니다. 이 때, 암호화된 정보를 검증하는데에는 third party 라이브러리(위의 코드에서 Secp256k1)를 사용합니다. 하지만, 웜홀에서 정보를 불러올 때 사용한 함수는 라이브러리가 버전이 올라가면서 기능이 퇴색된 함수(load_current_index)였습니다.
그 결과로, 가져온 정보가 실제로 sysvar_account 에서 온 것인지 검증하지 못 하는 일이 생겼고, 공격자는 가짜 account 로 sysvar_account 를 대체하여 가짜 서명을 통해 Debt Issuer 를 속일 수 있었고, 0.1ETH 만 있는 지갑으로 120,000ETH 를 발행하였습니다.
4. Protocol Invariant 를 통한 피해 예방
웜홀에서의 Protocol Invariant인 서명은 sysvar_account 에 저장된 정보를 검증하는 방식입니다. 따라서 이 서명의 무결성과 정합성을 지키기 위해서는 Communicator 가 검증하는 서명뿐만 아니라 참고하는 정보의 출처가 정확해야했습니다. 이 경우에는 서명의 형식은 옳았지만 출처가 sysvar_account 가 아니었기 때문에 문제가 생긴 것입니다.
따라서, 위 코드의 하이라이트된 부분처럼, Protocol Invariant 를 고려해 출처를 한 번 더 확인했다면 예방할 수 있었습니다.
혹자는 브릿지를 시도하는 지갑의 잔고가 Protocol Invariant 가 될 수 있지 않는지 궁금할 수도 있습니다. 왜냐하면 최소한 예치하려는 만큼의 잔고는 있어야 Custodial 에서 발생된 이벤트가 믿을만 하기 때문입니다. 하지만, 서로 다른 블록체인은 폐쇄적이기 때문에 트랜잭션으로 일어나지 않은 이벤트(e.g. balanceOf)는 확인할 수야 있겠지만, 이 또한 허점이 많을 수 있습니다.
이번 글에서는 크로스 체인 브릿지의 Protocol Invariant 를 살펴보고, 웜홀 해킹 사례도 자세히 살펴봄으로써 Protocol Invariant 를 잘 고려했다면 예방할 수 있었다는 것을 알 수 있었습니다. 하지만 사후 분석이 아닌 실제 코드를 설계할 때 고려할 수 있는 부분인지에 대한 지적이 충분히 있을 수 있습니다. 왜냐하면, 분명 sysvar_account 에서 가져오는 코드를 쓴 것임에도 버전 차이 때문에 취약점이 노출된 것이기 때문입니다.
따라서 Protocol Invariant 를 통해 모든 문제를 해결할 수 있다는 것은 아니지만, 최소한의 코드를 짤 때나 읽을 때의 경각심은 줄 수 있다는 점에서 좋은 가이드라인이라고 생각합니다.
Reference
Bridge Explanation (https://medium.com/@Magpieprotocol/blockchain-bridges-custodial-vs-non-custodial-46b8e786a7ca)
Meter hacking analysis (https://blog.chainsafe.io/breaking-down-the-meter-io-hack-a46a389e7ae4)
pNetwork hacking analysis (https://medium.com/pnetwork/pnetwork-post-mortem-pbtc-on-bsc-exploit-170890c58d5f)
pNetwork hacking post mortem (https://blocksecteam.medium.com/the-further-analysis-of-the-poly-network-attack-6c459199c057)
Wormhole hacking post mortem (https://www.certik.com/ko/resources/blog/1kDYgyBcisoD2EqiBpHE5l-wormhole-bridge-exploit-incident-analysis)
Wormhole hacking analysis (https://arstechnica.com/information-technology/2022/02/how-323-million-in-crypto-was-stolen-from-a-blockchain-bridge-called-wormhole/)