Line of code
https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/df2dd129a878d16d4adc75049179ac0029d9a96b/protocol/contracts/beanstalk/silo/L2ContractMigrationFacet.sol#L88
Summary
L2Migration for Silo signature can be replayed
Vulnerability Details
function redeemDepositsAndInternalBalances(
address owner,
address reciever,
AccountDepositData[] calldata deposits,
AccountInternalBalance[] calldata internalBalances,
uint256 ownerRoots,
bytes32[] calldata proof,
uint256 deadline,
bytes calldata signature
) external payable fundsSafu noSupplyChange nonReentrant {
verifyDepositsAndInternalBalances(owner, deposits, internalBalances, ownerRoots, proof);
verifySignature(owner, reciever, deadline, signature);
uint256 accountStalk;
for (uint256 i; i < deposits.length; i++) {
accountStalk += addMigratedDepositsToAccount(reciever, deposits[i]);
}
setStalk(reciever, accountStalk, ownerRoots);
}
When depositing in the migration faucet, the deposit info must be in the merkle root and user signature must be valid.
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 function above will verify that the info is included in the merkle root. The problem is that the signature and proof is never checked if proof and signature is already used.
Becase of this the signature and merkle proof can be used more than once and replayed for deposits in the migration contract
Impact
Becase of this the signature and merkle proof can be used more than once and replayed for deposits in the migration contract to get the balance multiple times.
Tools Used
manual review
Recommendations
the code should validate that the signature cannot be replayed and the user should be only add the balance once (check if the data is in merkle root and if the user balance is alread added.)