code4rena-2023-10-brahma-m03

[M-03] Protocol is not EIP712 compliant: incorrect typehash for Validation and Transaction structures

보고서

Summary

미리 계산된 typeHash의 값이 잘못되었다. 이때문에 ERC712를 준수하지 않아 통합에 문제가 발생할 수 있다.

Keyword

erc712

Vulnerability

ERC712을 구현할 때, 서명 메시지의 일부가 될 데이터는 타입 해시에 포함되어야 한다.

EIP-712 스펙에 따르면 타입 해시는 typeHash = keccak256(encodeType(typeOf(s))) 로 정의되고, encodeTypename ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")" 로 정의된다. 각 membertype ‖ " " ‖ name 로 정의된다. 예를 들어 Mail 이라는 구조체에 서명을 한다면, typeHash는 Mail(address from,address to,string contents) 가 된다. 만약 구조체 내에서 또다른 구조체를 사용한다면 Transaction(Person from,Person to,Asset tx)Asset(address token,uint256 amount)Person(address wallet,string name) 와 사용하는 구조체까지 typeHash에 포함한다.

Validator는 ERC712를 기반으로 Validation 구조체의 데이터에 서명을 한다. 이는 uint32 expiryEpoch, bytes32 transactionStructHash, bytes32 policyHash 로 이루어져 있다. transactionStructHashTransaction 구조체에 대한 hashStruct(hashStruct(s : 𝕊) = keccak256(typeHash ‖ encodeData(s)))이다.

    /**
     * @notice Structural representation of transaction details
     * @param operation type of operation
     * @param to address to send tx to
     * @param account address of safe
     * @param executor address of executor if executed via executor plugin, address(0) if executed via execTransaction
     * @param value txn value
     * @param nonce txn nonce
     * @param data txn callData
     */
    struct Transaction {
        uint8 operation;
        address to;
        address account;
        address executor;
        uint256 value;
        uint256 nonce;
        bytes data;
    }
 
    /**
     * @notice Type of validation struct to hash
     * @param expiryEpoch max time till validity of the signature
     * @param transactionStructHash txn digest generated using TypeHashHelper._buildTransactionStructHash()
     * @param policyHash policy commit hash of the safe account
     */
    struct Validation {
@>      uint32 expiryEpoch;
@>      bytes32 transactionStructHash; // @audit-info Transaction 의 해시값(_buildTransactionStructHash 함수로 생성)
@>      bytes32 policyHash;
    }

transactionStructHash, 즉 Transaction 구조체에 대한 hashStruct을 작성하는 데 사용하는 typeHash와 Validation에 대한 hashStruct을 작성하는 데 사용하는 typeHash를 미리 계산하여 상수로 정의한다.

주석에 명시한 대로 Transaction 구조체에 대한 타입 해시(TRANSACTION_PARAMS_TYPEHASH)로는 ExecutionParams(address to,uint256 value,bytes data,uint8 operation,address account,address executor,uint256 nonce) 를 해시한 값을 사용한다. Validation 구조체에 대한 타입 해시(VALIDATION_PARAMS_TYPEHASH)로는 ValidationParams(ExecutionParams executionParams,bytes32 policyHash,uint32 expiryEpoch)ExecutionParams(address to,uint256 value,bytes data,uint8 operation,address account,address executor,uint256 nonce) 를 해시한 값을 사용한다.

하지만 해시한 값과 실제 구조체의 구조가 동일하지 않다. ExecutionParams 라는 구조체는 더이상 존재하지 않으며, 위에서 본 구조체인 Transaction 로 이름이 변경되었다. 하지만 미리 계산된 typeHash에는 이것이 적용되지 않았다. 동일하게 ValidationParamsValidation 로 이름이 변경되었다. 또한 Validation 에는 더이상 ExecutionParams 구조체가 직접 들어가지 않으며, Transaction의 해시값(bytes32)이 들어가야 한다.

    /**
     * @notice EIP712 typehash for transaction data
@>   * @dev keccak256("ExecutionParams(address to,uint256 value,bytes data,uint8 operation,address account,address executor,uint256 nonce)");
     */
    bytes32 public constant TRANSACTION_PARAMS_TYPEHASH =
        0xfd4628b53a91b366f1977138e2eda53b93c8f5cc74bda8440f108d7da1e99290;
 
    /**
     * @notice EIP712 typehash for validation data
@>   * @dev keccak256("ValidationParams(ExecutionParams executionParams,bytes32 policyHash,uint32 expiryEpoch)ExecutionParams(address to,uint256 value,bytes data,uint8 operation,address account,address executor,uint256 nonce)")
     */
    bytes32 public constant VALIDATION_PARAMS_TYPEHASH =
        0x0c7f653e0f641e41fbb4ed1440c7d0b08b8d2a19e1c35cfc98de2d47519e15b1;

Impact

미리 계산된 typeHash의 값이 잘못되어 ERC712를 준수하지 않아 통합에 문제가 발생할 수 있다.

Mitigation

실제 구조체와 일치하는 typeHash로 변경한다.


tags: bughunting, brahma, smart contract, solidity, erc712, severity medium