Smart Contract Security Newsletter #16 — CREATE2 FAQ

Distilled News

This week was a little bit slow in the sense that there were no “major incidents”. Though there has been a lot of chatter about CREATE2 (EIP-1014) this week.

So, what is all this stuff about CREATE2?

I thought I’d try to distill that down a bit, and an FAQ format felt like the right way to do it.

What is CREATE2?

It’s a new opcode for deploying smart contracts. Similar to CREATE, when CREATE2 is called in a contract (which I’ll refer to as the ‘factory’) it will run some init_code. Whatever that init_codereturns will be the code in the new contract. In Solidity, this init_code is defined by a constructor() function.

How is it different from CREATE?

The important difference is in how the address of the new contract is determined.

With CREATE the address is determined by the factory contract’s nonce. Everytime CREATE is called in the factory, it’s nonce increases by 1.

With CREATE2, the address is determined by an arbitrary salt value, and the init_code.

Why do we need it?

The big advantage of CREATE2 is that the destination address is not dependent on the exact state (ie. the nonce) of the factory when it’s called. This allows for the result of a transaction to be simulated off-chain, which is an important part of many state channel based approaches to scaling.

So what is everybody so worked up about?

Recalling two things I wrote above:

Whatever that init_code returns will be the code in the new contract.

With CREATE2, the address is a function of an arbitrary salt value, and the init_code.

So here’s the key detail: if CREATE2 is run twice in the same factory contract, with the same salt, and the sameinit_code, it will result in a second contract being deployed to the same address as the first contract!

OK, so… then what happens?

If the first contract is still at the address when CREATE2 tries to put a new contract there, the operation will simply REVERT; which is fine.

But a few other facts complicate the story:

Another infamous opcode, SELFDESTRUCT, is also lurking within the EVM.

The same init_code can return different values at different times.

How could an attacker make use of this?

An example attack that this would enable goes like this:

Using CREATE2, deploy a safe looking contract.

Convince some people to deposit their ETH, Tokens, Kittens, etc.

Call SELFDESTRUCT on that safe looking contract, and it will be removed from state.

Using CREATE2 again, replace it with a malicious contract that sends all the funds to you.

My god that’s horrible

Right!?

What can we do about it?

Even if a contract was deployed by CREATE2, the attack would not be possible if it doesn’t contain a SELFDESTRUCT, DELEGATECALL, or CALLCODE opcode.

That kind of analysis could technically be run by wallets and block explorers to warn users, but this is quite a burden to place on them.

Why weren’t people talking about this earlier?

This to me is one of the more interesting things, ie. the pattern of bugs not being found, or possible issues not being discussed until the last minute.

The Constantinople fork was postponed with 48 hours notice before due to a potential vulnerability completely unrelated to CREATE2. So, this public conversation very easily may not have happened before the change was made permanent.

The ENS launch was the same; two serious bugs were discovered the day of it’s launch.

What should we do to get more attention on these things?

Some advocate bug bounties, which worked recently for Gnosis’ dxDAO. But bounties often see very little activity, and because of the economics, bounty hunters tend to look broadly for easy to find bugs.

In order to ensure the security of protocol changes, both a deep and holistic review is required. The audit being done on ProgPOW is a good start in that direction.

What do I think about CREATE2?

Well… scaling is essential, and state channels are extremely promising, so I want the improvements it brings. But I’m starting to think the side effects are excessively dangerous. A suggestion I find compelling was put forward by Nick Johnson:

I honestly think the simplest solution to all of this would have been to modify self destruct to leave an account’s nonce intact. Selfdestruct is already an ineffective way to encourage freeing state, and this would solve the issues.