The EIP712Base contract contains a mutable domain separator that could inadvertently be modified by inheriting contracts. Although the _setDomainSeperator function is internal and not directly accessible externally, exposing it in a derived contract or allowing it to be called more than once could result in the domain separator being altered post-deployment. This change could invalidate previously signed transactions, cause replay issues, and expose users to unexpected behaviors.
The domain separator in the EIP-712 standard is intended to provide a unique identity for the contract and domain-specific data to avoid cross-chain or cross-platform replay attacks. In the EIP712Base contract, the domainSeperator is set during construction via _setDomainSeperator but is not protected from being called again, allowing potential modification if exposed by derived contracts.
Since _setDomainSeperator is an internal function, any derived contract could call it to update domainSeperator. If domainSeperator is modified, any previously signed transactions with the old separator would become invalid. Furthermore, replay protection relies on a stable domain separator, and any unintended alteration could expose users to replay attacks or prevent them from executing expected transactions.
PoC:
Let's define a derived contract that exposes _setDomainSeperator, allowing us to simulate a domain separator change.
Using Foundry, we can deploy ExposedEIP712Base and verify the domainSeperator before and after calling changeDomainSeparator.
The test shows that the initial domain separator differs from the new domain separator after calling changeDomainSeparator, demonstrating the vulnerability.
Any user signatures that were signed with the old domain separator become invalid.
Modifying the domain separator can undermine the EIP-712's primary goal of protecting against cross-domain replay attacks, exposing users to potential unauthorized replay scenarios.
If the domain separator changes unexpectedly, users may encounter failed transactions, causing frustration and potentially leading to a loss of trust in the contract.
Manual review.
Define domainSeperator as immutable in the constructor, so it cannot be changed after initialization.
Set _setDomainSeperator to be called only once by using an initializer pattern if deploying via proxies, or structuring it to prevent multiple calls.
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.