草庐IT

Move语言安全性分析及合约审计要点之逻辑校验漏洞

SharkTeam 2023-03-28 原文

1、逻辑校验漏洞

智能合约开发的业务相关逻辑设计复杂,涉及的经济学计算和参数较多,不同项目和协议之间可组合性极其丰富,很难预测,非常容易出现安全漏洞。

在Solidity智能合约中,我们总结了4种类型的逻辑校验漏洞:

(1)未校验返回值

(2)未校验相关计算数据公式

(3)未校验函数参数

(4)未规范使用require校验

同样地,我们将从这4个方面分析Move合约中是否存在这些逻辑检验漏洞以及其可能性和危害。

1.1 未校验返回值

不检查消息调用的返回值,即使被调用的函数返回一个异常值,执行逻辑仍然会继续进行,只是该函数的调用并没有实现正确的逻辑,这会导致整个交易得不到正确的结果,甚至会威胁到数字资产的安全性。

比如,Solidity合约中的call函数,functionCallWithValue函数如下:

代码中调用了call函数,如果call函数执行发生意外,比如转账失败,则返回值success为false。如果没有验证该返回值,即使success为false,交易仍然会正常执行。只是交易中的这笔转账没有成功。这里通过require对success进行了验证,如果是false,交易就会回滚(revert)。

call函数是Solidity动态函数调用的一个关键函数,是Solidity语言层面的一个容易因为返回值而产生漏洞的典型代表。除了call函数之外,在业务层面,Solidity合约也经常使用返回值来判断函数是否执行成功,比如ERC20合约中的函数:

对于这类函数,在实际应用的时候一般需要对返回值进行校验,否则会产生漏洞,甚至会威胁到数字资产的安全性。

此外,根据实际的业务逻辑,函数会返回一些业务需要的数据,这些数据也需要根据业务进行验证,进一步保证函数调用没有发生意外,包括但不限于返回值的类型、长度、范围等。比如上面的functionCallWithValue函数中,调动了verifyCallResultFromTarget函数对返回值进行校验。其不仅对返回值success进行了检查,还对retrundata的长度进行了校验和处理。

在Move合约中,从语言层面来讲,由于其静态调用的特性,不存在类似于Solidity中的call函数需要校验返回值的情况,即使有需要校验函数是否执行正确,一般会在spec模块使用规范语言在Move Prover中进行校验,校验失败则交易会中止。

从业务层面来讲,Move合约中的spec模块同样可以校验函数对全局数据的修改。此外,还可以在合约中编写单元测试函数对函数直接进行单元测试,来保证函数执行的正确性。因此,一般不会将表示函数执行是否成功的布尔变量作为返回值。因此,Move函数的返回值多是实际的业务数据,是否需要校验,则需要根据实际业务需求来确定,比如需要根据返回值的不同,进入不同的函数逻辑分支,则需要对返回值进行判定和检验,比如DEX中的流动性函数:

X与Y的排序不同,需要访问的balance也是不同的,还需要校验order!=0。

总的来说,Move语言静态调用特性、spec模块以及单元测试等极大地提高了函数的安全性,这一点Solidity要好很多。但也不排除函数会因为没有校验返回值而产生漏洞的情况。因此,开发人员更需要对业务和实现逻辑熟悉,开发的时候需要谨慎而行。

1.2 未校验相关计算数据

相关业务在合约实现过程中,考虑到情况不够全面没有正确校验相应的业务经济学公式和计算数据,导致合约对于特殊的计算数据容错性差。比如:

(1)XCarnival安全事件

事件发生在2022年6月24日,NFT借贷协议XCarnival遭受到黑客攻击,损失大约380万美元。

根本原因是controller合约borrowAllowed函数调用的orderAllowed函数对数据结构order的校验不完整,仅仅是校验了订单存在、地址正确并且没有被清算,并没有校验订单中的NFT是否被提取,即使订单中的NFT已经被提取了,order的校验仍然可以通过。

