Summary
The proxy forwards all requests to the distributor using delegatecall, but it's never checked whether the distributor address actually exists or possesses code. If that's not the case, delegatecall still returns True, and it's assumed that everything is functioning correctly.
Vulnerability Details
Here's a PoC demonstrating that even if the distributor address doesn't exist, there is no revert, and it's assumed that everything has worked correctly. (Test can be added in test/integration/ProxyFactoryTest.t.sol)
function testNoRevertIfDistributorNotExist() public {
address wrongDistributorAddress = 0x29D7d1dd5B6f9C864d9db560D72a247c178aE86B;
vm.startPrank(factoryAdmin);
bytes32 randomId = keccak256(abi.encode("Jason", "001"));
proxyFactory.setContest(organizer, randomId, block.timestamp + 8 days, wrongDistributorAddress);
vm.stopPrank();
bytes32 salt = keccak256(abi.encode(organizer, randomId, wrongDistributorAddress));
address proxyAddress = proxyFactory.getProxyAddress(salt, wrongDistributorAddress);
vm.startPrank(sponsor);
MockERC20(jpycv2Address).transfer(proxyAddress, 10000 ether);
vm.stopPrank();
bytes memory data = createData();
assertEq(MockERC20(jpycv2Address).balanceOf(user1), 0);
assertEq(MockERC20(jpycv2Address).balanceOf(stadiumAddress), 0);
vm.warp(9 days);
vm.startPrank(organizer);
proxyFactory.deployProxyAndDistribute(randomId, wrongDistributorAddress, data);
vm.stopPrank();
assertEq(MockERC20(jpycv2Address).balanceOf(user1), 0);
assertEq(MockERC20(jpycv2Address).balanceOf(stadiumAddress), 0);
}
Impact
Events are being emitted stating that the tokens have been distributed, even though it hasn't actually worked. Systems tracking these events might potentially be 'confused'
Tools Used
Manual Review, VSCode
Recommendations
Add a function to ProxyFactory that checks for code presence or existence of the Distributor Address when setting up the Contest.
function isContract(address _addr) private returns (bool isContract){
uint32 size;
assembly {
size := extcodesize(_addr)
}
return (size > 0);
}
Add this line to the setContest function:
if(!isContract(implementation)) revert ProxyFactory__ImplementationNotExists();
If you run the PoC now, there should be a revert with the custom error.