The Hack
Note: The Security Team at FYEO will continue to investigate the situation and will update this blog and Twitter as new technical information becomes available.
In Binance, there are special precompile contracts from Binance Smart Chain core that are used to verify Merkle proof for cross chain bridges: tmHeaderValidate and iavlMerkleProofValidate. Merkle proofs are used to prove the validity of data being part of a blockchain without storing the whole blockchain. Binance smart chain bridge is an IBC-based cross-chain bridge which realizes these Merkle proofs to verify whether certain events actually happen on the chain. Thus, if an attacker can forge a proof, a fraudulent transfer can become valid and it was actually what happened to the Binance smart chain in the last 24 hours where the hacker was able to steal $570 million worth of Binance’s BNB token.
Timeline:
6:19 PM EDT Oct 6, 2022: An initial announcement was posted to Twitter by BNB Chain saying there would be a temporary pause on the BSC network.
6:35 PM EDT Oct 6, 2022: The network pause turned into a halt.
7:20 PM EDT Oct 6, 2022: BNB Chain said that $7 million in assets had been frozen before it could be transferred but acknowledged that between $70 million and $80 million were stolen from the Binance Smart Chain
2:53 AM EDT Oct 7, 2022: BNB Chain validators updated the node to the last release v1.1.15 (Nano fork) and brought back the chain online.
The Patch
Binance just recently release a new version to hard fork the chain call Nano fork. It seems like the Binance dev team just temporarily patches it by suspending the precompile contract and blocking the attacker’s accounts.
1, The hacker’s accounts are blocked blacklist.go
var NanoBlackList = []common.Address{
common.HexToAddress("0x489A8756C18C0b8B24EC2a2b9FF3D4d447F79BEc"),
common.HexToAddress("0xFd6042Df3D74ce9959922FeC559d7995F3933c55"),
// Test Account
common.HexToAddress("0xdb789Eb5BDb4E559beD199B8b82dED94e1d056C9"),
}
States are updated and applied in the state_transition.go file. Here, the code checks for the sender and ensures that if the message sender is in the Nano Blacklist, the transaction would fail.
if st.evm.ChainConfig().IsNano(st.evm.Context.BlockNumber) {
for _, blackListAddr := range types.NanoBlackList {
if blackListAddr == msg.From() {
return nil, fmt.Errorf("block blacklist account")
}
if msg.To() != nil && *msg.To() == blackListAddr {
return nil, fmt.Errorf("block blacklist account")
}
}
}
2, The Nano hard fork
var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{100}): &tmHeaderValidate{},
common.BytesToAddress([]byte{101}): &iavlMerkleProofValidate{},
}
var PrecompiledContractsIsNano = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{100}): &tmHeaderValidateNano{},
common.BytesToAddress([]byte{101}): &iavlMerkleProofValidateNano{},
}
Old vulnerable cross chain bridge related precompile contracts are disabled and replaced with the two new contracts tmHeaderValidateNano and iavlMerkleProofValidateNano contracts that are suspended.
func (c *tmHeaderValidateNano) Run(input []byte) (result []byte, err error) {
return nil, fmt.Errorf("suspend")
}
func (c *iavlMerkleProofValidateNano) Run(input []byte) (result []byte, err error) {
return nil, fmt.Errorf("suspend")
}
Conclusion
This is a vulnerability from layer-1 and this again reminds us the importance of blockchain security review and audit to both core layer as well as smart contract layer. At FYEO, our code audit team is comprised of some of the best DeFi logic experts in the world having worked on 100s of projects and protocols. The FYEO team continues its work to identify and evolve its products to solve the biggest security problems facing Web3 ecosystems. If you are interested in code review performed by world-class experts, submit a request here.
Comments