M-1: Permissioned deployment protection is disabled in create3 calls
Summary:
In the deploy scripts, salt generation is based on the contract name and version number. The underlying create3 lib, CreateX, only enables permissioned deployment protection when the first 20 bytes in the salt are equal to msg.sender. In other words, currently, all create3 calls will be executed without this protection.
The security concern is that, since create3 deployment is deterministic, an attacker can frontrun Centrifuge’s official deployment on new chains and occupy that address. If this happens, the “contract address stays the same across all chains” assumption will be broken.
Description:
In Centrifuge deploy scripts, all contracts are deployed using the create3() function. This is essentially a wrapper around pcaversaccio's CreateX project; the function being called is deployCreate3().
Across all the chains, CreateX is expected to be deployed at 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed. The entire deploy chain is:
- User calls createx-forge (the wrapper)
create3()function - The wrapper interacts with CreateX deployed at
0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed - CreateX
deployCreate3()function deploys a proxy contract using CREATE2 opcode. The factor that determines where the proxy is deployed isguardedSalt; the logic is implemented in an internal function_guard() - The proxy bytecode embeds CREATE opcode in it. CreateX sends the initCode of the actual contract being deployed to this proxy and lets the proxy deploy it. In other words, the proxy is the deployer, so the predicted address of the actual contract depends on the address of the proxy (the deployer).
How the salt should be formatted is defined in _parseSalt(). In particular, we care about the address(bytes20(salt)) == msg.sender check: this check must pass so that the permissioned deployment protection will be enabled. In other words, the first 20 bytes of salt must be msg.sender.
Currently, the deploy scripts are using salt generated from the contract name and version, so the permissioned deployment protection is disabled.
Impact:
Medium. When Centrifuge expands to new chains using this deployment script, an attacker could frontrun the transaction with identical data to occupy the expected addresses. While highly unlikely, current deployments may also have been vulnerable to such attacks, though any successful frontrunning attempt should have triggered a revert in the deployment script.
Recommendation:
Set the first 20 bytes of each salt to msg.sender to turn on CreateX built-in permissioned deployment protection.
Developer Response:
The Centrifuge team has verified that all deployed addresses on mainnet were deployed by our main deployment wallet and have not been frontrun, and all parameters have been tested using fork tests to ensure they are the intended parameters.
Fixed in PR 573.