Sparkn

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

Any attacker can grief the contest reward tokens

Summary

Any attacker can grief the contest reward tokens.

Vulnerability Details

  • The flow of the contest creation by Sparkin protocol:

    1. first the contract owner adds a contest by setContest function.

    2. then when the contest ends (saltToCloseTime[salt] <= block.timestamp); the contest organizer can deploy the contest proxy and distribute rewards to the winners.

    3. but before step#2: the contest sponsor transfers reward tokens to the to-be-deployed proxy address; and this address is predicted by the getProxyAddress, where the function arguments are salt (which is generated by encoding the organizer address,proxyFactory address & contestId ) & the implementation (Distributor.sol) address.

  • But as can be noticed; any malicious actor can deploy the contest proxy on the same address before the contest organizer deploys it.

Impact

This will result in contest reward tokens being stuck/blocked in the proxy contract that has been deployed by the malicious actor (attacker) since deployProxyAndDistribute function will always revert as it tries to deploy contest proxy on the previously deployed proxy address by the attacker.

Proof of Concept

getProxyAddress function:

File: 2023-08-sparkn/src/ProxyFactory.sol
Line 225-229:
function getProxyAddress(bytes32 salt, address implementation) public view returns (address proxy) {
bytes memory code = abi.encodePacked(type(Proxy).creationCode, uint256(uint160(implementation)));
bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(code)));
proxy = address(uint160(uint256(hash)));
}

deployProxyAndDistribute function:

File: 2023-08-sparkn/src/ProxyFactory.sol
Line 127-138:
function deployProxyAndDistribute(bytes32 contestId, address implementation, bytes calldata data)
public
returns (address)
{
bytes32 salt = _calculateSalt(msg.sender, contestId, implementation);
if (saltToCloseTime[salt] == 0) revert ProxyFactory__ContestIsNotRegistered();
// can set close time to current time and end it immediately if organizer wish
if (saltToCloseTime[salt] > block.timestamp) revert ProxyFactory__ContestIsNotClosed();
// @audit : will revert if a malicious actor deployed on the same address
address proxy = _deployProxy(msg.sender, contestId, implementation);
_distribute(proxy, data);
return proxy;
}

_deployProxy function:

File: 2023-08-sparkn/src/ProxyFactory.sol
Line 239-243:
function _deployProxy(address organizer, bytes32 contestId, address implementation) internal returns (address) {
bytes32 salt = _calculateSalt(organizer, contestId, implementation);
address proxy = address(new Proxy{salt: salt}(implementation));
return proxy;
}

Tools Used

Manual Testing.

Recommendations

Add a mechanism that enables sponsors from funding contests by transferring rewards tokens to the ProxyFactory contract (will act as an escrow); then each rewards tokens are transferred to its intended contest when the contest organizer calls deployProxyAndDistribute.

Support

FAQs

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