Look at the next code snippet and tell me two possible problems:
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 {
// verify deposits are valid.
// note: if the number of contracts that own deposits is small,
// deposits can be stored in bytecode rather than relying on a merkle tree.
verifyDepositsAndInternalBalances(owner, deposits, internalBalances, ownerRoots, proof);
// signature verification.
verifySignature(owner, reciever, deadline, signature);
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");
}
function verifySignature(
address owner,
address reciever,
uint256 deadline,
bytes calldata signature
) internal view {
require(block.timestamp <= deadline, "Migration: permit expired deadline");
bytes32 structHash = keccak256(
abi.encode(REDEEM_DEPOSIT_TYPE_HASH, owner, reciever, deadline)
);
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(hash, signature);
require(signer == owner, "Migration: permit invalid signature");
}
in redeemDepositsAndInternalBalances there is no validation about the parameters that have been used which should be stored and should not be reused.
In verifyDepositsAndInternalBalances, leaf is not stored, and there are no checks to ensure that `leaf` is only used once.
This means that parameters that have been filled in verifyDepositsAndInternalBalances and succeeded, can be reused.
And verifySignature also has no checks to ensure that the signature is only used once.
verifySignature prevents replay attacks by relying solely on deadline, which is bad.
Even if block.timestamp reaches the deadline or is smaller than block.timestamp, the owner can sign again to execute a replay attack. This is because verifyDepositsAndInternalBalances does not prevent replay attacks and there is no access control on redeemDepositsAndInternalBalances.
The vulnerabilities present in the verifyDepositAndInternalBalance and verifySignature functions make replay attacks unavoidable.
Read the full report here:
Link: https://codehawks.cyfrin.io/c/2024-05-beanstalk-the-finale/results/?lt=contest&page=1&sc=reward&sj=reward&t=report#merkle
#signature
#replay
Completely free courses
Learn more about the blockchain world
Free education videos
by RareSkills
by Jeiwan
by RareSkills
by RareSkills
by Andreas M. Antonopoulos, Gavin Wood
by Micah Dameron
Compare execution layer differences between chains
Dive deep into the storage of any contract