Not sure if this has been discussed elsewhere/before but I came across this topic today on AllCoreDevs gitter and thought it may be useful to start a thread here. Thanks to @carver and @holiman for initiating this discussion.
Summary (per my understanding so far): CREATE2
allows contracts to change in-place after being deployed. This is because, although CREATE2
includes the hash of init_code
in address generation, the same init_code
could intentionally generate arbitrary contract code. There appear to be use cases where such behaviour is desirable but potentially leaves it open for misuse. So users interacting with a seemingly âbenignâ contract earlier could suddenly be interacting with a newer contract (at the same address) which implements a completely different (potentially malicious) functionality.
Perhaps this is obvious to some but not to many. So it may be worthwhile to make sure this is discussed and documented. Iâm copying the gitter discussion context below:
From @carver:
So, based on my social network and @holiman 's sampling, it looks like a lot of contract devs arenât aware that (new) contracts will be able to change in-place after CREATE2 from Constantinople goes live. How about in this room, is this a broadly-understood change?
It opens up a lot of interesting ways of tricking people into giving you their ether/tokens/etc â for example: have a user verify contract source, send a transaction to your contract, and then front-run them with a couple transactions to swap in the new contract with arbitrary source.
I think the implication there is that any mischievous contract post-Constantinople already looks shady, pre-Constantinople. But you can construct a pretty innocuous contract pre-C, one that has two possible outcomes from a transaction: {âcontract existsâ: âswap tokensâ, âcontract self-destructsâ: âwaste some gasâ}. Post-Constantinople, the options could now become {âcontract existsâ: âswap tokensâ, âcontract self-destructsâ: âwaste some gasâ, âcontract replacedâ: âall ERC20 tokens that were pre-approved to the contract are stolenâ}.
There are mitigations for these problems, of course. We just need to educate coders and auditors, and soon. (Iâll do my part and write something up, but my reach is pretty minimal)
For example, the init code could load bytecode from another contract and return it. So the init code doesnât change, but returns arbitrary contract code to deploy.
From @holiman:
And the corollary being, as previously, that if someone verified the source, he should have noticed the
SELFDESTRUCT
(without a due inactivity period) and avoid interacting with it. I would believe that most perusers of this channel are fully aware of all the effects ofCREATE2
. The general eth deveopers and auditors, less so, unfortunately.
Yes, thatâs it. Iâm constantly surprised about how little known that fact is - even in this very niche channel
From @veox:
CREATE2 allows for many new funky use cases; @carver - could you explain your example?..
CREATE2-derived contract address depends on the âinit codeâ hash being passed as argument to the instruction, so ânew contract with arbitrary sourceâ (in actual on-chain deployment) translates to that contract being deployed to a different address (than would have been presented previously to the user).
(This is a non-issue for the attacker if the user is to interact with the CREATE2-deployed contract through an attacker-provided delegation mechanism.)
You mean a scheme of: in-CREATE2 init_code loads data to memory from storage of a fixed external contract, and the external contract is under an attackerâs control (allows changing payload in storage).
Future educational material on CREATE2 could benefit from highlighting that init_code is not the code to be deployed, but the code to generate the code to be deployed. Similar to current send-to-none âcontract deploymentâ transactions that use CREATE. Highlighting generate, that is (so dynamic!).
From @rajeevgopalakrishna:
Doesnât this change a major invariant assumed by users today and introduce a potentially serious attack vector with
CREATE2
? Doesnât this mean that any contract post-Constantinople with aselfdestruct
is now more suspect than before? Should restrictions be considered oninit_code
used inCREATE2
to prevent it from loading bytecode from storage of other contracts (not sure if this is possible)?
From @veox:
Not SELFDESTRUCT per-se, but non-deterministic init_code. Restricting storage access would also limit the usefulness of CREATE2, specifically in cases where one would want non-determinism. Say, a claim to ownership of a particular ENS entry in the past. Or, more broadly, a query to ENS in init_code. Or to the reverse registrar. Or even an ERC-20 balance look-upâŚ