Skip to content

Latest commit

 

History

History
85 lines (62 loc) · 2.96 KB

File metadata and controls

85 lines (62 loc) · 2.96 KB

Flat Watermelon Terrier

High

Cross-chain replay attack vulnerability

Summary

Signatures needed to execute a transaction can be reused on another chain if several conditions are satisfied

Root Cause

https://github.com/sherlock-audit/2024-11-hats-protocol/blob/main/hats-zodiac/src/HatsSignerGate.sol#L644-L684

The HatsSignerGate.sol::_countValidSignatures to check signatures validity uses ecrecover function that is subject to cross-chain replay attack.

  function _countValidSignatures(bytes32 dataHash, bytes memory signatures, uint256 sigCount)
    internal
    view
    returns (uint256 validSigCount)
  {
    // There cannot be an owner with address 0.
    address currentOwner;
    uint8 v;
    bytes32 r;
    bytes32 s;
    uint256 i;

    for (i; i < sigCount; ++i) {
      (v, r, s) = signatureSplit(signatures, i);
      if (v == 0) {
        // If v is 0 then it is a contract signature
        // When handling contract signatures the address of the contract is encoded into r
        currentOwner = address(uint160(uint256(r)));
      } else if (v == 1) {
        // If v is 1 then it is an approved hash
        // When handling approved hashes the address of the approver is encoded into r
        currentOwner = address(uint160(uint256(r)));
      } else if (v > 30) {
        // If v > 30 then default va (27,28) has been adjusted for eth_sign flow
        // To support eth_sign and similar we adjust v and hash the messageHash with the Ethereum message prefix before
        // applying ecrecover
        currentOwner = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
      } else {
        // Default is the ecrecover flow with the provided data hash
        // Use ecrecover with the messageHash for EOA signatures
@>        currentOwner = ecrecover(dataHash, v, r, s);
      }

      if (isValidSigner(currentOwner)) {
        // shouldn't overflow given reasonable sigCount
        unchecked {
          ++validSigCount;
        }
      }
    }
  }

Internal pre-conditions

Safe.sol, HatsSignerGate.sol deployed on two blockchains so that: Safe.nonce() chain A > Safe.nonce() chain B and the signatures have been already used in a transaction in chain A

External pre-conditions

An attacker that takes signatures through the block explorer and use them in another chain (aforementioned chain B) when the Safe.nonce() value will be equal to the one used in chain A

Attack Path

The attacker could get from a block explorer the signatures used on one chain and replay them on another chain that has a lower nonce value when it will be equal to the nonce used in the OG transaction (first chain).

Impact

Permissions to execute a transaction coming from the Safe contract can be bypassed replaying signatures already used in another transaction on a different chain when the Safe.nonce() values are the same.

PoC

No response

Mitigation

Use ECDSA library functions