变形智能合约:EVM代码真的不可更改吗?

  • mixbytes
  • 发布于 2024-12-25 22:11
  • 阅读 26

本文深入探讨了以太坊智能合约的变更机制,通过CREATE2与SELFDESTRUCT操作,可以实现在特定地址上更新合约逻辑。这种技术的理解对于区块链安全至关重要,同时也提出了针对可变合约的检测和防御措施。作者呼吁改变SELFDESTRUCT在以太坊中使用的现状,以增强智能合约的安全性。

作者:Sergey Boogerwooger - MixBytes的安全研究员

人们普遍认为以太坊上的智能合约代码是不可更改的,一旦部署就无法更改。然而,这仅在合约是通过标准程序部署的情况下才成立。

本文将讨论允许在特定地址创建智能合约,然后通过修改处理用户数据的字节码来更改其内部逻辑的技术。

CREATE2 + CREATE + SELFDESTRUCT

在以太坊中,更改固定账户的字节码可以通过一系列CREATE/CREATE2和SELFDESTRUCT调用来实现。每个合约在此序列中部署并销毁其继承者,理解这些操作对于继续阅读本文至关重要。虽然这些操作在文档中被充分描述,但我们将提供一个简要概述:

CREATE 和 CREATE2 是独特的EVM操作,用于创建新账户。只有当账户是“空”的时(“空”意味着代码大小 == 0 且 nonce == 0),才能创建合约。新创建的智能合约的代码大小 > 0 且 nonce == 1。虽然这个信息众所周知,但值得提及关于CREATE/CREATE2的以下细节:

  • CREATE - 从keccak256(_deployer_addr, deployer_nonce_)计算新合约的地址(实际上是: keccak256(rlp([_deployer_addr, deployer_nonce_]))[12:])
  • CREATE2 - 从keccak256(_0xFF, deployer_addr, salt, bytecode)计算新合约的地址(实际上是: keccak256(_0xFF, deployer_addr, salt, bytecode)[12:])

相反,SELFDESTRUCT "清除"账户,通过重置其字节码和nonce。对我们来说,重要的是要注意SELFDESTRUCT重置账户的nonce,使我们能够使用相同的nonce从同一地址多次调用CREATE。

这是如何实现的

如果你使用CREATE -> SELFDESTRUCT -> CREATE -> SELFDESTRUCT -> ...,并且使用相同字节码从相同地址,每个新的CREATE将部署到一个新地址,因为deployer_nonce将在每个新交易中增加。

如果使用CREATE2 -> SELFDESTRUCT -> CREATE2 -> SELFDESTRUCT -> ...,并且使用相同的字节码和salt,结果地址在每次迭代中将保持不变。

然而,正如你可能已经注意到的,在第一种情况下(使用CREATE),如果“deployer_nonce”未更改,则部署地址可以是相同的。这是诀窍:通过SELFDESTRUCT,我们可以重置合约所处地址的nonce,从而允许其重新部署。然后,CREATE可以从同一地址部署一个新合约,但使用不同的代码。

结合这一切,我们可以实现以下示例场景:

  1. 使用CREATE2部署MutDeployer合约。新合约账户的nonce == 1(EIP161)。
  2. MutDeployer使用CREATE部署MutableV1(可变代码的第一个版本)。新合约被部署在地址 = keccak256(MutDeployerAddr, MutDeployerNonce == 1)。
  3. 用户与MutableV1交互,认为其代码是常量。
  4. 所有者SELFDESTRUCT MutableV1,然后在同一地址部署MutableV2。
  5. 所有者SELFDESTRUCT MutDeployer,使其账户变为空(codesize == 0 && nonce == 0)。
  6. 用户重复第1步,在同一地址用CREATE2部署相同的MutDeployer合约。但现在MutDeployer有nonce == 1,正如在第1步中一样。
  7. MutDeployer使用CREATE部署新的字节码MutableV2(如第2步)。新合约在同一地址 == keccak256(MutDeployerAddr, MutDeployerNonce == 1)被部署。
  8. 用户使用与MutableV1相同的地址与MutableV2交互。

该场景的代码:

展示此模式工作原理的代码和测试可以在这里找到。你可以通过运行npx hardhat test test/Metamorph.js在这个教育库中进行实验。

这类构造的另一个重要点是DELEGATECALL,允许一个合约在其上下文中调用SELFDESTRUCT。虽然SELFDSECTRUCT指令存在于外部合约的字节码中,但它增加了检测可变代码的复杂性。

如何防御

对变形合约的检测在这个检测器和文章中有详细描述,它允许你检查给定地址是否可以具有可变代码。简而言之,为了检测这种情况,你需要检查该合约是否不是由另一个合约部署的,是否不包含SELFDESTRUCT操作码,或是否使用DELEGATECALL到包含SELFDESTRUCT的合约,并且在部署时是否使用了CREATE2等。

因此,如果这些检查都被执行,你可以确定合约的代码是不可改变的。然而,在安全场景中,避免检测似乎是可能的,通过构建DELEGATECALL->DELEGATECALL->DELEGATECALL->...的调用链或其他技巧,所以要小心。

我们非常尊重a16z团队在这个重要主题上的工作。Coinmonks关于此主题的另一篇好文章可以在这里找到。

销毁 SELFDESTRUCT

SELFDESTRUCT在解决上述问题中的作用极为重要,我们还必须提及以太坊社区中关于SELFDESTRUCT的持续讨论。对于EVM来说,SELFDESTRUCT是一个“非常特殊”的操作,因为它是由EVM中的操作码从合约代码中发起的。后续操作在状态数据库中在EVM上下文之外执行,因为在SELFDESTRUCT后,节点必须销毁代码和账户状态,并清除其存储和所有相关字段。因此,除了与创建/销毁相关的问题外,还有关于区块链整体性能的担忧。

此外,SELFDESTRUCT的主要目的是清理状态数据库中的空间,这在现实世界的场景中并不实用。协议不会在其代码中包含SELFDESTRUCT,因为用户不愿意将自己的资金投入可以被销毁的合约中。此外,SELFDESTRUCT使任何合约分析脚本不一致,因为当你从过去的特定区块下载合约字节码时,你不能确定该代码是否会在以后保持在同一地址。

Vitalik已提出强有力的理由来将SELFDESTRUCT指令从EVM中移除。我们同意此举将极大地提高智能合约的安全性,并不会显著影响区块链的行为。SELFDESTRUCT所解决的问题可以通过其他机制来缓解,例如清理和压缩不活跃的账户、数据和共识层的分离以及其他机制。因此,我们在等待SELFDESTRUCT的自我销毁 :)

  • MixBytes是谁?

MixBytes是一个专家区块链审计和安全研究团队,专注于为兼容EVM和基于Substrate的项目提供全面的智能合约审计和技术咨询服务。请关注我们X,以获取最新行业趋势和见解。

  • 原文链接: mixbytes.io/blog/metamor...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
mixbytes
mixbytes
Empowering Web3 businesses to build hack-resistant projects.