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 代码中缺少初始化函数的修饰符的问题,可以被重复初始化。
3.Move合约初始化
Move生态的项目仍然是Solidity生态中的那些类型,包括代币、NFT、DEX等,经济模型相同,只是实现的语言是Move。因此,Move合约同样需要初始化函数,初始化合约全局的变量,尤其是资源。Move合约没有构造函数,而是需要自定义初始化函数,类似于Solidity中自定义初始化函数。
从安全的角度考虑,Solidity合约初始化函数需要有调用者和调用次数的限制。Move合约初始化同样需要这些限制,比如新建一种AToken:
其中的intialize是初始化函数,需要在合约部署后调用,因为该函数的可见性声明为public(script),因此可以直接用命令行调用。该初始化函数中先后调用了Token中的register_token函数以及Account中的deposit_to_self函数。
(1)Token::register_token函数
该函数实现了AToken的注册,其精度为9,并且将AToken的铸造和销毁权限给与account。
注意到代码标注的assert语句,该断言是对发起调用的账户account的校验,必须是AToken的地址,即合约部署者的地址。这类似于Solidity中初始化函数的调用者权限校验(如onlyOwner检查)。如果没有这条语句,任意一个账户都可以对AToken进行初始化从而获得铸造和销毁的权限,这会造成AToken的最高权限泄露,该代币将会变得一文不值。
另一方面,保证初始化函数仅能被调用一次,Move语言中资源的唯一性就可以保证这一点。
在register_token函数的规范中,有对铸造能力、销毁能力以及代币信息3中资源的检查,即保证在函数执行前这3种资源不存在,在函数执行完成后这3种才会存在。资源的唯一性保证了该函数只能被执行一次。这一点比Solidity要安全,毕竟Solidity合约需要额外的限制,如使用Openzeppelin中的Initializer。
(2)Account::deposit_to_self函数
该函数将AToken添加到account账户的Balance中,其额度为0。在规范中定义了必要条件,包括account账户的Balance中没有AToken。这要保证了该函数只能被执行一次。
这里并没有对account的权限做校验,因为任何账户都应该被允许将AToken添加到其Balance中。
4.总结
调用以上两个函数完成了AToken的初始化。从整个初始化过程中,我们发现,Move的初始化函数同样要求:
(1)只能指定的有权限的账户才能调用,比如这里的代币地址,同时也是代币合约部署地址
(2)初始化函数只能被调用一次。Move中的初始化函数一般是对资源的初始化构造,资源的唯一性以及Move规范的使用可以比Solidity更加方便安全地保证做到这一点。这更加突显了Move语言的安全特性。
5.补充
合约部署以及初始化过程,如下:
(1)使用mpmrelease命令编译模块
(2)解锁Admin账户,然后使用dev deploy命令部署合约
(3)使用account execute-function命令调用initialize函数进行合约初始化
(4)初始化后的Admin账户详情
在balances中增加了AToken,其余额为0。
About Us
SharkTeam的愿景是全面保护Web3世界的安全。团队由来自世界各地的经验丰富的安全专业人士和高级研究人员组成,精通区块链和智能合约的底层理论,提供包括智能合约审计、链上分析、应急响应等服务。已与区块链生态系统各个领域的关键参与者,如Polkadot、Moonbeam、polygon、OKC、Huobi Global、imToken、ChainIDE等建立长期合作关系。
Twitter:https://twitter.com/sharkteamorg
Discord:https://discord.gg/jGH9xXCjDZ
Telegram:https://t.me/sharkteamorg
更多区块链安全咨询与分析,点击下方链接查看
D查查|链上风险核查https://m.chainaegis.com