BSC 链上的攻击:为何发生及其潜在的危害

Authors: | Tofu | CyberPigeon| Beihai |made @BHBA_DAO

背景

币安生态有两条链,其中大家最常用交互的链为BNB chain,这条链由以太坊魔改而成。还有一条链为币安链(Binance Chain),这条链基于 Cosmos SDK 开发而成。由于BNB代币需要在两条链之间流动,因此,BNB chain和 Binance Chain之间有一个币安官方的资产跨链桥,本次受到攻击的就是这个桥。本文会从代码调用角度对这次攻击流程进行详细解析。

 

攻击怎么发生的 ?

根据 samczsun 的分析, 我们看看币安链的攻击怎么发生的。

 

概览

首先,我们回顾一下BSC跨链合约的调用流程。

  1. Relayer向BSC 跨链合约发起一笔交易,调用 handlePackage 函数,函数入参数主要为 payloadproof
  2. 跨链合约根据 默克尔证明 验证 payload 的有效性。
  3. 如果 payload 有效, 将 payload 解析为 msgBytes ,并发送至 TokenHub Contract 处理消息。
  4. TokenHub 合约 处理 msgBytes, 接收者从全零地址接收指定数量的BNB代币

 

本次攻击中,攻击者发送了一个无效的payload并伪造其证明proof,该证明通过了步骤2中的Merkle证明验证,并成功从TokenHub合约中盗取了200万美元BNB。具体来说,攻击者通过将一个恶意的叶子节点添加到IAVL树的rangeProof中实现proof的伪造,同时该伪造并未引起其默克尔根的计算结果的改变。值得一提,IAVL树是Cosmos团队设计的一种新的数据结构,其结合了Merkle树AVL树的优点,且IAVL树叶节点的存在性证明与Merkle证明的过程相似。

 

攻击详解

Solidity 上的默克尔证明

这种攻击是如何发生的? 让我们先来看看跨链合约的调用逻辑。

(1) 在 contracts/CrossChain.sol中, MerkleProof.validateMerkleProof 函数被调用

 

(2)在 contracts/MerkleProof.sol中,函数 function validateMerkleProof 首先将上述步骤(1)中的所有入参组装成十六进制字节码,并通过staticcall调用bnb-chain的0x65预编译合约

 

(3) 在 预编译合约中, func (c *iavlMerkleProofValidate) Run(input []byte) 被调用, 步骤(2)的入参被解析并写入变量kvmp。随后, kvmp.Validate() 被调用

值得一提, kvmp 数据结构定义如下:

 

IAVL树验证预编译合约

(4) 在 github.com/bnb-chain/bsc/core/vm/lightclient/types.go文件中, 函数 func (kvmp *KeyValueMerkleProof) Validate() 被调用,后续相应调用流程如下

值得一提,从第1步开始,BSC开始使用 tendermint相关代码库。

具体的调用流程读者可以阅读如下代码

 

(5) 步骤(4)中的opz是由函数 poz, err := prt.DecodeProof(proof)解码proof所得,具体解析结果如下:

注意。这些 "操作 "指定了计算IAVL树的Merkle根的方法,并由 func DefaultProofRuntime() (prt *merkle.ProofRuntime) in github.com/bnb-chain/bsc/core/vm/lightclient/multistoreproof.go 指定

 

(6) 如上图所示,它将首先跳转到 func (op IAVLValueOp) Run

为通过上述步骤(1)中 MerkleProof.validateMerkleProof的验证 ,上述代码需要满足两点要求

 

(7) 为了满足上述要求,关键在于函数 op.Proof.ComputeRootHash() in func (op IAVLValueOp) Run 计算所得的root是不可伪造的。因为,我们进一步深入函数 op.Proof.ComputeRootHash() 进行细节追踪:

上述调用过程细节如下,有兴趣的读者可以进一步阅读代码

 

