想要解决这个问题,就可以用代理模式。
代理模式实现了合约的可升级性,并且不会改变合约的部署地址,这也是目前最普遍的合约升级模式。
代理模式是一个可升级的合约系统,包括代理合约和逻辑实现合约。
代理合约处理用户交互和数据及合约状态存储。用户对代理合约的调用会通过delegatecall()执行来自逻辑合约的代码,从而改变代理合约的状态。升级则是通过更新在代理合约预定存储槽里记录的逻辑合约地址来实现。
较为常规的三种代理模式分别是透明代理、UUPS代理和Beacon代理。
升级后,攻击者可以销毁用户的PAID,并为自己铸造了一批PAID,随后再将其出售。代码本身并不存在安全漏洞,而是攻击者从管理员那里获取了升级合约的私钥。
然而,从逻辑合约的角度来看,逻辑合约并没有被初始化,因为initialize()并没有在逻辑合约中被直接调用。鉴于逻辑合约本身未被初始化,任何人都可以调用initialize()函数来初始化它,将状态变量设置为一个恶意的值,并有可能接管逻辑合约。
逻辑合约被接管的影响取决于系统中的合约代码。在最坏的情况下,攻击者可以将UUPS代理模式中的逻辑合约升级为恶意合约,并执行“自毁”函数调用,这可能导致整个代理合约变得毫无用处,合约中的资产将永久丢失。
案例
① Parity Multisig Freeze:未初始化逻辑合约。攻击者触发了许多钱包的初始化,并通过调用 selfdestruct() 将以太币锁定在合约中。
② Harvest Finance、Teller、KeeperDAO和Rivermen都使用了未初始化的逻辑合约,这将允许攻击者任意设置合约的初始化参数,并在delegatecall() 期间执行selfdestruct()将代理合约销毁。
OpenZeppelin库提供的代理合约不会在合约中声明状态变量,而是基于EIP 1967标准,将需要存储的数值(如管理地址)保存到特定的存储槽中,以防止冲突。
案例
北京时间2022年7月23日,去中心化音乐平台Audius遭到黑客攻击,该事件是由于在代理合约中引入新的逻辑,从而产生存储冲突所导致的。
代理合约声明了一个proxyAdmin地址状态变量,在执行逻辑合约代码时,其值会被错误地读取。
项目方私自定义的proxyAdmin的值被错误的当成了initialized和initializing的值,使得initializer修饰符返回了错误的结果,进而允许攻击者再次调用initialize()函数并将管理合约的权限授予自己。攻击者随后更改了投票参数并通过了他们的恶意提案,以窃取Audius资产。
案例
Pickle Finance、Furucombo以及dYdX攻击事件。
在这几起事件中,存在漏洞的合约获得了用户token的批准,并且合约内存在一个由用户提供调用合约地址和数据的call()/delegatecall(),攻击者将能够调用具有transferFrom()功能的合约,以提取用户余额。在dYdX事件中,dYdX执行了他们自己的白帽攻击以保护资金。
逻辑实现合约也应注意不要使用delegatecall(),这样可以防止攻击者执行某些恶意代码,如selfdestruct()。
虽然遵循最佳实践可以确保代理合约部署的稳定,同时保持可升级的灵活性,但所有代码都容易出现可能危及项目的新安全或逻辑问题。因此所有的代码最好由具备审计和保护代理合约协议经验的安全专家团队进行审计。
温馨提示:仅提供区块链&数字货币平台信息分享服务,所有产品及展示信息均来源于发行方或者互联网。炒币属于投资行为,不等同于银行存款。市场有风险,投资需谨慎。投资虚拟货币有极大的风险,本网站提供的任何信息都不构成投资建议、财务咨询、交易咨询,或任何其他建议的依据,领域OK并不推荐您购买、售出或持有任何虚拟货币。在做出任何投资决定前,请先充分衡量风险。如有损失,请自行承担后果。