Summary
Without the length check, the contract may accept Merkle proofs of incorrect lengths. This could allow attackers to exploit vulnerabilities in the verification process and claim tokens with invalid or forged proofs.
Vulnerability Details
In the following function there is no verification of the lengnth of the merkle proof. Allowing attackers to submit invalid proofs or forged proofs of varying lengths.
function pick(
address token,
uint256 amount,
bytes32[] memory proof,
LibTransfer.To mode
) external payable nonReentrant {
bytes32 root = s.u[token].merkleRoot;
require(root != bytes32(0), "UnripeClaim: invalid token");
require(!picked(msg.sender, token), "UnripeClaim: already picked");
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
require(MerkleProof.verify(proof, root, leaf), "UnripeClaim: invalid proof");
s.unripeClaimed[token][msg.sender] = true;
LibTransfer.sendToken(IERC20(token), amount, msg.sender, mode);
emit Pick(msg.sender, token, amount);
}
Impact
Function can allow forged proofs submitted that can potentially lead to loss of funds.
Potential Denial-of-Service (DoS), an attacker could submit a very large or malformed proof to consume gas and block execution.
Tools Used
Manual Review
Recommendations
Introduce a check to verify length of the merkle proof:
function pick(
address token,
uint256 amount,
bytes32[] memory proof,
LibTransfer.To mode
) external payable nonReentrant {
bytes32 root = s.u[token].merkleRoot;
require(root != bytes32(0), "UnripeClaim: invalid token");
require(!picked(msg.sender, token), "UnripeClaim: already picked");
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
uint256 expectedProofLength = calculateExpectedProofLength();
require(proof.length == expectedProofLength, "UnripeClaim: invalid proof length");
require(MerkleProof.verify(proof, root, leaf), "UnripeClaim: invalid proof");
s.unripeClaimed[token][msg.sender] = true;
LibTransfer.sendToken(IERC20(token), amount, msg.sender, mode);
emit Pick(msg.sender, token, amount);
}
and a function that handles the calculation of expected proof.
function calculateExpectedProofLength(bytes32[] memory proof) internal pure returns (bool) {
uint256 len = proof.length;
if (len < 1) {
return false;
}
uint256 maxLength = 256;
if (len > maxLength) {
return false;
}
return true;
}