文章讨论了以太坊升级中的CREATE2
操作码及其可能带来的安全风险,特别是‘Wild Magic’和‘Zombie Contracts’的概念。作者详细介绍了如何防御这些潜在的攻击,并提供了几种保护措施,如使用不可销毁合约、验证部署历史和验证目标字节码。
在 Constantinople 升级之后,合约可以被就地升级。发现一些新的有趣的方式,让你可能会失去你的加密资产。
我的观点是我们应该从 Constantinople 中移除 EIP-1014。但它仍然被保留了下来,因此请传播如何在 Constantinople 升级后的世界中思考...
在我成长的过程中,我最喜欢的作家之一是 Tamora Pierce。遗憾的是,这些故事对我来说已经大多淡忘了,但有一个有趣的事情留了下来:在《不朽者》系列中,主角可以变形为动物。读者们很高兴看到 Daine 使用她的“野性魔法”(动物变形)来拯救世界。但在错误的人手中,它是一种令人反感的武器。前一分钟你面前还是一只小猫在咕噜咕噜,下一分钟你可能会被一头犀牛扔来扔去。
我更喜欢 Bufficorn
为什么提到野性魔法?因为变形是一个很好的比喻,代表了即将在下一个以太坊网络升级中出现的新功能。
CREATE2
Constantinople 引入了一个新的操作码 CREATE2
。它有一些有趣的功能,但也有一些似乎尚未被广泛理解的特性。
CREATE2
的核心命题是,你可以承诺使用初始化代码来部署合约,然后使用该初始化代码部署到一个可预测的地址。最有趣的用例之一是将其用作 Layer 2 交互的仲裁合约。如果 Layer 2 出现问题,执行仲裁合约来纠正问题。仲裁合约已经是可能的,但通常它们必须在 Layer 2 交互发生之前部署,因为每个人都需要就合约是什么以及它的位置达成一致。使用 CREATE2
,你可以等到需要仲裁时再部署合约(理想情况下这种情况很少发生),从而实现大幅提升的可扩展性,并降低 Gas 成本。我听说这被称为“反事实合约”。
CREATE2
带来了一个特性,至少在 EIP 1014 中是如此定义的。一个 CREATE2
合约在销毁后可以重新部署到相同的地址。selfdestruct()
之后,合约会从状态中完全移除,因此允许重新部署到相同的地址。
稍微聪明一点,你可以将不同的字节码部署到相同的地址,或者使用标准的 CREATE
重新部署合约。我将把这些具体实现的细节留到后续的文章中。
在 Constantinople 升级之前,合约部署的心理模型是,合约可能处于三种状态:“尚未部署”、“已部署”或“已自毁”。在 Constantinople 之后,我们增加了第四种状态:“重新部署”。根据一些随意的交谈和一项非正式调查,许多人对这一变化并不了解。在不了解这种可能性的情况下,你可能会被利用。
在这篇文章中,我将使用野性魔法来指代重新部署的合约具有不同的字节码。我们还将讨论使用相同字节码复活的僵尸合约。
野性魔法可以用来部署一个修复了错误的升级合约,或者也可以用来试图将你与你的加密资产(如以太币和代币)分离。以下是一个黑帽可能会尝试的事情:
pragma solidity ^0.5.3;
contract OMGVendor {
// 我们验证了两个合约的地址
ERC20 OMG = ERC20(0xd2...);
ERC20 DAI = ERC20(0x89...);
function vend(uint omg_to_buy) external {
// 硬编码价格为 1:1
uint dai_cost = omg_to_buy;
// 我们验证了 transferFrom 如果任何一个转账失败会回滚
DAI.transferFrom(msg.sender, address(this), dai_cost);
OMG.transferFrom(address(this), msg.sender, omg_to_buy);
}
function shutdown() external {
if (msg.sender == 0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7) {
selfdestruct(msg.sender);
}
}
}
4. 源代码很简单且合法!它只会转移出你请求的 DAI 数量,并会将 OMG 发送给你。
5. 作者甚至是一个好公民,并包含了 selfdestruct()
,以便他们可以在使用后清理合约
6. 你批准合约访问你的全部 DAI 余额
在 Constantinople 之前,潜在的缺点是合约在你的 vend()
交易被挖出之前被销毁。
在 Constantinople 之后,潜在的缺点是你持有的所有 DAI 被耗尽。
黑帽可以销毁合约并替换为一个会窃取你所有DAI的合约!
有几种方法可以保护自己免受黑帽手中的野性魔法攻击:
EXTCODEHASH
野性魔法需要在升级之前进行自毁步骤。所以一种方法是验证销毁合约是不可能的。这有一定的限制性。它也比在 Solidity 中找到 selfdestruct()
调用要复杂一些。任何调用 CALLCODE
或 DELEGATECALL
的东西也可能触发 SELFDESTRUCT
。
稍微微妙一点,你可能会努力确保合约不是立即可销毁的。例如,在发出声明意图的事件并等待 72 小时之前,合约可能无法 selfdestruct
。只要你有足够的时间对事件做出反应(例如,取消对合约访问你的资金的批准),那么你可能会安全,这取决于合约。
如果目标合约是由另一个合约创建的,那么你可以验证它是通过 CREATE
调用而不是 CREATE2
创建的。但要小心!即使被审计的合约是通过 CREATE
部署的,它的父合约可能已通过 CREATE2
部署。如果两者都是可销毁的,那么目标合约仍然可能是可变的。因此,你需要追溯交易链,从创建目标合约的交易到创建该父合约的交易,再到创建祖父合约的交易,一直追溯到 EOA。如果它们中没有使用 CREATE2
,那么你就知道 野性魔法 是不可能的。(如果你已经达到一个在 Constantinople 之前部署的合约,你可以稍微打个折)
在调用合约之前,你对部署的字节码进行审计。审计之后,保留审计代码的哈希值。你可以使用 Constantinople 中的另一个新操作码 EXTCODEHASH
,它可以帮助你廉价地确认代码没有改变。(由于前置攻击,你不能从 EVM 之外验证字节码中获得任何信心
- 原文链接: medium.com/@jason.carver...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!