事件背景
北京時間2022年10月7日凌晨,BNB Chian跨鏈橋BSC Token Hub遭遇攻擊。黑客利用跨鏈橋漏洞分兩次共獲取200萬枚BNB,價值約5.66億美元。
漏洞分析
BSCTokenHub是BNB信標鏈(BEP2)和BNB鏈(BEP20 或 BSC)之間的跨鏈橋。BNB鏈使用預編譯合約0x65驗證BNB信標鏈提交的IAVL的Proof,但BNB鏈對提交的Proof邊界情況處理不足,它僅考慮了Proof只有一個Leaf的場景,對多個Leaves的處理邏輯不夠嚴謹。黑客構造了一個包含多Leaves的Proof數據,繞過BNBChain上的校驗,從而在BNB鏈造成了BNB增發。
以其中一次攻擊交易為例:0xebf83628ba893d35b496121fb8201666b8e09f3cbadf0e269162baa72efe3b8b
黑客構造輸入數據payload和proof,輸入參數通過validateMerkleProof校驗,返回值為true。
在後續IApplication(handlerContract).handleSynPackage處理中,合約給黑客增發100萬個BNB。
函數調用過程
交易首先調用CrossChain合約0x2000的handlePackage函數:
handlePackage會進一步調用MerkleProof.ValidateMerkleProof對輸入的proof進行校驗:
MerkleProof相關代碼可以看到,實際的驗證邏輯是使用預編譯合約0x65完成:https://github.com/bnb-chain/bsc-genesis-contract/blob/master/contracts/MerkleProof.sol#L66
系統預編譯合約0x65對應iavlMerkleProofValidate功能:https://github.com/bnb-chain/bsc/blob/f3fd0f8bffb3b57a5a5d3f3699617e6afb757b33/core/vm/contracts.go#L81
系統合約0x65實現代碼如下,主要邏輯為使用DecodeKeyValueMerkleProof解碼輸入參數,並調用Validate進行校驗:
https://github.com/bnb-chain/bsc/blob/master/core/vm/contracts_lightclient.go#L106
其中kvmp.Validate()實現代碼如下:https://github.com/bnb-chain/bsc/blob/master/core/vm/lightclient/types.go#L220-L234
DefaultProofRuntime構造函數使用IAVL庫進行Proof的驗證:
IAVL代碼問題
IAVL的Proof校驗過程中,Hash計算存在漏洞,導致黑客可以在Proof添加數據,但計算Hash時並沒有用到添加的數據。詳細分析如下:
在len(pin.Left)不為0的分支中,計算Hash並沒有使用pin.Right數據。黑客利用該處漏洞構造數據,添加proof.LeftPath[1].Right數據,但是該數據並不參與Hash計算。https://github.com/cosmos/iavl/blob/master/proof.go#L79-L93
根據上述分析,正常數據組織結構如下,proof.LeftPath[1].Right為空值,計算得到正確的Hash。
- proof.LeftPath = len(2)
- proof.LeftPath[0]是一個正常數據,proof.LeftPath[1].Left是一個正常數據,proof.LeftPath[1].Right空值
- proof.InnerNodes = len(0)
- proof.Leaves = len(1),proof.Leaves[0]是一個正常數據
黑客構造攻擊數據結構如下,添加proof.LeftPath[1].Right數據,且該數據不參與Hash計算。
- proof.LeftPath = len(2)
- proof.LeftPath[0]是一個正常數據,proof.LeftPath[1].Left是一個正常數據,proof.LeftPath[1].Right是一個偽造數據
- proof.InnerNodes = len(1), InnerNodes[0]=nil
- proof.Leaves = len(2),proof.Leaves[0]是一個正常數據,proof.Leaves[1]是一個偽造數據
- 且proof.LeftPath[1].Right = COMPUTEHASH(proof.Leaves[1])
IAVL的Proof校驗代碼如下,主體邏輯為COMPUTEHASH遞歸調用。由於lpath.Right也為黑客輸入數據,使得黑客構造的數據能夠通過bytes.Equal(derivedRoot, lpath.Right)的校驗,並返回上一輪COMPUTEHASH通過proof.Leaves[0]計算的結果,該結果為正常數值,從而繞過了IAVL的Proof校驗。
https://github.com/cosmos/iavl/blob/master/proof_range.go#L222-L309
黑客攻擊構造的數據中,包括了IAVL:V和multistore相關數據,multistore數據也是基於IAVL進行操作,原理是一樣的,不再進行詳細分析。
這次IAVL Proof暴露的問題在於,數據局部的變化無法反應到整體,使得校驗發生錯誤。在Cosmos生態中,IBC使用 ICS23來做數據的校驗處理,ICS23與IAVL Proof校驗不同點在於,ICS23會對所有的「葉子節點」的值進行數據校驗,最後計算得出的根Hash再與鏈上數據進行校驗,OKC採用的是ICS23的Prove,因此不存在BNBChain這次遇到的安全漏洞。
測試驗證代碼
利用黑客攻擊交易數據,基於BNBChain單元測試代碼,增加了基於黑客攻擊交易的測試用例,可以完整復現黑客的攻擊交易。單元測試代碼利用iavlMerkleProofValidate.Run介面驗證輸入數據,即相當於調用預編譯合約。https://github.com/BananaLF/bsc/blob/bsc-hack/core/vm/contracts_lightclient_test.go#L99-L100
利用黑客攻擊交易數據,構造新的payload數據為value := []byte(「okc test hack」),並對proof相應數據進行了修改,即修改proof.LeftPath[1].Right和proof.Leaves[1]對應的數據,新構造的數據可以通過okcIavlMerkleProofValidate校驗,即修改了黑客數據也能通過校驗。另外,如下單元測試代碼對原始黑客數據和修改後的數據兩種case都進行了校驗,且校驗都能成功,從而說明如下測試代碼利用本文所述漏洞成功進行了復現。https://github.com/BananaLF/bsc/commit/697c5cd73a755a7c93c0ed6c57d069e17f807958
事件過程
1)北京時間2022年10月6日7點27分黑客使用ChangeNOW服務轉入了100多個BNB到BSC鏈上,作為起始攻擊資金:0xa84f85e1afc3e1b8ed5111ba16e11325f8fc5d6081cb6958becd6a333f6d0d1d
2)北京時間2022年10月7日0點55分黑客調用系統RelayerHub合約 0x1006 進行註冊,成為relayer:0xe1fe5fef26e93e6389910545099303e4fee774427d9e628d2aab80f1b53396d6
3)黑客使用Bsc跨鏈橋的漏洞執行了兩次,總共從裡面盜取了200萬個BNB:
北京時間2022年10月7日2點26分:https://www.oklink.com/zh-cn/bsc/tx/0xebf83628ba893d35b496121fb8201666b8e09f3cbadf0e269162baa72efe3b8b
北京時間2022年10月7日4點43分:https://www.oklink.com/zh-cn/bsc/tx/0x05356fd06ce56a9ec5b4eaf9c075abd740cae4c21eab1676440ab5cd2fe5c57a
4)黑客使用Venus的借貸服務,抵押了90萬個BNB,從裡面借走了5000 萬 USDT、6250萬 BUSD和3500萬USDC。
5)黑客使用Stargate跨鏈橋,將資產轉移到ETH,AVAX,FTM等網路上,總計轉出資產約為9000萬美金。
6)北京時間2022年10月7日6點19分,幣安暫停BNBChain鏈。
目前,OKLink 多鏈瀏覽器已標記 BNB Chain 被盜案黑客地址(0x489A 開頭),關於此次被盜後續,歐科雲鏈鏈上衛士團隊將進一步追蹤案件細節並及時同步。