Attacks on BSC chain : How did this happen and the view of its potential affects.

 

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

1. Background

In Binance Ecosystem there are two chains. One is called BNB chain, which is a EVM competable chain ( Forked from Geth ) . And another one is Binance Chain ( Built on cosmos sdk) . And there is a "Bridge" between Binance Chain and BNB chain, which is attacked lately. In this write up we will analyze how the attack is committed , and more we will evaluate the effect of this attack.

 

2. How this attack happens ?

Based on samczsun's analysis, we will go through details of the BSC Cross Chain attack initiated on Oct. 7th, 2022.

 

2.1 Overview

First, we review the process of how the BSC Cross-Chain Contract works.

  1. A relayer sends a transaction to BSC Cross-Chain Contract to call handlePackage function with payload and proof;
  2. The Cross-Chain Contract verifies the validity of the payload based on the Merkle proof proof;
  3. If the payload is valid, the Cross-Chain Contract decodes the payload to msgBytes and sends it to the TokenHub Contract to handle the message;
  4. After TokenHub Contract executing the msgBytes, the receiver will receive the amount of $BNB specified in the msgBytes from a full-zero address.

 

In the attack, the attacker sent an invalid payload with a forged proof which passed the verification of Merkle proof in step 2, taking 2 million $BNB from the TokenHub Contract. To be specific, in the forged proof , a mallicious leafNode is added to the IAVL tree rangeProof without changing the result of its hash root computation. The IAVL tree is a new data structure designed by Cosmos team that combines the benifits of Merkle tree and AVL tree. The existence proof of an IAVL tree leaf node is similar to the process of the Merkle proof.

 

2.2 Attack details

2.2.1 Verify proof on solidity

How did this attack happen?Let's go thourgh the call logics of the Cross-Chain Contract first.

(1) In contracts/CrossChain.sol, the MerkleProof.validateMerkleProof is called

 

(2)In contracts/MerkleProof.sol, the function validateMerkleProof first assembles all the inputs above into bytes and calls the pre-compiled contract 0x65 of bnb-chain.

 

(3) In pre-compiled contract, the func (c *iavlMerkleProofValidate) Run(input []byte) is triggered. The input bytes of step (2) are unmarshalled into kvmp. Then, kvmp.Validate() is called

Notice: the data structure of kvmp is defined as

2.2.2 Precompile contract for validate IAVL tree

(4) In github.com/bnb-chain/bsc/core/vm/lightclient/types.go, the func (kvmp *KeyValueMerkleProof) Validate()

  1. first goes to err := prt.VerifyValue(kvmp.Proof, kvmp.AppHash, kp.String(), kvmp.Value) .
  2. then goes to prt.Verify(proof, root, keypath, [][]byte{value})
  3. and then goes to `poz.Verify(root, keypath, args)
  4. finally goes to args, err = op.Run(args)

From step 1 on, the bsc project utilizes the library of tendermint.

 

(5) The opz in step (4) is decoded by poz, err := prt.DecodeProof(proof) in func (prt *ProofRuntime) Verify as follow:

image-20221009164226504

Notice: The Ops specify the ways to calculate the Merkle root of IAVL tree and are pre-registered by func DefaultProofRuntime() (prt *merkle.ProofRuntime) in github.com/bnb-chain/bsc/core/vm/lightclient/multistoreproof.go

 

(6) As shown in the figure above, it will first goes to func (op IAVLValueOp) Run

In order to pass the verification of MerkleProof.validateMerkleProof in step (1), two requirements should be satisfied

(7) To satisfy the above requirements, the root caculated by op.Proof.ComputeRootHash() in func (op IAVLValueOp) Run should be unforgeable. The op.Proof.ComputeRootHash() first

  1. goes to func (proof *RangeProof) ComputeRootHash()
  2. then goes to func (proof *RangeProof) _computeRootHash()
  3. and then goes to the closure func(path PathToLeaf, rightmost bool) of _computeRootHash()
  4. finally goes to hash = (pathWithLeaf{Path: path, Leaf: nleaf,}).computeRootHash()

 

(8) As we can see, the root is only calculated by func (pwl pathWithLeaf) computeRootHash() through hashing the concatenation of IAVL tree left leaf node and its path .

For ease of understanding, we give out a naive illustration of the Merkle root calculation in IAVLValueOp. Also, you could go through samczsun's code.

image-20221009200332800

Notice: the RangeProof struct used for Merkle root calculation is defined as followed

Thus, there should be something wrong with the leave node or inner node path computations.

 

2.2.3 Where Things Become wired

(9) As a result, in github.com/tendermint/iavl@v0.12.0/proof_path.go, we found that the hash of inner node path is calculated in a wrong way.

Notice: Although the version utilized is v0.12.0, the problem still exists in the latest version at the moment of writing. However, the issue#579 is already noticed by the tendermint team.

Based on the func (pin proofInnerNode) Hash(childHash []byte) above, the len(pin.Left) of target pin is not 0, then the code goes into else branch. As we can see, the pin.Right is not counted into the buf in the else branch. Therefore, although a malicious node is added to the IAVL tree, the hash of the IAVL tree remains unchanged.

 

3. Potential affects of the library flaw

Usually, attacks on bridges are committed due to the flaw of contract codes or misconfig during the bridge upgrade. However, this time the attack is built on the incorrect implementation of the base library.

As a result, many of the projects that utilized the flawed library github.com/cosmos/iavl including cosmos-sdk, the core component of the cosmos ecosystem, and the IBC protocol may be affected. We categorized the potential affected projected into two categories: projects built with cosmos-sdk and projects bridged with cosmos.

 

3.1 Potential attacks on Cosmos

3.1.1 IBC protocol

The IBC protocol is the standard of cross-chaining between Cosmos ecosystems. The IBC protocol utilize a vector commitment to verify the valid executions of transactions on the source chain while cross-chaining. The IBC protocol specifies that the vector proofs which include proofs of IAVL trees can be used via ics23. Therefore we need to check whether the buggy implementation of github.com/cosmos/iavl is introduced into the implementation of ics23.

The implementation of ics23 which includes ics-23-go and confio/ics23 does not utilize the IAVL library. Thus, it will be not be affected.

3.1.2 Cosmos-SDK

Cosmos-SDK and Cosmos light client use IAVL+ tree to store states and proof existence or nonexistence of states respectively. Although Cosmos-SDK provides a handy tool to convert IAVL tree proof to ics23 proof, there is no evidence that the it uses IAVL proof directly. Thus, Cosmos-SDK is not under the impact.

 

3.2 Potential attacks on project bridged with Cosmos

3.2.1 Gravity bridge

Gravity bridge uses multisig to bridge messages between Ethereum and Cosmos which does not rely on vector commitments.

3.2.2 EVMOS

EVMOS is a EVM compatible chain which bridges ETH to the EVMOS ecosystem. However, the bridge verifies the proof of messages based on ics23 and thus will not be affected.

 

3.3 Conclusion

We do not found any further potential attacks on the flaw library currently. However, projects which use github.com/cosmos/iavl directly (not ics23 ) for message verifications are still vulnerable. BE Careful !

 

4. Who We are

BHBA_DAO is a decentralized and autonomous student organization formed by Beihang students. Our vision is to provide a platform for blockchain enthusiasts to learn and share innovation ideas. We will help university students to learn blockchain technology and master blockchain development skills through activities such as meet-ups, industry researches, and development activities like Hackathon. All university students are welcome to join us, and welcome to follow our Twitter @BHBA_DAO for latest progress.