(2)Fortress Loans安全事件

事件发生在2022年5月9日,Fortress Loans遭到黑客攻击,损失了1048.1 ETH以及40万DAI。

根本原因是submit函数虽然校验了signer的数量,但却没有对signer本身和计算的数据power进行校验。

这使得攻击者可以调用submit函数修改状态变量fcds,最终修改了价格预言机中的价格。

最终,攻击者利用该漏洞窃取了1048.1 ETH以及40万DAI。

类似的安全事件还有不少,它们都是因为在函数内部缺少对经济模型建立的数据结构或者计算的数据缺少校验引起的漏洞。这类漏洞是由于项目设计与开发并没有考虑到全部的情况造成的,其严重等级不一,严重的甚至会给项目带来极大的经济损失,就像上面的安全事件。

在Move合约实现各类项目时,同样难以保证不会出现这类问题,尤其是新型项目。希望发生在Solidity智能合约中的这些安全事件能够给Move开发者一些警示,在开发过程中,尽可能地避免安全漏洞。

1.3 未校验函数参数

函数接收参数时,它不会自动地验证输入的数据属性是否具有安全性和正确性。因此,函数在实现的时候需要根据业务需要对参数进行校验,若缺少校验后者校验不符合业务需求,则会产生漏洞,甚至会威胁到数字资产的安全性。

以Superfluid.Finance安全事件为例。事件发生在2022年2月8日,以太坊上的DeFi协议Superfluid遭遇黑客攻击,损失超1300万美元。

根本原因在于,Superfluid合约存在严重的逻辑漏洞,callAgreement函数缺少对参数的校验,使得攻击者将合约构造的ctx数据替换为自定义ctx数据,这给攻击者发起攻击提供了机会。

在Move合约开发中更加需要对参数进行校验。在Move中,函数的参数不仅仅是业务需求的数据,还包括了权限需要的数据,比如signer。Move没有类似Solidity中的msg.sender这种全局变量,Move中对权限的鉴定是通过参数实现的。比如下面的函数:

该函数中的account参数是代币铸造的发起账户,它必须铸币的权限,即MintCapStore,类似于Solidity中的msg.sender必须是owner。如果缺失了这部分校验,该代币就是任何账户都可以铸造的了。

此外,Move生态中的项目类型跟Solidity生态相同,只是实现的语言不同。因此,Solidity合约中存在的业务逻辑上的漏洞在Move合约中有很大的可能性依然存在。因此,Move开发者在开发项目时要注意这些在Solidity合约中已经出现过的漏洞。

1.4 未规范使用require

Solidity中的require旨在验证函数的外部输入,包括调用者输入的参数、函数的返回值、函数执行前后状态变化等。如果不能规范使用require,合约可能会产生漏洞,甚至威胁到数字资产的安全性,比如XDXSwap安全事件。

事件发生在2021年7月2日,火币生态链(Heco)上DeFi项目XDXSwap受到闪电贷攻击,损失约400万美金。

根本原因就是闪电贷的功能实现合约,存在借出不还的严重漏洞,造成巨额损失,是项目方fork Uniswap合约代码并修改时引入的严重漏洞,即缺少K值校验的require语句。最根本的原因还是业务的不熟悉,导致实现存在漏洞。

在Move合约中, assert语句和spec模块完成require类似的功能。同样,很多Solidity生态的项目,包括DEX、借贷、农场等类型的项目在未来都将出现在Move的生态中。Move与Solidity原理以及机制是不同的,但项目的业务时相同的。鉴于Solidity生态项目踩坑无数,安全事件层出不穷,Move虽然安全性高,但是在实现各类项目时仍然要谨慎小心,尽量避免出现同类型的漏洞,希望同一个坑不要再踩一次了。

2、总结

