The permitDeposit and permitDeposits
functions of ApporveFacet utilizes EIP-2612 permits and EIP-712 for typed structured data hashing and signing. The vulnerability arises when a hard fork occurs, resulting in two parallel chains that retain the same chain ID. In such a scenario, a signature created on the pre-fork chain remains valid on both resulting chains, allowing malicious actors to reuse the signature to transfer funds on both chains.
1: Initial Setup on Mainnet:
Bob holds tokens worth $1,000 on the Ethereum mainnet.
Bob submits a signed permit allowing Eve to spend those tokens on his behalf using the permitDeposits
function.
2: Permit Generation:
Bob's wallet generates the permit using EIP-712 structured data hashing. The data includes:
owner
: Bob's address.
spender
: Eve's address.
tokens
: Array of token addresses.
values
: Array of values corresponding to the tokens.
nonce
: A nonce value specific to Bob's address.
deadline
: A timestamp until when the permit is valid.
The permit data is hashed using _hashTypedDataV4
, which includes the chain ID in the domain separator.
3:
Bob signs the hash with his private key, producing the signature components v
, r
, and s
.
4: Permit Submission:
Eve submits the permit to the either the permitDeposit or PermitDeposits on the Ethereum mainnet using the permitDeposits
function. The contract verifies the signature and sets the approval for Eve to transfer Bob's tokens.
5: Hard Fork:
The Ethereum mainnet undergoes a hard fork, resulting in two chains (Chain A and Chain B). Both chains retain the same chain ID (1).
6: Post-Fork Exploitation:
Since the chain ID is the same, the signature Bob generated before the fork is valid on both Chain A and Chain B.
Eve can submit the same permit on both Chain A and Chain B, allowing her to transfer Bob's tokens on both chains.
The primary impact of this vulnerability is that any signed permit allowing a spender to transfer tokens on behalf of an owner before the fork can be reused on both chains post-fork. This can lead to unintended double-spending or unauthorized transfers on one of the chains, compromising the security and integrity of the system.
Manual review
1: Replace LEGACY_CHAIN_ID with Dynamic Chain ID: Instead of using a constant LEGACY_CHAIN_ID, use the current chain's ID:
2: Add a Unique Network Identifier: Introduce a unique identifier for each network that's set at contract deployment:
3: Modify _domainSeparatorV4(): Update the function to include all these elements:
4: Consider Using OpenZeppelin's EIP712 Implementation: OpenZeppelin provides a well-tested implementation of EIP712 that includes many best practices:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.