code-423n4 / 2024-07-reserve-validation

0 stars 0 forks source link

delegatee able to use signer signature for replay (Cross chain signature replay) #176

Closed c4-bot-4 closed 1 month ago

c4-bot-4 commented 1 month ago

Lines of code

https://github.com/code-423n4/2024-07-reserve/blob/main/contracts/p1/StRSRVotes.sol#L176

Vulnerability details

Vulnerability details

The delegateBySig function is part of a contract that allows for delegation of voting power using off-chain signed messages. But here malicious delegatee able to re use the signer signature for other chain also. Past occurrences of this issue: https://solodit.xyz/issues/cross-chain-replay-attack-vulnerability-in-beanstalks-l2contractmigrationfacet-codehawks-beanstalk-the-finale-git

Proof of Concept

Implementation of delegateBySig function as follows. Here its not used block.chainid for the hashing. So cross chain replay is possible.


        function delegateBySig(
            address delegatee,
            uint256 nonce,
            uint256 expiry,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public {
            require(block.timestamp <= expiry, "signature expired");
            address signer = ECDSAUpgradeable.recover(
                _hashTypedDataV4(keccak256(abi.encode(_DELEGATE_TYPEHASH, delegatee, nonce, expiry))),
                v,
                r,
                s
            );
            require(nonce == _useDelegationNonce(signer), "invalid nonce");
            _delegate(signer, delegatee);
        }

Another instances of this issue on permit function in the StRSR.sol as well.

Impact

A malicious user could exploit the vulnerability by reusing the signer's signature to transfer voting power to themselves, potentially leading to a governance attack.

Tools Used

Manual Review

Recommended Mitigation Steps

Use block.chainid in the hash creation.


            function delegateBySig(
                    address delegatee,
                    uint256 nonce,
                    uint256 expiry,
                    uint8 v,
                    bytes32 r,
                    bytes32 s
                ) public {
                    require(block.timestamp <= expiry, "signature expired");
                    address signer = ECDSAUpgradeable.recover(
                        _hashTypedDataV4(keccak256(abi.encode(_DELEGATE_TYPEHASH, delegatee, nonce, expiry,block.chainid))), //@auditfix
                        v,
                        r,
                        s
                    );
                    require(nonce == _useDelegationNonce(signer), "invalid nonce");
                    _delegate(signer, delegatee);
                }

Assessed type

Other

Yasashari commented 1 month ago

@thereksfour This is basically cross chain replay attack by malicious decelerator due to not adding block.chainid in hash creation. Past occurrences of this issue: https://solodit.xyz/issues/cross-chain-replay-attack-vulnerability-in-beanstalks-l2contractmigrationfacet-codehawks-beanstalk-the-finale-git

thereksfour commented 1 month ago

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol Check out the _hashTypedDataV4 implementation, where the chainID is already included