freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Move语言安全性分析及合约审计要点之函数恶意初始化
2022-11-24 18:08:48
所属地 江苏省

SharkTeam在之前的“十大智能合约安全威胁”系列课程中,根据历史发生的智能合约安全事件,总结分析了在智能合约领域中出现较多、危害最大的前10大漏洞。这些漏洞之前通常出现在Solidity智能合约中,那么对于Move智能合约来说,会不会存在相同的危害呢?

SharkTeam【Move语言安全性分析及合约审计要点】系列课程将带您逐步深入,内容包括权限漏洞、重入漏洞、逻辑校验漏洞、函数恶意初始化、回退攻击、操纵预言机、合约升级漏洞、三明治攻击、重放攻击、提案攻击。本章内容【函数恶意初始化】。

1.Solidity合约初始化

在Solidity智能合约中,实现合约的初始化有两种方式:

(1)构造函数方式,即使用constructor实现对合约的初始化。

构造函数会在合约部署后自动执行,不需要额外的单独调用,节省一笔交易的费用。

构造函数和合约部署绑定,只能对绑定部署的合约进行初始化,对于单个合约的情况,有极大的优势;对于多个合约联合部署和初始化,构造函数的方式就缺乏灵活性,甚至,在一些特殊场景中会产生错误或漏洞,比如基于代理的可升级合约。在基于代理的可升级合约中,逻辑合约使用构造函数初始化状态变量,只能初始化逻辑合约中的状态变量,对于代理合约中的全局状态变量无效,因为通过代理合约访问的状态变量实际是代理合约中的状态变量。

(2)自定义初始化函数方式,即自定义initialize函数对合约进行初始化。

使用自定义初始化函数initialize实现合约初始化,需要在合约部署完成后单独发起一笔交易来调用initialize函数。初始化函数是对全局状态的初次设置,必须只能由指定账户调用执行,并且执行初始化一次。但initialize函数跟普通函数并无本质区别,并不能像constructor一样只能执行一次,并且在部署合约时执行。因此,initialize函数需要一些额外的限制才能保证其安全性。如果initialize函数可以多次调用,则可能存在漏洞,甚至威胁到数字资产的安全性,比如Punk Protocol安全事件。

2.Punk Protocol安全事件

2021年8月10日,去中心化年金协议 Punk Protocol遭遇黑客攻击,造成 890 万美元损失,后来团队又找回了 495 万美元。攻击原因在于投资策略中存在严重漏洞, CompoundModel 代码中缺少初始化函数的修饰符的问题,可以被重复初始化。

1669284044_637f40cc88a8ad64934b7.png!small

3.Move合约初始化

Move生态的项目仍然是Solidity生态中的那些类型,包括代币、NFT、DEX等,经济模型相同,只是实现的语言是Move。因此,Move合约同样需要初始化函数,初始化合约全局的变量,尤其是资源。Move合约没有构造函数,而是需要自定义初始化函数,类似于Solidity中自定义初始化函数。

从安全的角度考虑,Solidity合约初始化函数需要有调用者和调用次数的限制。Move合约初始化同样需要这些限制,比如新建一种AToken:

1669284052_637f40d4164455ba50855.png!small

其中的intialize是初始化函数,需要在合约部署后调用,因为该函数的可见性声明为public(script),因此可以直接用命令行调用。该初始化函数中先后调用了Token中的register_token函数以及Account中的deposit_to_self函数。

(1)Token::register_token函数

1669284058_637f40daf1b40b0b88285.png!small

该函数实现了AToken的注册,其精度为9,并且将AToken的铸造和销毁权限给与account。

注意到代码标注的assert语句,该断言是对发起调用的账户account的校验,必须是AToken的地址,即合约部署者的地址。这类似于Solidity中初始化函数的调用者权限校验(如onlyOwner检查)。如果没有这条语句,任意一个账户都可以对AToken进行初始化从而获得铸造和销毁的权限,这会造成AToken的最高权限泄露,该代币将会变得一文不值。

另一方面,保证初始化函数仅能被调用一次,Move语言中资源的唯一性就可以保证这一点。

1669284065_637f40e1884c634301330.png!small

在register_token函数的规范中,有对铸造能力、销毁能力以及代币信息3中资源的检查,即保证在函数执行前这3种资源不存在,在函数执行完成后这3种才会存在。资源的唯一性保证了该函数只能被执行一次。这一点比Solidity要安全,毕竟Solidity合约需要额外的限制,如使用Openzeppelin中的Initializer。

(2)Account::deposit_to_self函数

1669284071_637f40e7af394101b04c8.png!small

该函数将AToken添加到account账户的Balance中,其额度为0。在规范中定义了必要条件,包括account账户的Balance中没有AToken。这要保证了该函数只能被执行一次。

这里并没有对account的权限做校验,因为任何账户都应该被允许将AToken添加到其Balance中。

4.总结

调用以上两个函数完成了AToken的初始化。从整个初始化过程中,我们发现,Move的初始化函数同样要求:

(1)只能指定的有权限的账户才能调用,比如这里的代币地址,同时也是代币合约部署地址

(2)初始化函数只能被调用一次。Move中的初始化函数一般是对资源的初始化构造,资源的唯一性以及Move规范的使用可以比Solidity更加方便安全地保证做到这一点。这更加突显了Move语言的安全特性。

5.补充

合约部署以及初始化过程,如下:

(1)使用mpm release命令编译模块

1669284078_637f40eeb2ae0f09d9424.png!small

(2)解锁Admin账户,然后使用dev deploy命令部署合约

1669284083_637f40f367730c914ade3.png!small

(3)使用account execute-function命令调用initialize函数进行合约初始化

1669284088_637f40f8907568479ba88.png!small

(4)初始化后的Admin账户详情

1669284093_637f40fde1a2cc81e3cca.png!small

在balances中增加了AToken,其余额为0。

# 智能合约 # 智能合约攻击 # 智能合约代码审计 # 智能合约审计 # 智能合约安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者