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))) 로 정의되고, encodeType 은 name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")" 로 정의된다. 각 member 는 type ‖ " " ‖ 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 로 이루어져 있다. transactionStructHash 는 Transaction 구조체에 대한 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에는 이것이 적용되지 않았다. 동일하게 ValidationParams는 Validation 로 이름이 변경되었다. 또한 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