shaneson.eth

发布于 2022-06-15到 Mirror 阅读

EIP-1014: Skinny CREATE2

Eip学习进行时。

在Uniswap 的代码中,出现了以下这段代码。当时我在研读代码的时候留意到了,create2不是一个标准的语法糖,所以这里肯定会有比较深的理论和学习的地方。经过学习和研究,这是用于Create Contract的EIP-1014标准。

CREATE2 是以太坊在 “君士坦丁堡” 这次硬分叉升级中引入的一个新操作码,不同于 CREATE,它使用新的方式来计算合约地址,让生成的合约地址更具有可控性。通过 CREATE2 可以延伸出很多有意思的玩法,在 CTF 中最常见的就是利用这种可控性,在同一个地址先后部署字节码完全不同的合约。

原理

如果利用外部账户或者使用 CREATE 操作码的合约账户创建一个合约,那么很容易就能确定被创建合约的地址。每个账户都有一个与之关联的 nonce:对外部账户而言,每发送一个交易,nonce 就会随之 +1;对合约账户而言,每创建一个合约,nonce 就会随之 +1。新合约的地址由创建合约交易的发送者账户地址及其 nonce 值计算得到,其具体公式如下

keccak256(rlp.encode(address, nonce))[12:]

不同于原来的 CREATE 操作码,在合约地址的计算方法上,CREATE2 不再依赖于账户的 nonce,而是对以下参数进行哈希计算,得出新的地址:

  • 合约创建者的地址(address)
  • 作为参数的混淆值(salt
  • 合约创建代码 (init_code)
keccak256(0xff ++ address ++ salt ++ keccak256(init_code))[12:]

一个需要注意的重要细节是,计算合约地址所需的最后一个参数并非合约代码,而是其创建代码。该代码是用来创建合约的,合约创建完成后将返回运行时字节码。

这意味着,如果我们控制了合约的创建代码并使其保持不变,然后控制合约构造函数返回的运行时字节码,那么我们很容易就能做到在同一个地址上,反复部署完全不同的合约。事实上 CREATE2 这种让合约在部署后可以被重新更改的特性存在着潜在的安全问题,也引起了人们对其的讨论

在 CTF 中,这种特性往往会被用来作为一个技巧,通过在同一个地址上部署不同的合约用来 bypass 不同的校验。