Loss of fund due to replay attacks.
Approvals made on one chain could be replayed when there is a fork without owner's consent.
Proof of Concept
The issue is in the ERC1155PermitSignatureExtension.sol which is inherited by the OceanERC1155.sol and subsequently inherited by the Ocean.sol contract which is in scope.
EIP712 spec incorporates the chainID into signed data to prevent replay attacks but the ERC1155PermitSignatureExtension.sol's implementation caches the DOMAIN_SEPARATOR variable as immutable variable.
The cached DOMAIN_SEPARATOR immutable variable is used in the setPermitForAll to _setApprovalForAll of owner's ERC1155 without considering the actual chainId in case of a hard fork.
The immutable DOMAIN_SEPARATOR becomes hardcoded with the chainID in the constructor. And since there is no dynamic function to get the DOMAIN_SEPARATOR based on chainId as implemented by Openzeppelin's EIP712, the hardcoded chainID in the DOMAIN_SEPARATOR is always used for all chains in case of hard forks. Thus creating vulnerability to replay attacks.
This implementation of EIP712 is unsafe as all signatures to approve owner's ERC1155s will be valid in all forked networks causing owner's to lose all their ERC1155s in all forked networks since the signature is used to _setApprovalForAllowner's ERC1155s.
In order to prevent replay attack and as best practice, adhere to the EIP712 specification by using the Openzepelin's EIP712.
The Openzeppelin's EIP712 dynamically gets the domain seperation ensuring that the chainid is same before using the cached value else the new Domain separator is hashed based on chainId
Lines of code
https://github.com/code-423n4/2023-11-shellprotocol/blob/main/src/ocean/ERC1155PermitSignatureExtension.sol#L17-L18 https://github.com/code-423n4/2023-11-shellprotocol/blob/485de7383cdf88284ee6bcf2926fb7c19e9fb257/src/ocean/ERC1155PermitSignatureExtension.sol#L40 https://github.com/code-423n4/2023-11-shellprotocol/blob/main/src/ocean/OceanERC1155.sol#L36-L39 https://github.com/code-423n4/2023-11-shellprotocol/blob/485de7383cdf88284ee6bcf2926fb7c19e9fb257/src/ocean/Ocean.sol#L79
Vulnerability details
Impact
Loss of fund due to replay attacks. Approvals made on one chain could be replayed when there is a fork without owner's consent.
Proof of Concept
The issue is in the ERC1155PermitSignatureExtension.sol which is inherited by the OceanERC1155.sol and subsequently inherited by the Ocean.sol contract which is in scope.
EIP712 spec incorporates the
chainID
into signed data to prevent replay attacks but the ERC1155PermitSignatureExtension.sol's implementation caches the DOMAIN_SEPARATOR variable as immutable variable. The cached DOMAIN_SEPARATOR immutable variable is used in thesetPermitForAll
to_setApprovalForAll
ofowner
's ERC1155 without considering the actual chainId in case of a hard fork.The immutable DOMAIN_SEPARATOR becomes hardcoded with the chainID in the constructor. And since there is no dynamic function to get the DOMAIN_SEPARATOR based on chainId as implemented by Openzeppelin's EIP712, the hardcoded chainID in the DOMAIN_SEPARATOR is always used for all chains in case of hard forks. Thus creating vulnerability to replay attacks.
This implementation of EIP712 is unsafe as all signatures to approve
owner
's ERC1155s will be valid in all forked networks causing owner's to lose all their ERC1155s in all forked networks since the signature is used to_setApprovalForAll
owner
's ERC1155s.Tools Used
EIP712 specification and Openzepelin's implementation of EIP712
Recommended Mitigation Steps
In order to prevent replay attack and as best practice, adhere to the EIP712 specification by using the Openzepelin's EIP712.
The Openzeppelin's EIP712 dynamically gets the domain seperation ensuring that the chainid is same before using the cached value else the new Domain separator is hashed based on chainId
Assessed type
Other