Sparkn

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

Owner can deploy the proxy and distribute before the expiration date

Summary

The owner can bypass the expiration date by acting as an organizer

Vulnerability Details

In setContest & deployProxyAndDistribute there is no check on msg.sender, so essentially after a contest is created where owner = organizer msg.sender can be the owner and deploy a proxy and distribute funds before expiration ends.

Steps:

  • Owner calls setContest with organiser as owner's address.

  • We know that owner can deploy and distribute only after EXPIRATION_TIME is over.
    https://github.com/Cyfrin/2023-08-sparkn/blob/0f139b2dc53905700dd29a01451b330f829653e9/src/ProxyFactory.sol#L187

  • But owner can bypass this check if they call deployProxyAndDistribute function directly as they are the organiser too.

https://github.com/Cyfrin/2023-08-sparkn/blob/0f139b2dc53905700dd29a01451b330f829653e9/src/ProxyFactory.sol#L127

Impact

Invariant that owner can distribute only after EXPIRATION_TIME is over is broken. Also, it increases unnecessary centralization.

POC

Run forge test --mt testIfOwnerCanDeployAndDistributeFundsBeforeExpiryEnds -vvv

function testIfOwnerCanDeployAndDistributeFundsBeforeExpiryEnds() public {
// before
assertEq(MockERC20(jpycv2Address).balanceOf(user1), 0 ether);
assertEq(MockERC20(jpycv2Address).balanceOf(stadiumAddress), 0 ether);
bytes32 randomId_ = keccak256(abi.encode("Jason", "001"));
address[] memory tokens_ = new address[](1);
tokens_[0] = jpycv2Address;
address[] memory winners = new address[](1);
winners[0] = user1;
uint256[] memory percentages_ = new uint256[](1);
percentages_[0] = 9500;
bytes memory data = abi.encodeWithSelector(Distributor.distribute.selector, jpycv2Address, winners, percentages_, "");
vm.startPrank(factoryAdmin);
bytes32 randomId = keccak256(abi.encode("Jason", "001"));
proxyFactory.setContest(factoryAdmin, randomId, block.timestamp + 8 days, address(distributor));
vm.stopPrank();
bytes32 salt = keccak256(abi.encode(factoryAdmin, randomId, address(distributor)));
address proxyAddress = proxyFactory.getProxyAddress(salt, address(distributor));
vm.startPrank(sponsor);
MockERC20(jpycv2Address).transfer(proxyAddress, 10000 ether);
vm.stopPrank();
// console.log(MockERC20(jpycv2Address).balanceOf(proxyAddress));
assertEq(MockERC20(jpycv2Address).balanceOf(proxyAddress), 10000 ether);
vm.warp(9 days);
vm.startPrank(factoryAdmin);
proxyFactory.deployProxyAndDistribute(randomId_, address(distributor), data);
vm.stopPrank();
console.log("balance of user after distribute",MockERC20(jpycv2Address).balanceOf(user1));
// after
assertEq(MockERC20(jpycv2Address).balanceOf(user1), 9500 ether);
assertEq(MockERC20(jpycv2Address).balanceOf(stadiumAddress), 500 ether);
}

Tools Used

Manual

Recommendations

In setContest, check that organiser != owner

Support

FAQs

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