Binance Smart Chain Token Bridge Hack

Author

Blockchain security researcher

Backstory

On October 6th 2022, the BSC Token Hub bridge (hereinafter BSC), belonging to the largest cryptocurrency exchange, Binance, was hacked. This was one of the largest cryptocurrency hacks ever. BSC ensures the interaction between the Binance Beacon Chain blockchain used by Binance for decentralized management (stacking, voting) and Binance Smart Chain, an EVM-compatible blockchain used to create various decentralized applications. Hackers withdrew 2 million BNB (Binance’s cryptocurrency) from the bridge protocol, with 1 BNB worth $293 at the time. A total of $586 million was stolen.

The technical aspects

Blockchain bridges are used to transfer data and assets between heterogeneous blockchains. They act as intermediaries to send transactions, so whether you trust a transaction sent from blockchain A to blockchain B depends on the bridge between A and B. To trust a transaction provided by blockchain A, the bridge needs to validate it. Depending on the bridge logic, there are several ways to verify transactions, but they all depend on how data is recorded and stored in the blockchain, that is, the tree-like structure of data representation.

Each node of the binary tree is a concatenation of hashes from its two child nodes. The end nodes of the tree corresponding to the transactions added to the blockchain are called leaves, and the top root nodes are called roots. This tree-like structure of data representation is called a binary search tree and allows you to easily check the legitimacy (authorship) and integrity of data recorded in any of the tree nodes. Knowing the hash of the data being checked and the values of intermediate nodes used when calculating the root hash, you can perform the Merkle proof: starting at the bottom of the node, check that each successive hash is correct, up to the root. Any discrepancy will indicate that the data in the node has been tampered with.

Binary tree of hashes

How BSC transaction validation works

The BSC bridge uses a balanced AVL tree, a kind of a binary search tree, to validate transactions. For each node of this tree, the height of its two branches differs by no more than 1. The verification algorithm is called in the handlePackage function of the main CrossChain smart contract, which processes token transfers between blockchains.

handlePackage function declaration in BSC main smart contract

This function also contains the onlyRelayer modifier, which means that only a relayer can call this function.

Structure of the transaction validation request

Relayers in bridges process specially formatted data packets coming from blockchain A, extract the necessary parameters from them, and translate them to the network for transmission to blockchain B. To register as a relayer, it is necessary to deposit 100 BNB tokens and configure the device connected to the blockchain in accordance with the configuration file. After registration, the relayer starts parsing the data in the endBlock event table of each network block and selecting from it all the IBCPackage events. 

Structure of the transaction validation request

The value parameter has four attributes separated by “::”:

  • The first attribute is the destination chain name; in this example it is “bsc”.
  • The second attribute is the CrossChainID of destination chain; in this example it is “2”.
  • The third attribute is the channel id; in this example it is “8”.
  • The fourth attribute is the sequence; in this example it is “19”.

After processing this event, the relayer processes the data packet with the transaction sended from blockchain A to blockchain B. The relayer extracts the following parameters from the packet:

Parameter nameSizeValue
prefix1 byte0x00
source chain CrossChainID2 bytesTransaction source blockchain ID
destination chain CrossChainID2 bytesTransaction destination blockchain ID
channelID1 byteIBCPackage event channel ID
sequence8 bytesIBCPackage event sequence number

Next, the relayer sends these parameters, transaction data, the transaction validation sign (prove), and block height to a special RPC request that calls the handlePackage function of the CrossChain contract:

RPC request to handlePackage function

Merkle proof

In the handlePackage function, the validateMerkleProof function of the MerkleProof library is called; using the staticcall method at 0x65, the precompiled iavlMerkleProofValidate contract is called. This contract is a library written in Go that has a number of dependencies (methods) of the Cosmos cross-chain framework that implement the Merkle proof functionality.

MerkleProof library

These dependencies are called in the Run function of the iavlMerkleProofValidate contract in strings 8, 9, and 16:

Run function of iavlMerkleProofValidate precompiled contract

The called op.Proof.ComputeRootHash() method calculates the AVL tree root hash for the tree leaf that contains the transaction being checked. Next, the op.Proof.Verify(root) method compares the hash of the AVL tree root with the one calculated at the previous step. If the compared hashes differ, the op.Proof.Verify(root) method will return an error, and the transaction transfer from blockchain A to blockchain B will be canceled. If the hashes are the same, the op.Proof.VerifyItem(op.key, value) method is called, in which the presence of the transaction data hash in the AVL tree is checked. If the hash is found, the transaction is considered valid and is executed.

Transaction verification vulnerability

The vulnerability of the transaction verification process is related to the way the tree root hash for the transaction is calculated and checked. The ComputeRootHash function calls the pwl.Leaf.Hash() method:

Hash function call in computeRootHash function

The Hash function correctly calculates the root hash in case when the left leaf in the Merkle proof chain for the transaction being checked equals zero, that is, not defined. But if the left leaf is defined, the tree root hash is calculated without taking into account the right leaf. In other words, if the left leaf in the Merkle proof chain for the checked transaction is defined, the value of the tree root hash will not depend on the presence of the right leaf in the proof chain.

Vulnerability in root hash calculation

This flaw allows attackers to write a payload to the right leaf and successfully pass the tree root hash value check, provided that the payload hash is calculated correctly.

Attack scenario

Before starting the attack, criminals deposited 100 BNB to the Relayer Hub contract in order to register as a BSC bridge relayer.

They used a legitimate transaction that was used to transfer 0.05 BNB from the BSC bridge two years ago. They changed the payload by specifying the attacker address as the recipient and changed the amount to 1 mln BNB:

