Author: Xiaobai
Editor: Liz
Background Overview
In the Ethereum ecosystem, the deterministic generation mechanism of contract addresses provides convenience to developers but also introduces new attack surfaces. In this issue, we will analyze the attack techniques and defense strategies of deploying different contracts to the same address using CREATE and CREATE2 opcodes at different times. Previous articles on smart contract security auditing can be found in the collection.
Prerequisite Knowledge
First, let's understand the two rules of Ethereum address generation:
1. CREATE
CREATE is the native opcode used in the Ethereum Virtual Machine (EVM) for dynamically deploying smart contracts. Since the Ethereum genesis block, all contract deployments rely on this mechanism. Its core feature is that the address generation depends on the deployer's account nonce, making the address non-deterministic (cannot be precisely predicted before deployment).
The contract address generated by CREATE is determined by the deployer's address and the nonce of the address:

2. CREATE2
CREATE2 is a new contract creation opcode introduced in the Ethereum Constantinople hard fork (February 2019). Unlike traditional CREATE, off-chain participants can pre-calculate the contract address before contract deployment, making off-chain interactions (such as state channels) and complex contract architectures possible.
The contract address generated by CREATE2 is determined by the following four parameters:

At this point, I believe everyone has understood the two ways Ethereum generates contract addresses. Observant readers may wonder what happens if the computed contract address already exists? This need not worry; in Ethereum, regardless of whether the address generated by CREATE or CREATE2 already exists on the chain (whether it is an externally owned account EOA or a contract account), the EVM will reject the request to create the contract. The following are two scenarios of address conflict:
1. The target address is an externally owned account (EOA)
Rule: If the target address is an existing EOA (for example, a user wallet address), the EVM will reject the contract deployment request.
Result: Transaction fails, gas is consumed, the contract will not be created, and no data at that address will be overwritten.
2. The target address is a contract account
Rule: If the target address is a deployed contract account, the EVM will also reject the deployment request.
Result: Transaction fails, gas is consumed, and the original contract's code and storage data remain unchanged.
Of course, there are exceptions. If the target address is a contract and has already been self-destructed, a new contract can then be redeployed at that address.
At this point, the various characteristics of the CREATE and CREATE2 opcodes have been introduced. Next, let's see how to use the characteristics of these two opcodes to execute a combination of attacks and complete a contract attack.
Vulnerability Example

Vulnerability Analysis
This DAO contract implements a basic governance mechanism: the Owner reviews and records the proposal contract address in the Proposals array through the approve function. Any user can then execute the reviewed proposal through the execute function. The seemingly strict access control (only Owner can review proposals) combined with execution checks (the proposal must be reviewed and not executed) actually contains a hidden logical flaw: the reviewed proposal address may point to completely different contract code during execution. The attacker can implement the attack in three steps:
1. Deploy a normal contract and obtain authorization
The attacker first deploys a contract A that contains a harmless executeProposal() function, and adds the address to the proposal list through Owner's review.
2. Self-destruct the original contract to occupy the address
Contract A executes the self-destruct operation (selfdestruct) to clear the code, and then the attacker uses the CREATE2 opcode to deploy a malicious contract B (containing dangerous logic for executeProposal()) at the same address.
3. Trigger execution and hijack control
When the user calls execute, the contract executes the code of the newly deployed malicious contract B through delegatecall. Since delegatecall retains the context of the current contract, the attacker can use this operation to tamper with the state of the DAO contract (such as modifying the Owner) or transfer assets.
Next, let's look at the specific attack process combined with the attack contract.
Attack Contract

The attack process is as follows:
1. Alice deployed the DAO contract.
2. Evil deploys DeployerDeployer contract, address DD.
3. Evil calls DD.deploy(), using CREATE2 to deploy Deployer to address D (fixed salt).
4. Call D.deployProposal(), create proposal contract address P, at this time D's nonce is 0.
5. Alice approves address P.
6. The attacker calls D.kill(), destroying D. At this point, the account of address D is cleared, including the nonce being reset to 0.
7. The attacker again calls DD.deploy(), redeploying Deployer to address D (same CREATE2 parameters).
8. Call D.deployAttack(). Since D's nonce has been reset to 0 and D's address has not changed, the created Attack contract address is the same as the previous Proposal contract address P.
9. At this point, there is a proposal in the DAO's proposals array pointing to address P, but now address P has been redeployed as the Attack contract. Therefore, when calling execute(), it will execute Attack's executeProposal(), ultimately modifying the DAO's Owner to msg.sender, which is the Attack contract.
The attack principle can be summarized as the attacker utilizing the CREATE2 feature to redeploy contracts to the same address. First, deploy a legitimate Proposal contract and let the DAO approve it. Then destroy the Deployer contract, redeploy the same address Deployer, reset the nonce of that address, and ultimately successfully deploy a malicious contract to the same address as the previous Proposal. Since the proposal address stored in the DAO now points to the malicious contract, executing the proposal will execute the malicious code in the DAO's context, successfully changing the Owner to the attacker.
Fix Suggestions
As a developer:
When approving proposals, it is necessary not only to record the address but also to record the code hash of that address, and verify the code hash during execution.
Avoid using delegatecall to call external contracts unless there are sufficient security measures.
Consider the possibility of redeploying to the same address after contract self-destruction. When making external calls, check whether the external contract is trustworthy. If you need to call an unfamiliar external contract, it is recommended to check whether there is a self-destruct prevention mechanism within the contract.
As an auditor:
Identify whether there is a selfdestruct functionality in the externally called contracts. If so, be cautious of the risk of external contracts self-destructing and deploying malicious code to the same address.
Check whether the contract deployment method uses CREATE2, ensuring that the salt value used to generate the contract address is random enough to prevent contract address being pre-emptively predicted and occupied by attackers.
Check whether the DAO verifies the code consistency of the target address when executing proposals, such as comparing the current code hash with the approved hash.
Carefully analyze the usage scenarios of delegatecall, confirming whether the target address is trustworthy and whether there is a risk of arbitrary address calls.
Review the lifecycle management of proposals, such as whether there are mechanisms to prevent proposals from being modified or replaced, such as locking the target address's status after approval.
