Sparkn

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

'data' param on _distribute should check in the first bytes if the function selector is distribute

Summary

The internal function ProxyFactory::_distribute, which is publically called by 5 functions, has as a parameter bytes calldata data, which the protocol assumes will be called with the function selector of the Distributor::distribute function.

Vulnerability Details

Whoever, a call to for example Distributor::getConstants will pass.

// Add this to tests/integration/ProxyTest.t.sol
// Then run this: $ forge test --mt test_distributePhantomFunctionCheck
function test_distributePhantomFunctionCheck() setUpContestForNameAndSentAmountToken("James", jpycv2Address, 10000 ether) public {
bytes32 randomId_ = keccak256(abi.encode("James", "001"));
bytes memory data = abi.encodeWithSelector(Distributor.getConstants.selector);
vm.startPrank(organizer);
deployedProxy = proxyFactory.deployProxyAndDistribute(randomId_, address(distributor), data);
vm.stopPrank();
}

Impact

A call to for example Distributor::getConstants will pass, emitting the ProxyFactory::Distributed event, which can cause problems in the indexing and/or logs tracing understanding of how the contract is behaving.

Tools Used

Manual review and Foundry.

Recommendations

Add the following require

function _distribute(address proxy, bytes calldata data) internal {
(bool success,) = proxy.call(data);
if (!success) revert ProxyFactory__DelegateCallFailed();
+ require(bytes4(data[:4]) == 0x3cc83ebe, "Wrong function selector");
emit Distributed(proxy, data);
}

POC of Recommendation:

// first test that with the anterior POC it no longer works
// then replace the anterior function and copy this to the same place
function test_distributePhantomFunctionCheck() setUpContestForNameAndSentAmountToken("James", jpycv2Address, 10000 ether) public {
bytes32 randomId_ = keccak256(abi.encode("James", "001"));
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(organizer);
deployedProxy = proxyFactory.deployProxyAndDistribute(randomId_, address(distributor), data);
vm.stopPrank();
}
// it correctly checks that the function selector is correct

No other call which is not directed to Distributor::distribute is possible to the contract, all others will revert.

Support

FAQs

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