DeFiHardhatFoundry
250,000 USDC
View results
Submission Details
Severity: medium
Invalid

Potential Second-Preimage Attack in Merkle Tree Verification.

Summary

First Take look at this code: https://github.com/Cyfrin/2024-05-Sablier/blob/main/v2-periphery/src/SablierV2MerkleLL.sol#L69-L71

A potential second-preimage attack vulnerability in the verifyDepositsAndInternalBalances function. The vulnerability exists if the combined size of encoded data (account, deposits, internalBalances, and ownerRoots) can reach 64 bytes. This scenario allows an attacker to forge a fake Merkle proof.

Vulnerability Details

file: https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/main/protocol/contracts/beanstalk/silo/L2ContractMigrationFacet.sol#L120-L129

The verifyDepositsAndInternalBalances function utilizes a Merkle tree for verification. Merkle trees are cryptographic data structures
that allow efficient verification of the integrity of a piece of data. However, a second-preimage attack can exploit weaknesses
in how leaves are hashed within the Merkle tree. The function is implemented as follows:

function verifyDepositsAndInternalBalances(
address account,
AccountDepositData[] calldata deposits,
AccountInternalBalance[] calldata internalBalances,
uint256 ownerRoots,
bytes32[] calldata proof
) internal pure {
bytes32 leaf = keccak256(abi.encode(account, deposits, internalBalances, ownerRoots));
require(MerkleProof.verify(proof, MERKLE_ROOT, leaf), "Migration: invalid proof");
}

The vulnerability arises because the code uses keccak256 to create a single 32-byte leaf bytes32 from potentially more than 32 bytes of encoded data.
If the combined size of the encoded data reaches 64 bytes, an attacker could potentially Forge a Fake Leaf.

Impact

An attacker might be able to forge a Merkle proof that bypasses validation.

Tools Used

Recommendations

Double Hashing for Leaf Nodes:
Implement a double hashing mechanism for leaf nodes. This approach, used by OpenZeppelin, involves hashing the leaf node twice to prevent second preimage attacks:

file: https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/main/protocol/contracts/beanstalk/silo/L2ContractMigrationFacet.sol#L120-L129

function verifyDepositsAndInternalBalances(
address account,
AccountDepositData[] calldata deposits,
AccountInternalBalance[] calldata internalBalances,
uint256 ownerRoots,
bytes32[] calldata proof
) internal pure {
- bytes32 leaf = keccak256(abi.encode(account, deposits, internalBalances, ownerRoots));
+ bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(account, deposits, internalBalances, ownerRoots))));
require(MerkleProof.verify(proof, MERKLE_ROOT, leaf), "Migration: invalid proof");
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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