当下Move仍处于发展阶段,Move生态离成熟尚一定距离,开发者较少,开发者经验欠缺,真正能够熟练开发Move合约的不多,因此更容易出现业务层面的一些漏洞。这需要Move合约在设计和开发过程中对Move语言特性以及业务都要熟悉,才可能少出现业务漏洞。

另外,Solidity已经实现了大量的业务类型,比如去中心化交易所、去中心化借贷、收益聚合、杠杆借贷、杠杆挖矿、闪电贷、跨链交易等。这些典型的业务场景需要在Move生态足逐一实现,而且需要结合Move与Solidity的差异进行重新设计实现方案。在这个过程中,就比较容易出现一下漏洞,就像Solidity早期经历了很多次攻击和大量资产的损失才逐步走向成熟。Move虽然是一个安全性较高的语言,但谁也无法保证没有漏洞,我们希望可以借鉴Solidity的发展过程,让Move生态的发展少走一些弯路,少一些损失,更快更稳地走向成熟。

有关Move语言安全性分析及合约审计要点之逻辑校验漏洞的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  3. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  4. ruby - 如何安全地删除文件? - 2

    在Ruby中是否有Gem或安全删除文件的方法?我想避免系统上可能不存在的外部程序。“安全删除”指的是覆盖文件内容。 最佳答案 如果您使用的是*nix,一个很好的方法是使用exec/open3/open4调用shred:`shred-fxuz#{filename}`http://www.gnu.org/s/coreutils/manual/html_node/shred-invocation.html检查这个类似的帖子:Writingafileshredderinpythonorruby?

  5. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  6. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  7. ruby - rbenv 安装 ruby​​ 校验和不匹配 osx - 2

    我已经在mountainlion上成功安装了rbenv和ruby​​build。运行rbenvinstall1.9.3-p392结束于:校验和不匹配:ruby-1.9.3-p392.tar.gz(文件已损坏)预期f689a7b61379f83cbbed3c7077d83859,得到1cfc2ff433dbe80f8ff1a9dba2fd5636它正在下载的文件看起来没问题,如果我使用curl手动下载文件,我会得到同样不正确的校验和。有没有人遇到过这个?他们是如何解决的? 最佳答案 tl:博士;使用浏览器从http://ftp.rub

  8. ruby - 用 YAML.load 解析 json 安全吗? - 2

    我正在使用ruby2.1.0我有一个json文件。例如:test.json{"item":[{"apple":1},{"banana":2}]}用YAML.load加载这个文件安全吗?YAML.load(File.read('test.json'))我正在尝试加载一个json或yaml格式的文件。 最佳答案 YAML可以加载JSONYAML.load('{"something":"test","other":4}')=>{"something"=>"test","other"=>4}JSON将无法加载YAML。JSON.load("

  9. ruby-on-rails - 安全地显示使用回形针 gem 上传的图像 - 2

    默认情况下:回形针gem将所有附件存储在公共(public)目录中。出于安全原因,我不想将附件存储在公共(public)目录中,所以我将它们保存在应用程序根目录的uploads目录中:classPost我没有指定url选项,因为我不希望每个图像附件都有一个url。如果指定了url:那么拥有该url的任何人都可以访问该图像。这是不安全的。在user#show页面中:我想实际显示图像。如果我使用所有回形针默认设置,那么我可以这样做,因为图像将在公共(public)目录中并且图像将具有一个url:Someimage:看来,如果我将图像附件保存在公共(public)目录之外并且不指定url(同

  10. ruby - RVM pkg 安装校验和错误 - 2

    root@li417-132:~#rvmpkginstallzlibFetchingzlib-1.2.7.tar.gzto/usr/local/rvm/archivesThereisnochecksumfor'http://prdownloads.sourceforge.net/libpng/zlib-1.2.7.tar.gz'or'zlib-1.2.7.tar.gz',it'snotpossibletovalidateit.Ifyouwishtocontinuewithunverifieddownloadadd'--verify-downloads1'afterthecommand.

随机推荐