北京时间2022年10月7日,据欧科云链链上卫士安全团队监测,BNB Chain遭遇有史以来金额最大的黑客攻击。据链上数据显示,10月7日黑客利用跨链桥漏洞分别于2点26分、4点43分共获取200万个BNB,价值约5.66亿美元(按发生时价格计算)。
与以往跨链桥攻击不同的是,此次黑客攻击获取的200万个BNB并非从链上用户的账户中偷盗而来,而是通过BNB Chain跨链桥的安全漏洞,凭空增发出来的。
这里需要强调的是,虽然攻击手法不同,但凭空生成的大量资产流入市场与资产被盗带来的影响一样,均会对当前市场带来严重的负面影响,甚至需要广大用户为项目方的安全事故买单。
关于此次事件的经过,欧科云链链上卫士团队已第一时间进行了时间线梳理(以下均为北京时间):
1
10月6日7点27分
黑客使用ChangeNOW服务转入了100多个BNB到BNB Chain上,作为起始攻击资金。
2
10月7日0点55分
黑客调用系统RelayerHub合约 0x1006 进行注册,成为relayer。
3
10月7日2点26分、4点43分
黑客使用BNB Chain跨链桥的漏洞盗取200万个BNB。
4
10月7日2点31分、2点37分
黑客使用Venus的借贷服务,抵押了90万个BNB,从里面借走了5000万USDT、6250万 BUSD和3500万USDC。然后通过Stargate跨链桥,将资产转移到ETH、AVAX、FTM等网络上,总计转出资产约为9000万美金。
5
10月7日6点19分
币安暂停BNBChain链
6
10月7日15点左右
币安宣布重新开放BNBChain链的同时,BSC节点程序通过黑名单与暂停iavlMerkleProofValidate功能2种方式,阻止被盗资金流动与潜在的攻击。
缺少“3行代码”
让BNB Chain链增发200万个BNB
BNB信标链(BEP2)是币安基于Cosmos开发的一条应用链,BNB Chain链(即BSC链,支持BEP20资产)是兼容EVM的一条链,BSCTokenHub是BNB信标链和BNB链之间的跨链桥。
正常状态下,BNB Chain链使用预编译合约0x65验证BNB信标链提交的IAVL的Proof,对提交的Proof进行校验,校验成功后在BNB Chain链上Mint对应的BNB。
而此次黑客事件发生,主因是BNB Chain链对提交的Proof边界情况处理不足,它仅考虑了Proof只有一个Leaf的场景,对多个Leaves的处理逻辑不够严谨。黑客通过构造了一个包含多Leaves的Proof数据,绕过BNB Chain上的校验,从而在BNB Chain链增发了200万个BNB。
通过对漏洞的解析及BNB Chain链最新代码,链上卫士团队发现在同样的位置,事故发生前并没有这“3行代码”。
此段代码,是指IVAL库在计算Hash过程需要考虑pin.Left和pin.Right均大于0的情况,事故发生后官方增加此段代码,是为了可以避免发生同类情况。
这里以其中一次攻击交易(增发100万个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校验。
黑客攻击构造的数据中,包括了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
跨链桥再次敲响安全警钟
跨链桥,作为区块链的基础设施之一。其自身的中心化特性,为去中心化的Web3世界引入了中心化风险,其安全性很大程度上取决于跨链桥项目方自身。
本次黑客攻击通过构造了一个包含多Leaves的Proof数据,绕过BNBChain上的校验,从而在BNB链造成了BNB增发。相较以往线下漏洞、私钥泄露等攻击方式,这次攻击难度更高,且涉及金额更大,据统计涉及金额在8.5亿左右,创下历史新高。
目前OKLink多链浏览器已对BNB Chain黑客地址进行风险标签标记(标记为“Hack”),关于此次被盗后续,链上卫士团队还将进一步追踪案件细节并及时同步。
原文始发于微信公众号(欧科区块链):只因缺少这“3行代码”,黑客凭空获取200万个BNB!