(8) 因此,从函数func (pwl pathWithLeaf) computeRootHash()中我们可以发现,步骤(6)中所述 root 仅与IAVL树的最左侧叶子节点及其路径级联的哈希相关。

为便于理解,我们给出了一个IAVL树的示意图。代码层面的理解可以参考samczsun's code

值得一提,上图中IAVL树中的数据类型在实际的结构体RangeProof 定义中的对应关系如下

因此,我们可以初步判断,在IAVL树的最左侧叶子节点及其路径的哈希级联过程中存在漏洞

 

核心的漏洞

(9) 最后,我们在代码库github.com/tendermint/iavl@v0.12.0/proof_path.go相关文件中,发现了中间节点哈希级联运算函数的漏洞。值得一提,尽管在BSC中调用的版本是v0.12.0,我们发现在该库的最新版本实现中仍然存在相应问题。无需担心的是,相关问题已经有热心开发者通过issue#579反馈给tendermint的相关开发团队。

结合我们的图例,在上述函数 func (pin proofInnerNode) Hash(childHash []byte) 中,目标中间节点的左孩子 len(pin.Left) 长度不为0,根据代码逻辑,理应进入到else分支。在else分支中,我们发现其计算仅涵盖了pin.LeftchildHash,而 pin.Right 并未纳入哈希计算中。因此,尽管一个恶意的节点被插入到IAVL树的证明中,其IAVL树的根哈希校验依旧可以通过,最终导致了此次攻击的发生。

 

其他潜在攻击

这次针对跨链桥的攻击比较特殊:往常桥出现问题往往是智能合约编写或者升级出现漏洞,而这次是因为区块链底层以来的库出现问题。

本次有问题的库为 github.com/cosmos/iavl,使用该库的项目包括 cosmos生态的核心组件 cosmos-sdk , IBC 等. 因此理论上使用cosmos-sdk构建的的项目、与cosmos跨链的项目都有可能受到影响,下文我们将分析是否有实际的攻击可能性。

 

针对COSMOS的潜在攻击

IBC protocol

IBC协议:IBC协议为COSMOS生态之间跨链的标准。IBC协议在跨链时,会使用 vector commitment 来验证源链的交易确实发生。IBC协议中通过 ics23 规定了可使用的向量证明,其中就包括了 iavl树的证明。因此我们需要调查 github.com/cosmos/iavl 的错误实现是否引入 ics23 的实现中。

ics23实现为ics-23-go 和 [confio/ics23](https://github.com/confio/ics23/blob/a4daeb4c24ce1be827829c0841446abc690c4f11/go/proof.go#L122。这里的实现没有使用iavl库,因此没有收到影响。

Cosmos-SDK

Cosmos-SDK 使用 IAVL+ 树来存储状态 , cosmos-sdk轻客户端使用 IAVL+ 树来证明某个状态的存在或者不存在。 Cosmos-SDK 提供了一个 工具将 IAVL树的证明 转换 为 ics23 Proof . 然而,没有证据表明SDK直接使用IAVL树的证明。 所以cosmos-sdk并没有受到影响

 

与COSMOS生态桥接的项目

Gravity bridge

Gravity bridge使用多签来做Evm与COSMOS之间的跨链,因此不受本次攻击影响。

EVMOS

EVMOS使用EVM上的 simple merkle 做跨链,不受影响。

结论

目前我们没有发现可能的攻击路径 . 然而直接使用 github.com/cosmos/iavl 库做验证的项目依旧可能存在被攻击的风险(即使已经升级SDK)。小心!

 

我们是谁

BHBA_DAO是一个由Beihang学生组成的去中心化学生自治组织。我们的愿景是为区块链爱好者提供一个学习交流的平台。我们将通过区块链科普活动、区块链行业研究、区块链技术开发活动等丰富多彩的活动帮助在校学生了解区块链生态,掌握区块链技术。欢迎所有在校学生加入我们,欢迎关注我们的推特@BHBA_DAO了解最新进展。