code-423n4 / 2023-10-party-findings

6 stars 4 forks source link

Signature validation will not be performed due to condition always failing in some situations #441

Closed c4-submissions closed 1 year ago

c4-submissions commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-10-party/blob/main/contracts/proposals/ProposalExecutionEngine.sol#L234

Vulnerability details

Bug description

The Party protocol has added a functionality to allow Parties to sign messages via the ERC1271 standard (see more about this standard here).

In order to do so, the ProposalExecutionEngine.sol contract incorporates an isValidSignature() , which will allow third parties to verify if a signature is actually valid by querying the Party contract directly:

function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4) {
        IERC1271 validator = getSignatureValidatorForHash(hash);
        if (address(validator) == address(1)) {  // @audit this enables potential to signature replay 
            // Signature set by party to be always valid
            return IERC1271.isValidSignature.selector;
        }
        if (address(validator) != address(0)) {
            return validator.isValidSignature(hash, signature);
        }
        if (tx.origin == address(0)) { 
            validator = getSignatureValidatorForHash(0);
            if (address(validator) == address(0)) {
                // Use global off-chain signature validator
                validator = IERC1271(
                    _GLOBALS.getAddress(LibGlobals.GLOBAL_OFF_CHAIN_SIGNATURE_VALIDATOR)
                );
            }
            return validator.isValidSignature(hash, signature);
        }
        return 0;
    }

As we can see in the code snippet, isValidSignature() will query the validator for a given hash by calling the getSignatureValidatorForHash() function, and then it will perform several checks:

The issue described can lead to a signature that is valid (a signature that either is set as valid in the validator corresponding to the zero hash, or set as valid in the global off-chain signature validator) to never be evaluated as actually valid. This makes the Party contract not being able to validate signatures that might be valid, breaking the expected signature validation functionality.

Impact

Raising as medium due to the fact that although this vulnerability completely breaks the intended behavior for smart contract signature validation in some situations, the issue can be mitigated by setting a validator for that specific hash.

Proof of Concept

As described in the bug description section, it is not possible to execute a transaction where tx.origin == address(0) , effectively making the block of code corresponding to such condition never be executed:

function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4) {
        ...
        if (tx.origin == address(0)) { // <-- this will always be false!
            validator = getSignatureValidatorForHash(0);
            if (address(validator) == address(0)) {
                // Use global off-chain signature validator
                validator = IERC1271(
                    _GLOBALS.getAddress(LibGlobals.GLOBAL_OFF_CHAIN_SIGNATURE_VALIDATOR)
                );
            }
            return validator.isValidSignature(hash, signature);
        }
        return 0;
    }

Tools used

Manual review

Recommended Mitigation

Rethink how the signature validation should be performed in the fallback situation where getSignatureValidatorForHash(hash) returns address(0) for the given hash, not relying on tx.origin.

Assessed type

Other

ydspa commented 1 year ago

Misunderstanding of the usage of tx.origin == address(0)

Invalid

c4-pre-sort commented 1 year ago

ydspa marked the issue as insufficient quality report

c4-judge commented 1 year ago

gzeon-c4 marked the issue as unsatisfactory: Invalid