TL;DR : Crosschain 탈중앙화 대출 서비스인 Qubit Finance가 해킹되어 약 960억에 달하는 가상자산이 유출되었다. 원인은 Crosschain Bridge의 프로그램과 컨트랙트의 데이터 검증이 상당히 빈약하여, ETH를 입금하지 않고도 BSC로 xETH를 옮길 수 있었다. 해커는 ETH 없이 xETH를 발행하여 그것을 담보로 가상자산을 빌려 이익을 보았다. 이 글은 해당 취약점에 대한 분석을 담고 있다.
KLEVA Protocol에 비해 피해규모가 크기도 하고, 이미 KLEVA 쪽 사고는 Theori에서 분석이 나와 있는 지라, KLEVA 보다 Qubit Finance 쪽을 다루기로 했다.
대체 무슨 일이 일어난거야?
위의 트위터에 따르면, 0xd01ae1a708614948b2b5e0b7ab5be6afa01325c7에 의해서 BSC에 있는 xETH를 무한히 발행할 수 있게 되었다고 한다. xETH란, Qubit Finance에서 사용하는 Cross-chain Ethereum 토큰이다. xETH는 원래 Ethereum 네트워크에서 Ethereum을 Bridge Contract에 예치하고, 신뢰할 수 있는 인증자가 그걸 확인해서 BSC의 Bridge Contract에 입금을 처리해서 xETH를 발행해주는 방식이다. Qubit Finance에서는 이 xETH를 담보로 대출을 받을 수 있다.
해커는 모종의 방법으로 ETH 없이 xETH를 무한히 발행할 수 있게 되었고, 그 xETH를 담보로 BSC에 있는 Qubit Finance의 wETH, BTC-B 등 약 960억원 어치를 대출 받아 갔습니다. 0원 짜리 가짜 xETH로 960억원을 대출받아 간 것이다.
어떻게 이게 가능했던거야?
그러니까 정상적으로 xETH가 발행이 되려면 동일한 양의 ETH를 입금해야 하는 것이다. 그런데, 해커는 어떻게 ETH 없이 xETH를 발행할 수 있었을까?
공격자의 Ethereum 트랙젝션 기록을 보면 QBridge를 향한 토큰 입금 (Deposit) 호출을 14번이나 호출하고 있는 것을 볼 수 있다. 그런데 흥미로운 점은 토큰 입금을 호출 했지만 그 어떤 토큰도 이동하지 않고 있다.
위의 코드는 그닥 특별할 것 없이 토큰 입출금을 관리하는 자원 ID를 받아서 토큰 브릿지 handler에게 입금을 처리하도록 하는 함수를 호출한다.
문제는 QBridgeHandler에서 발생한다. 위의 ResourceID로 resourceIDToTokenContractAddress를 호출하면 0x0000.. 즉 Zero address가 나오게 된다. 0x0000이 Whitelist 되어 있고, (0x0000은 통상적으로 ETH에 관련된 값을 저장시킬 때 사용하기 때문에 Whitelist 해둔 것이다.) 따라서 모든 방어 로직을 뚫어 내고 0x0000 주소를 가진 토큰을 입금자에게 이동 시키게 된다.
만약 저기서 토큰 이동 하기 전, tokenAddress가 컨트랙트인지 확인했더라면 문제가 없었겠지만, 안타깝게도 토큰 이동에 safeTransferFrom을 사용했다. 0x0000..은 EOA, 즉 컨트랙트가 아니기 때문에, low-level call을 이용하는 safeTransferFrom은 무조건 성공했는 값을 반환하게 되어 있다. (메시지를 담은 트랜젝션을 그냥 지갑으로 보내는 게 실패할 리 없다.)
입금은 처리되었고 0x0000... 자산 (ETH)이 지정한 갯수만큼 입금 되었다는 Deposit 이벤트가 네트워크에 전파된다. 중앙화 서버인 QBridge 프로그램은 Ethereum 네트워크에 전파된 Deposit 이벤트를 확인하고, BSC에서 xETH를 발행할 수 있게 처리해 두게 된다. 만약 여기서 DepositETH 함수와 Deposit 함수의 이벤트를 다르게 설정해 두었다면, 봇이 이벤트를 보는 과정에서 "어? 토큰 입금인데 왜 ETH를 옮겼다고 나오지?" 하고 xETH를 발행하지 않았을 것이다.
요약하자면 QBridge의 다음과 같은 실수들로 대량의 무가치한 xETH가 발행된 것이다 :
data를 제대로 검증하지 않았다 ( tokenAddress가 0x000..이 아닌지 확인하지 않았다)
depositETH와 deposit 함수가 분리되어 있음에도, 이벤트를 분리하지 않아 Bridge가 의도치 않은 작동을 하게 했다.
safeTransferFrom을 할 때, 대상이 ERC20을 따르는지 확인하지 않았다.
Bridge가 이벤트만을 데이터 소스로 사용하고 발행이 올바르게 이루어지는 것인지 검증하는 과정을 구현해 두지 않았다.
정리하며
지금까지 Qubit Finance에서 일어난 Bridge 공격 사건에 대해서 간단하게 원인 분석을 해 보았다. Bridge 프로그램이 만약 더 신중하게 데이터를 검증했거나, 컨트랙트 코드가 조금 더 명확하게 작성되어 있었더라면, 사실 취약점이 존재해도 막을 수 있는 공격 이었다고 생각한다.
Qubit Finance가 그래도 Bridge를 Ethereum-BSC만 지원해서 망정이지, KLAY-ETH를 지원했다면, 2022년 1월 27은 Klaytn 커뮤니티에게 악몽같은 하루가 되었을 수도 있다. Klaytn.. 참 바람잘날 없다.
2021년 중순 이후부터 MultiChain이나 ChainSwap 사건과 같이 Cross-chain bridge에 대한 공격이 늘어나고 있다. 각각의 공격들이 굉장히 큰 액수의 피해를 입게 되었다. 앞으로 새로운 Cross-chain bridge가 나올때 마다 철저한 검증을 거칠 필요가 있겠다.
On July 25th, 2023, EraLend suffered a 2.76M USD damage due to a Read-Only Reentrancy attack exploiting their collateral valuation algorithm. They are working to recover funds. SyncSwap, related in the attack, claims no responsibility. Impact on other zkSync Era protocols appears minimal.
Donkey 때도 그렇고, 실타래 때도 그렇고, 국내 DApp에서 이슈가 생길 때마다 블로그에 글을 쓰는 것이 좋게 느껴지지는 않아서, 다음부터는 이러한 "크립토 렉카" 같은 글은 지양해야겠다고 생각했습니다만, Klaytn의 사내 벤처 회사가 만든 DApp에 자금 세탁 기능이 포함되어 있다고 해서 너무 궁금해진 나머지, 코드를 분석해 정말로 자금 세탁 기능이 있는 것인지 확인해 보았습니다.
TL;DR : 전부터 하고 싶었던 이야기입니다. NFT가 왜 생겨났고, 게임과 예술품에 우선적으로 적용이 되었는지, 그리고 그 과정에서 어떤 오해들이 있었는지 이야기합니다. 그리고 나서 왜 NFT가 디지털 자산, 나아가서는 실물 자산과 연동되는 것이 필연적인지, 앞으로 NFT가 우리에게 있어 어떤 형태의 투자 상품이 되는 것이 바람직 할지에 대해서 간단하게 이야기 합니다.