freeBuf
主站

分类

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

特色

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

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

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

FreeBuf+小程序

FreeBuf+小程序

Move语言安全性分析及合约审计要点之合约升级漏洞
2023-01-04 14:53:20
所属地 江苏省

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

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

1672814698_63b5206a656dc92975db6.png!small?1672814699735

1.合约升级简述

区块链具有防篡改的特性,即保存在区块链上的数据是不能被更改的。智能合约本质上是保存在区块链上的可执行代码数据。智能合约一旦部署到区块链中,成为区块链上的数据,则具有不可篡改的特性,即使智能合约中有bug需要修复或者业务逻辑变更,也不能在原有的合约上直接修改然后重新部署。这也是智能合约与传统应用程序最大的不同之一。因此,智能合约并不能像其他传统应用程序一般进行迭代升级,不同的区块链又有不同的升级模式。

在以太坊中,智能合约有多重升级模式,其中最主要的就是代理升级模式,又包含继承存储模式、非结构化存储模式、永久存储模式等。这些升级模式的共同点就是使用了代理合约和逻辑合约来实现存储数据与业务逻辑的分离模式。通过代理调用逻辑合约中的函数访问分离的存储数据。

对于代理升级模式,在合约升级的时候要避免出现存储数据的冲突与覆盖,即新的数据存储结构不能与原来的数据存储结构冲突,更不能覆盖原来的数据,否则,历史数据就会丢失。这也是合约升级过程中可能存在的最大漏洞。

2.合约升级漏洞安全事件

2.1 Uranium Finance安全事件

2021年4月28日,币安智能链上区块链项目 Uranium Finance在流动性迁移过程中被攻击,涉及资金为 5000 万美元,攻击合约地址:0x2b528a28451e9853F51616f3B0f6D82Af8bEA6Ae。

分析发现,Uranium 项目合约中的漏洞出现在 UraniumPair.sol 合约中的 swap 函数中,这个漏洞会导致任何人可以随意的转出合约中的数字资产,而只需要付出一点点的代价。可以看到 swap 中,最后是一个10^8和一个10^6的比较,这是一个几乎是恒等的判断,这就意味着只要按照一定的套路不断的执行 swap 函数,就可以清空这个合约中所有的数字资产。

1672814713_63b52079a84bf27b5e25d.png!small?1672814714817

我们看到UniswapV2Pair.sol的合约中的写法是相同的,但是它是两个10^6的比较。

1672814721_63b5208101c8a8fd177e6.png!small?1672814721922

所以,造成这次事件的原因应该是项目方更新升级这个合约的时候,忘记了将后面的1000^2改为10000^2了。

2.2 Audius安全事件

2022年7月24日,黑客利用合约升级漏洞从音乐流媒体协议Audius转移了1800万枚AUDIO代币。

通过分析整个攻击过程,我们发现攻击者之所以能够攻击成功,根本原因在于通过代理合约多次调用初始化函数,而初始化函数本应该只能调用一次的。以Governance合约中的初始化函数为例,其代码如下:

1672814740_63b5209491d04fd25191d.png!small?1672814742620

这里使用了Openzeppelin里面的初始化器initializer。实际情况是initializer没有起到任何作用,原因在于代理合约中的delegatecall。实现合约中定义的两个bool类型的状态变量initialized和intializing分别占用了存储插槽slot0中的前16个字节。第1个8字节为initialized,第2个8字节为initializing。

由于代理合约本身定义了一个地址类型的状态变量proxyAdmin,其值为0x80ab62886eacfebca74511823d4699eb88fd097e,同样占用了存储插槽slot0。

1672814750_63b5209e6834bc663ab71.png!small?1672814750985

于是,实现合约中的initialized和intializing与代理合约中的proxyAdmin同时占用了存储插槽slot0,从而引起了存储冲突。插槽0中的数据分布如下:

1672814758_63b520a6c0a433d447c5c.png!small?1672814759379

初始化函数执行到initializer时,从存储插槽slot0的第1个8字节读取initialized,其值为0,即false;从存储插槽slot0的第2个8字节读取initializing,其值为0x80ab6288 > 0,即true。因此,初始化器initializer完全没有任何作用。

1672814778_63b520ba3ddf949fb7ef3.png!small?1672814780197

3.Move合约升级

Move生态具有多样性,不同的Move生态公链对合约升级的支持以及升级模式也是不同的。比如SUI目前并不支持合约升级。而Starcoin和Aptos则支持合约升级,但升级策略不同。

3.1 Starcoin合约升级

Starcoin是支持合约升级的,而且比Solidity中的代理合约升级更加规范便捷,这是因为Starcoin在设计之初就考虑到了合约升级的情况,并做出了不少探索:

(1)Starcoin的账号模型设计上支持合约升级;

(2)Starcoin的标准库Stdlib内置了多种合约升级的策略(其中也包括禁止升级的策略),供用户自由选择;

