新加坡安全公司Numen发现Aptos公链首个高危0day漏洞|字节

发布日期:2022-10-10 15:49:07

  来源:金色⌊财⊂≅经

 ੫ º1.▩前言

  相对于ethereum的soldity语੝言,move语言最近越来越火,而且由于其自身相对于soldity的强大优势,越来越受到重视。其中move语言被用于很多明星项目,比如Aptos,sui。近期我们的Web3 安全漏洞检测产品发现了一个整数溢出的漏洞。可以导致Ap⇔tos节点崩溃,造成拒绝服务。本文通过对该漏&#25b2;洞的介绍,希望大家对move语言以及其安全性有更多的认识和理解。作为move语言安全性研究的领导者,我们会持续关注move语言的安全性,为move的生态安全做出我们的贡献。 

  2લ.Move语ੋ言的重要概念

  模块和ë脚本&nbs↵p;¥

  Move 有两种不同类型的程序:模块(Modules)和脚本(Scટript)。模块是定义结构类型以及对‏这些类型进行操作的函数的库。结构类型定义了 Move 的全局存储的模式,模块函数定义了更新存储的ࢵ规则。模块本身也存储在全局存储中。脚本是可执行文件的入口点,类似于传统语言中的主函数 main。脚本通常调用已发布模块的函数来更新全局存储。脚本是临时代码片段,不会发布在全局存储中。一个 Move 源文件(或编译单元)可能包含多个模块和脚本。然而,发布模块或执行脚本都是独立的虚拟机(VM)操作。 

  对于熟ৄ悉操作Ù系统的人来说,move的module就类似系统的可执行文件运行的时候加载的动态库模块,而script类似主程序。用户可以通过自己编写script,来ੋ访问全局存储,包括调用module模块的代码。 

  ⓣ全局存&#222e;储 

  Move 程序的目的是读取和写入树形的持久全局存储。程序&#263c;不能访问文件系统 、网络或任何此树以外的&#263b;数据。 

ˆ

  在伪代码¯中,全局存储看起来像: ૨

  从结构上讲,Ó全局存Ο储是一个森林(forest),这个森林由以账户地址(aੇddress)为根的树组成。每个地址可以存储资源(resource)数据和模块(module)代码。如上面的伪代码所示,每个地址(address)最多可以存储一个给定类型的资源值,最多可以存储一个给定名称的模块。  

 ℜ MOVE®虚拟机原理&nbⓨsp;

  movevm 与evm虚拟机都⇓一样þ,需要将源码编译成字节码,然后在虚拟机中执行,下图是整体的流程ࢵ: 

  1.Â将字节码੥通过函数eâxecute_script被加载进来 

  2.执行load_script函数,这ϒ个函数主要用来反序列化字节ε码,并校验字节码是ઍ否合法,如果校验失败,就会返回失败 

  ઐ3.⌊校验成功之后就⌋会开始执行真正的字节码代码 

  4.执行字节&#260e;码,访问或修改ⓔ全局存储的状态Ô,包括资源,modules 

í  注:move语言还有很多特性,这里我们就不一∝一介绍,后续我们≅会从安全性角度继续分析move语言的特性。 

 ⓚ 3.漏洞描&#256e;述ਮ

  本漏洞主要设计验证模块Ü,在讲具体漏洞之前先介绍下验证模块的功能以及£StackUsageVerifierê::verify。 

  验证模Υ√块 ℑ

  通过前面,我们″知道在真正执行字节码代码之前ઍ,会有验证字节码ω的环节,而验证环节有可以细分为好多子过程, 

 ਠ 分ખ别是:÷ 

  该♩漏洞发生在verify环节 CodeUnitVerifier::verify_script(config, script)?;°函数中。可以看这里有许多的verify子流程。&nbsp◯;

  分别是stac‌k安全校验,类型安全校Ù验名,本地变量安全性校验,以及引用安全校验。而漏洞产生&#263e;的地方就在栈安全校验过程中。 

  栈安全Ψ校验(Stack௄UsaⓘgeVerifier::verify) 

  该模块用于验证函数的字节码指令序列中的基本块‘是否以平衡的方式使用。 每个基本块除了❄那些以 Ret(返回给调用者)操作码结尾的,必须确保离开blo⇔ck时候栈高度 与开头时候相同。 此外,对于任何基本块的块,栈高度不得低于开始时的栈高度。 

  循环校⊂验∏所有代码块是否满足以上条件:&nbsp&#25d0;; 

  即循环遍历验ભ证所有基本ä块的合法性ì。 

  漏洞&#25b3;详情&♨nbsβp;

