Author: Qin Xiaofeng
This morning, Beijing time, the BNB Chian cross-chain bridge BSC Token Hub was attacked. Hackers used the cross-chain bridge vulnerability to obtain a total of 2 million BNB, worth about $566 million. (Note: BSC Token Hub is a cross-chain bridge between BNB Beacon Chain (BEP2) and BNB Chain (BEP20 or BSC).)
As soon as the news came out, the price of BNB fell by nearly 5% within 2 hours to a low of $278.7, and is now quoted at $284, down 4.24% in 24 hours.
According to BNB Chain, preliminary estimates of funds withdrawn from BSC range from $100 million to $110 million. Moreover, Tether also blacklisted the hacker address for the first time. "Thanks to the community and our internal and external security partners, an estimated $7 million has been frozen."
Binance founder CZ posted on social media that at present, Binance has asked all validators to suspend the BSC network, and users’ funds are safe. He apologizes for the inconvenience caused to users and will provide further updates accordingly.
In response to the specific attack method, Paradigm researcher samczsun posted on social media that the data on the chain and related codes show that there is a bug in the verification method of the BSC cross-chain bridge, which may allow the attacker to forge any message; in this attack, the attack The attacker's forged information passed the verification of the BSC cross-chain bridge, so that the cross-chain bridge sent 2 million BNB to the attacker's address.
The samczsun analysis article is as follows:
Five hours ago, attackers stole 2 million BNB (~$566 million) from Binance Bridge. I've been working closely with multiple parties since then to uncover how this all happened.
It started when @zachxbt suddenly sent me the attacker's address. When I clicked in, I saw an account worth hundreds of millions of dollars; either a project rug ran away, or a massive hack was going on.
At first, I thought @VenusProtocol was hacked again. However, it soon became clear that the attackers "really" deposited over $200 million into Venus. That's when I needed to figure out where those funds came from.
The answer is that the attackers somehow convinced the Binance cross-chain bridge to send them (the hackers) 1,000,000 BNB directly, twice.
Either Binance rolled out the biggest “gift pack” ever for Web3, or the attackers discovered a serious vulnerability. I start by comparing the attacker's transaction with a legitimate withdrawal. The first thing I noticed is that the height used by the attacker is always the same - 110217401, while the height used by legitimate withdrawals is much higher, such as 270822321.
I also noticed that the attacker's proof is significantly shorter than the legal withdrawal proof. These two facts convince me that the attacker has found a way to forge the proof of this particular block (110217401). Now, I have to figure out how these proofs work.
On Binance, there is a special precompiled contract for validating IAVL trees. Don't worry if you don't know anything about IAVL trees, because I don't understand 95% of it. Fortunately, all you and I need is the remaining 5%.
Basically, when you validate an IAVL tree, you specify a list of "actions". Binance cross-chain bridge usually requires two operations: "iavl:v" operation and "multistore" operation. Here is their implementation: https://github.com/bnb-chain/bsc/blob/46d185b4cfed54436f526b24c47b15ed58a5e1bb/core/vm/lightclient/multistoreproof.go#L106-L125
In order to forge the proof, we need both operations to succeed, and we need the last operation (multistore) to return a fixed value (hash of the specified block: 110217401).
By looking at the implementation, we can see that manipulating the root hash is impossible, or at least very difficult. This means we need our input value equal to one of the commit ids.
The input value of the "multistore" operation is the output value of the "iavl:v" operation. This means we want to somehow control the root variable here, while still passing value validation.
(12) So how to calculate the root hash? It happens in a function called COMPUTEHASH. At a very high level, it recursively traverses every path and leaf node and does a lot of hashing.
Actually the implementation details don't matter, what matters is that because of the way hash functions work, we can basically say with certainty that any (path, nleaf) pair will yield a unique hash. If we want to falsify evidence, these have to stay the same.
Looking at how the proof is laid out in a legitimate transaction, we see that it has a long path with no internal nodes, just a leaf node that contains the hash of our malicious payload! If we cannot modify this leaf node, then we need to add a new leaf node.
Of course, if we add a new leaf node, we also need to add a new inner node to match.
Now we just have to face the last hurdle. How do we actually make COMPUTEHASH return the root hash we want? Well, note that eventually we will need a path containing a non-zero right hash. When we find a match, we assert that it matches the intermediate root hash.
Let's test the code a little bit so we can figure out what hash we need, then all that's left is to put it all together, we'll take the legal proof and modify it so that:
1) We add a new leaf node for the fake payload;
2) We add a blank internal node to satisfy the prover;
3) We adjust our leaf nodes to exit early with the correct root hash
It's worth noting that this is not the exact method used by attackers. Their proof path is much shorter and I'm not sure how exactly they were generated. However, the rest of the exploit is the same and I believe there is value in showing how to build it from scratch.
In conclusion, there is a bug in the way Binance cross-chain bridge verifies proofs that could allow an attacker to forge arbitrary messages. Fortunately, the attacker here only forged two messages, but the damage could have been much more severe.