(3)Starcoin的标准库Stdlib包含了完备的DAO链上治理功能,能够很方便地跟合约升级策略组合在一起,通过DAO来约束合约升级;

(4)支持合约升级的账号模型。Starcoin有一个ModuleId的数据结构,存储了账号的address和Identifier(模块名称),然后对ModuleId进行hash计算(即ModuleId hash),并作为唯一索引,映射到真正的合约代码。所以在加载代码的时候,需要使用ModuleId hash查找代码。Starcoin的合约以及其他资源(Token、NFT …)存储在账户地址,所以在调用合约时需要通过拥有者地址+模块的方式来找到合约。如果合约升级,不会影响调用合约的地址和模块名称。

(5)Stdlib合约升级策略。Starcoin支持4种合约升级策略,将选择权留给用户:

1)STRATEGY_ARBITRARY:随便更新

2)STRATEGY_TWO_PHASE:两阶段更新TwoPhaseUpgrade

3)STRATEGY_NEW_MODULE:只能新增Module,不能修改Module

4)STRATEGY_FREEZE:冻结,不允许更新合约

这4中升级策略限制越来越严格,只允许从低的策略往高的策略设置,不允许反过来。默认的合约升级策略是STRATEGY_ARBITRARY。

(6)DAO模式的合约升级方案。Starcoin合约升级方案采用DAO去中心化的管理,社区可通过投票操作来决定合约升级计划的部署等。代码提交是采用两阶段提交:先提交升级计划,再提交更新代码。整个升级流程分为七个阶段:

1)PENDING:在修改代码后向DAO提交一个合约升级的提案交易,整个流程进入PENDING状态。设置一段时间使社区对该项议题讨论和了解后进入下一阶段。

2)ACTIVE:在PENDING阶段结束后,进入ACTIVE阶段,在这个阶段需要社区的人员进行投票,在到达设置的规定时间后转为下一阶段。

3)AGREED:在ACTIVE阶段到达规定时间后,流程进入到AGREED阶段,在这个阶段中会对投票结果进行统计,如果超过预定占比,则视为升级计划被DAO社区允许,在发起公示后,可以进行下一阶段。

4)QUEUED:在AGREED阶段的发起公示后,流程进入公示期,这个阶段主要是展示发起人和提案的信息等,当公示期过去之后进入下一阶段。

5)EXECTABLE:在QUEUED阶段的公示期结束后,流程进入到可以升级合约的Two-phase(两阶段提交)的第一个阶段,提交合约代码升级计划,然后即进入下一阶段。

6)ETRACTED:在EXECTABLE阶段的提交合约升级计划后,流程进入到升级合约的Two-phase(两阶段提交)的第二个阶段,在此阶段可以提交修复或升级合约的代码,然后即可进入下一阶段。

7)Upgrade complete:在ETRACTED阶段的代码提交后,待交易确认,整个合约升级流程结束,在此之后就可以使用新的合约了。

采用DAO模式来管理合约升级时一种安全的方式。Starcoin合约升级并不一定就必须采用DAO来管理,也可以直接由合约所有者或开发者自行管理升级。

3.2 Aptos合约升级

Aptos中的Move合约是支持升级的,即合约所有者或开发人员可以在同一个账户地址发布新的合约代码来替代旧的合约代码,Aptos会自动接受最新版本的合约代码并与之交互。

(1)合约升级策略

Aptos支持2种升级策略:

1)兼容策略compatible:升级的合约要保持存储和API的向后兼容性:

存储方面,新的合约代码必须兼容所有旧的合约结构(尤其是资源),这确保新代码能够正确解释现有的存储状态。新合约可以添加新的结构声明。

公共API方面,新合约中公共函数必须兼容旧合约中的公共函数,即具有与旧合约中的公共函数相同的签名,此外还可以添加新的函数,包括公共函数和入口(entry)函数。

2)不变策略immutable:永远不允许更新合约,即不支持合约升级。

(2)合约升级方式

要升级已部署的Move合约,只需在之前部署的同一地址重新部署新的合约代码。当然,也可以结合DAO来管理合约升级,这样会让合约升级更加安全。

4.合约升级安全性分析

(1)不同的链有不同的合约升级策略和方案,需要根据链实际情况,选取最契合链和项目的升级方案。

(2)升级策略的选择由用户自己决定,要充分考虑策略级别。比如Aptos中每一个策略都是账户级别的,即同一个账户下的所有模块共享同一种升级策略,不可以为每一个模块设置独立的策略。

(3)考虑兼容性,新的合约中,模块、资源、公共API等对旧的合约要有一定兼容性,否则容易发生意外,甚至在合约升级成功后影响到项目的业务功能。

(4)DAO升级方案中要考虑DAO的安全性,避免被高权限账户操作;若是合约所有者或开发者独自管理合约升级,则需要考虑中心化的风险。

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