⌈  前面已经介绍过,由于movevm是栈虚拟机,在验证指令合法性的时候,很显然,第一需要确保ਮ指令字Û节码是否正确,第二需要确保栈空间经过一个block代码块调用之后,栈内存合法,即栈操作之后,栈保持平衡。verify_block函数正是用来完成第二个目的的。 

  从verify_block代码中我们可以看到,for循环会循环解析block代码块中的所有指令,然后通过对num_pops, num_pushes加减操作来验证指令块的对栈的影响是否合法,首先通过对stack_size_increment < num_pops来判断栈空间是否合法,如果num_pops大于stack_size_increment就说明字ৄ节码pop的数目大于栈本身的大小,就返回错误,字节码校验失败。然后通过 stack_size_increment -= num_pops; stack_size_incખrement +=ó num_pushes; 这两条指令来修改每个指令执行之后对栈的高度的影响,最后当循环结束之后,stack_size_increment需要等于0,即保持本block内的操作之后,需要保持栈的平衡。 

  看起来这里似乎没什么问题,但是由于这里在执行16行代码的时候,ⓗ没有去判断是Ñ否存在整数溢出,导致可以通过构造超大num_pushes,间接控制stack_size_increment,从而产生整数溢出漏洞。那么如何构造构造这样一个巨大的push数目呢?☜这里首先需要介绍一下move bytecode 文件格式。 

 ࠽ m«ove bytecode 文件格式&nb³sp;

  如同Windows PE文件,或者‰linuµx ELF文件,move的字节码文件以.mv⇑为结尾,文件本身也是有一定的格式的,总的来说move bytecode文件格式如图所示: 

  首先是macgic,值为A11CEB0B,接下来是版本信息,以及table的数目,之后是tables headers,这里可以有很多个tables,table kભinds就是table的类型,总共有0x1ⓚ0种(如图的右边所示),更多详细信息可以去看move语言文档,接下来是table的偏移,以及tablખe的长度。之后就是table的contents了,最后是Specific Data,有两种,对于module来说就是Module Specific Data,对于script类型来说就是Script Specific Data。 

  构造的恶意文件ઐ格式&nbs¡Îp;

  这里我们与aptos交互的时候,是以script来完成的,所以我们构造了下图所示的文件&#25a0;格式,就可以造成stack_size_Ìincrement溢出:&&#256e;nbsp;

  首先☼来解释一¨下这个字节码文件的格式&#25d0;: 

  +0x00-0x03: 是macgic word 0ðxA11CEB0👿εB 

  +0 x04-0x7: 文件格式版ૠ本,这里为版本4&ènbsp;

  +0x8-0xš8:Í 为table count这里为1 ⊗

Κ  +0x9-0x9: 为table kind 这里是 SIGNATURES 类型⇐઼ 

  ࠽+0xa-0xa: 为ta≅ble o઒ffset, 这里为0 

  +0xb-0xb: 为t‌ℑable length,这里为0çx10 

Α  &#263e;+0xc-0x18:为 SIGNATURES Tokenવ数据 

 † 从0x2ⓛ2开始为scrip的mian函数code代码部分&nbμsp;

  通过mov૦e-diÚsassemble∗r工具,我们可以看到指令的反汇编代码如下: 

  其中 0,1,2三条指令对应的代码就是红框‰,绿框,黄ⓘ框的数૦据。 

  LdU64与漏洞本身无关,我们这里就不做过多解释,感兴趣Σ的可∼以自行查看代码。这里重点解释下VecUnpack指令,VecUnpack的作用就是在代码中碰到vecηtor对象的时候,需要将数据全部push到栈上。 

  在构造的这个文件τ中,૜我们构造了两次VecUnpack,其vector的num分别是33152145434763648Ì30,18394158839224997406, 

  当执行函δ数instruction_effectσ的时候,实੥际上执行的是下面第二行代码: 

  执行完instruction_effect函数第一次返回(1,3315214543476364830),此时stack_size_increment为0,num_pops为1,num_pus↔hes为331521ℑ454ਮ3476364830,执行第二次返回(1,18394158839224997406)。当再次执行 stack_size_increment += num_pushes; 

👽

  ઴stack_sÖize_increment已经为0x2e020210021e161d(3315214543476364829), 

  num_pushes为0xff452e02021e161e(18394158839224997406),当两者相加之后,大于u64的最大值,产生了数据截断,stack_size_increm૤ent的值成为了0x12d473012043c2c3b,造成了整数溢出,੦从而造成了aptos节点崩溃,进而导致节点运行停止的严重影响(由于rust语言的安全特性,并不会向Ác/c++那样造成更进一步的代码安全影响)。 

  ૙4.漏洞影响“

  本漏洞由于是发生在movevm 执行模块,所以对于链上节点,只要执行该字节码代码,就会造成DoS攻击,严重的情况下,可以使得aptos网ⓥ络完全停止运¬行,会对其生态造成难以估量的影响,以及对节点的稳定性产生严重∠影响。 

  5.官方修复

  当我们发现这个漏洞之Ζ后,⌋第一时间报告给了官方,官方也很快修复了漏洞:&nbsŒp;

  官方的修复也很简单,就是对sta…ck_size_incਗ਼rement的加Ê减分别做了溢出检测。如果有溢出就直接返回异常。 

24小时滚动播报最新的财经资讯和ૠ视频੆,更多粉丝福利扫描二维码关注(sinaf‘inance)

新浪财ⓒÈ经意见反馈留言板º

All Righ—ts Reserved 新浪Ý公司  版权所有

关于

发表评论

邮箱地址不会被公开。 必填项已用*标注