sherlock-audit / 2024-08-winnables-raffles-judging

6 stars 2 forks source link

Rich Marigold Swan - [Info] ChainId is not used in signatures, which could lead to replay attacks if WinnablesTicketManager #635

Closed sherlock-admin4 closed 3 months ago

sherlock-admin4 commented 3 months ago

Rich Marigold Swan

Low/Info

[Info] ChainId is not used in signatures, which could lead to replay attacks if WinnablesTicketManager

Summary

ChainId is not used in signatures, which could lead to replay attacks in WinnablesTicketManager when buying tickets.

Vulnerability Detail

When buying a ticket, the offline generated signature is checked and verified in order to let users buy the tickets they wanted to. But in the signature verification chainId is not included which can lead to replay attacks if the WinnablesTicketManager is deployed on another network and the raffleId on one network matches another raffleId on the other network.

Impact

This is low/info as currently the WinnablesTicketManager is supposed to be deploed only on Avalanche and furthermore, there is a blockNumber included before which the tickets can be bought, which further limits the attack.

Code Snippet

function _checkPurchaseSig(uint256 raffleId, uint16 ticketCount, uint256 blockNumber, bytes calldata signature) internal view {
        if (blockNumber < block.number) revert ExpiredCoupon();
        address signer = _getSigner(
            keccak256(
                abi.encodePacked(
                    msg.sender, _userNonces[msg.sender], raffleId, ticketCount, blockNumber, msg.value
                )
            ), signature
        );
        if (!_hasRole(signer, 1)) revert Unauthorized();
    }

Tool used

Manual Review

Recommendation

Add chainId to the offline signature and include it in the verification of the signature also:

function _checkPurchaseSig(uint256 raffleId, uint16 ticketCount, uint256 blockNumber, bytes calldata signature) internal view {
        if (blockNumber < block.number) revert ExpiredCoupon();
        address signer = _getSigner(
            keccak256(
                abi.encodePacked(
                    msg.sender, _userNonces[msg.sender], raffleId, ticketCount, blockNumber, msg.value, block.chainid
                )
            ), signature
        );
        if (!_hasRole(signer, 1)) revert Unauthorized();
    }