Hidden Fossilized Dragonfly
High
The create function allows users to create vaults with specific parameters
function create(string memory _name, string memory _symbol, address _asset, uint96 _salt)
external
returns (address _vault)
{
These values are visible on the mempool and could be front-run by malicious actors.
The vulnerability arises due to the predictable nature of the salt parameter _salt
used in the CREATE2 address computation. Since _salt
is directly supplied by the caller and the function does not rely or use any dynamic, unpredictable factors (e.g., block.timestamp
, or msg.sender
an attacker can front run transactions to create()
with exact parameters and higher gas fees ultimately now in control of the vault.
function _getFullSalt(uint96 _salt) internal view returns (uint256) {
return uint256(uint160(address(this))) + _salt;
}
- Alice wants to deploy a vault using parameters
Doge
dog
0xAddressOfDogeToken
234678889
- Attacker sees Alice's transaction on the mempool
- Attacker sends a transaction with the exact same parameters (
Doge
dog
0xAddressOfDogeToken
234678889
) - Attacker now owns vault address
Front running transactions to create
can lead to various security risks
Funds Loss
– If users expect a vault at a known address, the attacker could deploy a malicious vault withselfdestruct
functionality , callselfdestruct()
replace deployed vault with malicious code which can be used to drain user funds.Stealing Alice’s Vault Name & Symbol
– The attacker can deploy a vault with the same branding(name and symbol), misleading users.DOS: Locking Out Legitimate Users
– If Alice intended to launch a vault at a specific CREATE2 address, she is now locked out of that address.
function testcreateFrontRunning() public {
address attacker = address(0x2);
string memory VAULT_NAME = "Doge Vault"; //Alice wants to create Doge Vault.
string memory VAULT_SYMBOL = "DOGE";
uint96 SALT = 6;
asset.transfer(attacker, 10000 * 10 ** 18);
address expectedVault = factory.getNewCaFromParams(VAULT_NAME, VAULT_SYMBOL, address(asset), SALT);
// Attacker front-runs the deployment
vm.startPrank(attacker);
asset.approve(address(factory), 1000);
address attackerVault = factory.create(VAULT_NAME, VAULT_SYMBOL, address(asset), SALT);
vm.stopPrank();
vm.startPrank(user);
vm.expectRevert(); //user can no longer create vault due to collision error.
factory.create(VAULT_NAME, VAULT_SYMBOL, address(asset), SALT);
vm.stopPrank();
assert(attackerVault == expectedVault );
}
Update _getFullSalt()
to use a dynamic factors during computation of _salt
such as block.timestamp
or msg.sender