Sparkn

CodeFox Inc.
DeFiFoundryProxy
15,000 USDC
View results
Submission Details
Severity: high
Valid

Owner can move the tokens of an already deployed proxy before expiry

Summary

Lack of Proxy address validation in the distributeByOwner() function enables the owner to move the funds of a contest before expiry

Vulnerability Details

The Owner is supposed to move the funds of a contest only after its expiry.
The distributeByOwner function tries to enforce the expiration check of a contest by checking the expiration of the salt computed from the organizer, contestId and implementation parameters. But the proxy address from which the funds will be distributed is not validated to be linked with the same contest.

function distributeByOwner(
address proxy,
address organizer,
bytes32 contestId,
address implementation,
bytes calldata data
) public onlyOwner {
if (proxy == address(0)) revert ProxyFactory__ProxyAddressCannotBeZero();
bytes32 salt = _calculateSalt(organizer, contestId, implementation);
if (saltToCloseTime[salt] == 0) revert ProxyFactory__ContestIsNotRegistered();
// distribute only when it exists and expired
if (saltToCloseTime[salt] + EXPIRATION_TIME > block.timestamp) revert ProxyFactory__ContestIsNotExpired();
_distribute(proxy, data);
}

This allows the owner to move funds from a closed contest for which the proxy has been deployed by calling the distributeByOwner() function with the organizer, contestId and implementation parameters of an expired contest and the proxy address of the closed contest.

Impact

Breaking project assumptions.

Tools Used

Manual review

Recommendations

Compute proxy address from the salt instead of using an input.

function distributeByOwner(
// address proxy,
address organizer,
bytes32 contestId,
address implementation,
bytes calldata data
) public onlyOwner {
//if (proxy == address(0)) revert ProxyFactory__ProxyAddressCannotBeZero();
bytes32 salt = _calculateSalt(organizer, contestId, implementation);
if (saltToCloseTime[salt] == 0) revert ProxyFactory__ContestIsNotRegistered();
// distribute only when it exists and expired
if (saltToCloseTime[salt] + EXPIRATION_TIME > block.timestamp) revert ProxyFactory__ContestIsNotExpired();
address proxy = getProxyAddress(salt, implementation);
_distribute(proxy, data);
}

Support

FAQs

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