通过一些巧妙的技巧,你可以创建“变形”的变形智能合约,通过了解这种变形的原理,则可以反向检测出它们。
作者:老雅痞
以太坊安全性的前提就是假设智能合约是不可变的。智能合约的代码一旦部署在区块链上就无法更改。但是在实践中,一些智能合约可能会发生变化——即使它们已经部署。通过一些巧妙的技巧,你可以创建“变形”的变形智能合约,通过了解这种变形的原理,则可以反向检测出它们。
变形智能合约是可变的,这意味着开发人员可以更改其中的代码。web3 用户他们信任代码,希望代码可以保持绝对一致性的运行,所以这些可变形的智能合约给 web3 用户带来了严重的风险,特别是当不良行为者拥有了编写变形智能合约的能力时。想象一下,攻击者使用该技术“欺骗”那些在智能合约中质押代币的人,他们没有意识到这是变形的。这种破坏力是巨大的,可变形的只能合约使诈骗者有能力捕食他人,并且通常会破坏对去中心化系统的全部承诺的信任。
为了分析智能合约是否包含变形属性,我构建了一个简单的变形合约检测器(受 Jason Carver、0age和其朋友的原创作品启发和构建)。任何人都可以使用该工具来检查给定的合约是否显示出可能表明可能发生变形的危险信号。该方法并非万无一失:仅仅因为智能合约显示了一个标志,并不意味着它一定是变形的;仅仅因为它没有,并不意味着它是安全的。检查器仅提供一个快速的初步评估:根据可能的指标 ,合约可能会变形。
Web3 用户应该熟悉变形合约带来的威胁,以便能够留意并避免可能的攻击。钱包和区块链索引器可以通过在用户与可能包含变形属性的智能合约交互之前警告用户来提供帮助。该工具旨在帮助教育人们了解这种潜在威胁……并防御它。
检测变形智能合约
我构建的Metamorphic Contract Detector分析了六个属性,这些属性可能表明智能合约是否是变形的。
是否用于部署合约的已知变形代码?如果已知的变形字节码——通常用 Solidity 编写的以太坊智能合约在编译后变成的较低级别的虚拟机可读代码——出现在给定智能合约部署的交易中,这是一个主要的危险信号。在接下来的部分中,我们将讨论一个由 0age 开发的变形字节码示例。一个重要的警告:变形字节码可能存在无数种变体,检测所有变体是非常困难的。但是,通过扫描众所周知的实例,检测器消除了那些攻击者容易得到的仅仅复制和粘贴现有示例的结果。
智能合约代码可以自毁吗?要替换合约中的代码(创建变形合约的关键步骤),开发人员首先需要删除预先存在的代码。做到这一点的唯一方法是使用SELFDESTRUCT 操作码,该命令的作用与听起来完全一样——它会擦除给定合约地址的所有代码和存储。合约中存在自毁代码并不能证明它是变形的;但是,它提供了一个线索,即合约有可能是变形的,无论如何,了解你所依赖的合约是不是有自我毁灭代码是很重要的。
智能合约是否从其他地方调用代码?如果有问题的智能合约不能直接自毁,它仍然可以通过使用DELEGATECALL 操作码来擦除自己。此操作码允许智能合约动态加载和执行存在于另一个智能合约中的代码。即使智能合约不包含 SELFDESTRUCT 操作码,它也可以使用 DELEGATECALL 从其他地方加载自毁代码。虽然 DELEGATECALL 功能并不能直接指示智能合约是否是变形的,但这是一个可能的线索——一个潜在的安全问题——值得注意。请注意,该指标有可能引发许多误报。
是否有另一个合约部署了这个合约?变形合约只能由其他智能合约部署。这是因为变形合约由另一个操作码启用,只能由其他智能合约使用,称为 CREATE2。(我们将在后面的部分中讨论 CREATE2是如何工作的以及为什么它很重。)这个特征是可能变形的最不显眼的指标之一。这是一个必要但不充分的先决条件。扫描此特征可能会引发许多误报——但这是有价值的信息,因为它可能会引起怀疑并提供进一步审查合约的理由,特别是如果智能合约包含下面描述的操作码。
部署者合约是否包含 CREATE2 操作码?如上所述,通过 CREATE2 部署是变形的必要前提。如果部署者合约包含 CREATE2 操作码,这可能表明它使用 CREATE2 来部署相关合约。 如果部署者确实使用 CREATE2 来部署所述合约,虽然这并不意味着合约一定是变形的,但这确实意味着它可能是变形的,谨慎行事并进一步调查可能是明智的。再次提醒您注意误报:CREATE2有很多合法用途,包括支持“第 2 层”扩展解决方案,以及更容易创建可以改进 web3 的智能合约钱包用户加入和密钥恢复选项。
代码变了吗?这是最明显的说法,但只有在变形合约已经变形后才会出现。 如果智能合约的代码哈希(一个唯一的加密标识符)与合约最初部署时的不同,那么很可能代码已被删除、替换或更改。如果哈希不再匹配,则代码的某些内容发生了变化,并且合约可能会变形。该标志是变形最可靠的指标,但这一条检测对预测或先发制人没有任何帮助,只能检查变形是否已经发生。
除了为 Metamorphic Contract Detector 构建一个简单的命令行工具外,我还构建了一些示例智能合约来演示一个骗局的变形合约抵押场景,我将在下一节中对此进行描述。所有代码都可以在这个GitHub 存储库中找到。
恶意行为者如何使用变形合约窃取人们的资金
以下是有人可能如何使用变形智能合约作为骗局的一部分。
首先是设置阶段。攻击者使用两种工具在区块链上的特定地址部署智能合约:变形字节码和 CREATE2 操作码。(稍后我们将扩展这两个概念。)变形字节码然后按照其名称的含义进行“变形”。在这里,它变成了一个质押合约,用户可以在其中质押 ERC-20 代币。(同样,我们稍后会讨论这个变形技巧的细节。)
接下来是诱饵和开关。毫无戒心的用户将他们的代币押在这份合约中,被赚取收益或其他一些好处的可能性所吸引。然后,攻击者使用上一节中讨论的SELFDESTRUCT 操作码删除此智能合约地址上的所有质押代码和“状态”——区块链存储或内存。(应该注意的是,作为单独的 ERC-20 合约的一部分存在的代币会持续存在,不受自毁合约的影响。)
最后,Rug Pull 就是一个卷包跑路的大动作。攻击者重用在设置阶段使用的相同变形字节码来“重新部署”一个新合约。这个新合约部署到最近被自毁合约腾出的同一个地址。然而,这一次,字节码“变形”(同样,我们将在稍后解释)变成一个恶意合约,可以窃取合约地址上的所有代币。骗局完毕。
变形智能合约带来的风险现在显而易见。但是你可能仍然想知道,这种变形技巧实际上是如何工作的?要理解这一点,必须更深入字节码级别地探索。
CREATE2如何开启变形的可能性
CREATE2是 2019 年 2 月引入以太坊的操作码升级,它提供了一种部署智能合约的新方法。
CREATE2 让开发人员能够比以前更好地控制智能合约的部署。原始的 CREATE 操作码使开发人员难以控制要部署的智能合约的目标地址。使用 CREATE2,人们可以在将特定智能合约实际部署到区块链之前,提前控制和了解特定智能合约的地址。这种预知——加上一些巧妙的技巧——使人们能够创建变形的智能合约。
CREATE2如何预测未来?操作码的计算是确定性的:只要输入不改变,CREATE2 确定的地址就不会改变。(即使是最小的更改也会导致部署发生在其他地方。)
更详细地说,CREATE2 是一个将几个元素组合并散列在一起的函数。首先,它包含部署者(或发送者)的地址:启动智能合约,充当要创建的合约的父合约。接下来,它添加发送者提供的任意数字(或“salt”),这允许开发人员将相同的代码部署到不同的地址(通过更改 salt)并防止覆盖现有的相同合约。最后,它使用一些智能合约初始化(“init”)字节码的 keccak256 哈希,这是变成新智能合约的种子。这种哈希组合确定了一个以太坊地址,然后将给定的字节码部署到该地址。只要字节码保持完全相同,CREATE2 将始终将给定的字节码部署到区块链上的相同地址。
下面是 CREATE2 公式的样子。(注意:在下面的示例中,你会注意到另一个元素,“0xFF”。这只是 CREATE2 用于防止与前面的 CREATE 操作码发生冲突的常量。)