Original transactionAttacker transaction
payload0x00000000000000000000000000000000000
00000000000000000038d7ea4c68000f86da0
424e420000000000000000000000000000000
0000000000000000000000000009400000000
0000000000000000000000000000000087b1a
2bc2ec50000
944e656459ed25bf986eea1196
bc1b00665401645d
94a10123c15a63135fe94
5a54232bae7fac8177056
845fd12999
0x00000000000000000000000000000000000
0000000000000000000000000000000f870a0
424e420000000000000000000000000000000
0000000000000000000000000009400000000
000000000000000000000000000000008ad3c
21bcecceda1000000
94489a8756c18c0b8b24
ec2a2b9ff3d4d447f79bec
94489a8756c18c0
b8b24ec2a2b9ff3d4d447f79bec
846553f100

The payload content is a structure encoded in RLP format: 

RLP encoding view structure

Next, attackers modified the proof variable content by adding the right leaf with the payload hash to the AVL tree, and added an empty internal host to balance the AVL tree:

Original transaction proof structureAttacker transaction proof structure
RangeProof{
   LeftPath: PathToLeaf{
      0:proofInnerNode{
         Height:  1
         Size:    3
         Version: 110217392
                    Left:
0C10F902D266C238A4CA9E26FA9BC36483CD3 EBEE4E263012F5E7F40C22EE4D2
         Right:
      }
      1:proofInnerNode{
         Height:  -1
         Size:    2
         Version: 110217392
              Left:
E4FD47BFFD1C06E67EDAD92B2BF9CA63631978676288A2AA99F95C459436EF63
         Right:
      }
   }
   InnerNodes:
      Leaves:
      proofLeafNode{
         Key:
         0000010038020000000000000002
         ValueHash:
11056C6919F02D966991C10721684A8D1542E44003F9FFB47032C18995D4AC7F
         Version:    110217392
            }









      (rootVerified): true
      (rootHash):
E09159530585455058CF1785F411EA44230F39334E6E0F6A3C54DBF069DF2B62
      (treeEnd): true
}
RangeProof{
   LeftPath: PathToLeaf{
      0:proofInnerNode{
         Height:  1
         Size:    3
         Version: 110217392
         Left:
0C10F902D266C238A4CA9E26FA9BC36483CD3 EBEE4E263012F5E7F40C22EE4D2
         Right:
      }
      1:proofInnerNode{
         Height:  -1
         Size:    2
         Version: 110217392
                 Left:
E4FD47BFFD1C06E67EDAD92B2BF9CA63631978676288A2AA99F95C459436EF63
         Right:
DA657C1FFB86C684EB3E265361EF0FA4F9DFA670B45F9F91C5EB6AD84B21A4D1
      }
   }
   InnerNodes:
      empty-PathToLeaf
      Leaves:
      proofLeafNode{
         Key:
         0000010038020000000000000002
         ValueHash:
11056C6919F02D966991C10721684A8D1542E44003F9FFB47032C18995D4AC7F
         Version:   110217392
      }
      proofLeafNode{
         Key:
         00000100380200000000010DD85C
         ValueHash:
2C3A561458F8527B002B5EC3CAB2D308662798D6245D4588A4E6A80EBDFE30AC
         Version:          1
      }

      (rootVerified): true
            (rootHash):
E09159530585455058CF1785F411EA44230F39334E6E0F6A3C54DBF069DF2B62
      (treeEnd): true
}

Due to the vulnerability, the content of the added right leaf did not affect the root hash, so the fraudulent transaction was successfully verified. After checking the transaction, the CrossChain contract called a function to transfer 1 mln BNB from BSC to the attacker address. Next, the attackers tried to repeat the transaction, but the next 15 attempts were unsuccessful because of the incorrect packageSequence value. On the 16th attempt, however, they managed to find the correct packageSequence value and obtain another 1 mln BNB at their address.

Next, for fear of freezing and blocking of assets in BSC, the attackers began to withdraw money from the bridge. For laundering, they used the Venus Finance DeFi: they issued wrapped vBNB tokens in exchange for BNB, and used vBNB as a collateral to loan BUSD, Binance’s stablecoin. Next, using the Stargate and Anyswap bridges, the attackers converted BUSD to the USDT and USDC stablecoins in several blockchains: Ethereum, Avalanche, Fantom, Polygon, Arbitrum, and Optimism.

Laundering chain of stolen funds

Reaction of Binance and the crypto community

After identifying the attack, Binance suspended and forked the BSC blockchain, preventing the attackers from withdrawing more than $400 million. Following that, Tether, the owner of the USDT stablecoin, blocked the attacker’s USDT address, preventing them from laundering part of the stolen funds. The attacker addresses were blacklisted:

Attacker address added in BSC blacklist

Vulnerability elimination

Initially, the AVL tree verification method assumed that only the right or only the left leaf of the tree could be defined for a transaction to be verified. However, the check for the simultaneous presence of the right and left leaves in the AVL tree check algorithm was initially missing, and the attacker took advantage of it.

Shortly after the hack, a fix was introduced to the AVL proof method of the Cosmos cross-chain framework: 

Changes after vulnerability fix in AVL proof method of the Cosmos framework

In case of simultaneous presence of the left and right leaf of the AVL tree, the transaction verification will be rejected with a corresponding error.

Conclusion

The Binance Smart Chain Token Hub bridge hack is an example of exploiting a vulnerability in a third-party component that the bridge uses to determine whether a transaction can be trusted and executed or rejected. Although the vulnerability was in a third-party component, it caused great financial damage to the BSC bridge. Apparently, before it was fixed, this vulnerability was also present in other DeFi protocols using the AVL proof method of the Cosmos cross-chain framework. However, based on the amount of funds stolen from BSC, we can assume that the attackers were after the jackpot from the very beginning, knowing that after the vulnerability is fixed, they would not get a second chance.