Tadle

Tadle
DeFiFoundry
27,750 USDC
View results
Submission Details
Severity: medium
Invalid

Create2 Address Collision

Summary

The TadleFactory factory function deployUpgradeableProxy() deploys a new related contract for the protocol using CREATE2. We show that a meet-in-the-middle attack at finding an address collision against an undeployed account is possible. This attack could further lead to an attacker draining funds from accounts deployed at these addresses

Description

The attack consists of two parts: Finding a collision, and actually draining the lending pool. We describe both here:

NOte that in deployUpgradeableProxy(), the _logic and the _data are both admin supplied.

code snippet

/// @dev deploy proxy
UpgradeableProxy _proxy = new UpgradeableProxy(
_logic,
guardian,
address(this),
_data
);

The address collision an attacker will need to find are:

  • One undeployed Arcadia account address (1).

  • Arbitrary attacker-controlled wallet contract (2).

Both sets of addresses can be brute-force searched because:

  • As shown above, both _logic and the _data can be brute-forced and by doing so we have obtained many different (undeployed) wallet accounts for (1).

  • (2) can be searched the same way. The contract just has to be deployed using CREATE2.
    An attacker can find any single address collision between (1) and (2) with high probability of success using the following meet-in-the-middle technique, a classic brute-force-based attack in cryptography:

  • Brute-force a sufficient number of values of salt ( 2^80), pre-compute the resulting account addresses, and efficiently store them e.g. in a Bloom filter data structure.

  • Brute-force contract pre-computation to find a collision with any address within the stored set in step 1.

The feasibility, as well as detailed technique and hardware requirements of finding a collision, are sufficiently described in multiple references:

The hashrate of the BTC network has reached 6.5 x 10^20 hashes per second as of time of writing, taking only just 31 minutes to achieve 2^80 hashes. A fraction of this computing power will still easily find a collision in a reasonably short timeline.

PoC: Setting Allowances

function createAccount(bytes calldata initData, bytes32 salt) external payable override returns (address payable) {
// Compute the actual salt for deterministic deployment
bytes32 actualSalt;
assembly {
let ptr := mload(0x40)
let calldataLength := sub(calldatasize(), 0x04)
mstore(0x40, add(ptr, calldataLength))
calldatacopy(ptr, 0x04, calldataLength)
actualSalt := keccak256(ptr, calldataLength)
}
// Deploy the account using the deterministic address
(bool alreadyDeployed, address account) = LibClone.createDeterministicERC1967(msg.value, ACCOUNT_IMPLEMENTATION, actualSalt);
if (!alreadyDeployed) {
INexus(account).initializeAccount(initData);
emit AccountCreated(account, initData, salt);
}
return payable(account);
}

Impact

Could lead to loss of funds

Mitigation

Consider also adding and encoding block.timestamp and block.number combined with the user's salt. Then the attacker, after they successfully found a hash collision, already has to execute the attack at a fixed block and probably conspire with the sequencer to ensure that also the time is fixed.

Updates

Lead Judging Commences

0xnevi Lead Judge
about 1 year ago
0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.