Open hats-bug-reporter[bot] opened 1 year ago
The integrators shouldn't use version
of the vault to create permit
signatures. There is no version
in ERC20Permit contract (see https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Permit.sol) and in most cases it's hardcoded to 1.
Github username: @milotruck Submission hash (on-chain): 0xc04addd385d6126e577c70f6cbcabf47744c9284a8b23a6f6e21b837f8e4276e Severity: medium
Description:
Bug Description
In
ERC20Upgradeable.sol
, the_computeDomainSeparator()
function is used to build the domain seperator that is used for signature verification inpermit()
:ERC20Upgradeable.sol#L144-L157
As seen from above, the version is hardcoded to
1
. This is problematic asERC20Upgradeable
is meant to be inherited by upgradeable contracts that have changing versions. An example of this would beEthErc20Vault.sol
, which has aversion()
function:EthErc20Vault.sol#L137-L139
This could lead to incorrect signature verification in
permit()
if the upgradeable contract's version is upgraded, but the hardcoded version in_computeDomainSeparator()
is unchanged.Attack Scenario
Consider the following scenario:
EthErc20Vault
is initially deployed withversion()
returning1
.EthErc20Vault
contract:version()
returns2
in the new implementation contracct.version()
function is called, it returns2
._computeDomainSeparator()
remains unchanged.permit()
is incorrect due to the outdated version.Impact
If an
EthErc20Vault
is upgraded to a newer version, thepermit()
function will still verify signatures withversion = 1
, making its signature verification incorrect.Therefore, after the upgrade, contracts/frontends that rely on the
version()
function will end up generating different signatures, causingpermit()
to revert when called.Furthermore, signatures that were signed for the vault's previous version can still be used. This is problematic as users expect their older signatures to become invalid due to the change in the vault's version and functionality.
Recommended Mitigation
Consider updating the version in
_computeDomainSeperator()
whenversion()
changes. This can be achieved by doing the following:ERC20Upgradeable.sol
, add a new state variable namedversion
:_computeDomainSeparator()
, useversion
instead of a hardcoded value:ERC20Upgradeable.sol#L144-L157
__ERC20Upgradeable_init()
to take in a_version
parameter used to initializeversion
:ERC20Upgradeable.sol#L170-L180
__VaultToken_init()
to take in aversion
parameter that is passed to__ERC20Upgradeable_init()
:VaultToken.sol#L65-L70
__EthErc20Vault_init()
, passversion()
into__VaultToken_init()
:EthErc20Vault.sol#L184
This ensures that
_computeDomainSeparator()
will always use the same version as theversion()
function as long as__EthErc20Vault_init()
is called after every upgrade.