Project

One World
NFTDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

Replay attack vulnerability in NativeMetaTransaction::executeMetaTransaction

Summary

The NativeMetaTransaction::executeMetaTransaction function is vulnerable to cross-chain replay attacks due to the fact that signatures are verified without incorporating the chainId.

I would like to note that I know that the scope of this audit competition only mentions polygon as a compatible blockchain for this protocol. But for the sake of security, I report this issue since the procotol could be deployed in another chain.

The issue can also occur when there is also a fork in the chain.

Vulnerability Details

The NativeMetaTransaction::executeMetaTransaction function verifies the authenticity of transactions using EIP-712 signatures. However, the current implementation of the NativeMetaTransaction::hashMetaTransaction` function does not include the chainId in the hash computation, making it possible for an attacker to replay the same transaction on different chains.

File: /contracts/meta-transaction/NativeMetaTransaction.sol#L33-L68
function executeMetaTransaction(
address userAddress,
bytes memory functionSignature,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) public payable returns (bytes memory) {
MetaTransaction memory metaTx = MetaTransaction({
nonce: nonces[userAddress],
from: userAddress,
functionSignature: functionSignature
});
require(
verify(userAddress, metaTx, sigR, sigS, sigV),
"Signer and signature do not match"
);
// increase nonce for user (to avoid re-use)
nonces[userAddress] = nonces[userAddress] + 1;
emit MetaTransactionExecuted(
userAddress,
msg.sender,
functionSignature,
hashMetaTransaction(metaTx)
);
// Append userAddress and relayer address at the end to extract it from calling context
(bool success, bytes memory returnData) = address(this).call{value: msg.value}(
abi.encodePacked(functionSignature, userAddress)
);
require(success, "Function call not successful");
return returnData;
}
File: contracts/meta-transaction/NativeMetaTransaction.sol#L90-L106
function verify(
address signer,
MetaTransaction memory metaTx,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) internal view returns (bool) {
require(signer != address(0), "NativeMetaTransaction: INVALID_SIGNER");
return
signer ==
ecrecover(
toTypedMessageHash(hashMetaTransaction(metaTx)),
sigV,
sigR,
sigS
);
}
File: contracts/meta-transaction/NativeMetaTransaction.sol#L70-L84
function hashMetaTransaction(MetaTransaction memory metaTx)
public
pure
returns (bytes32)
{
return
keccak256(
abi.encode(
META_TRANSACTION_TYPEHASH,
metaTx.nonce,
metaTx.from,
keccak256(metaTx.functionSignature)
)
);
}

Impact

An attacker can exploit this vulnerability by monitoring transactions on one chain and replaying them on another chain(fork) where the same contract is deployed. This can lead to unauthorized transaction execution.

Tools Used

Manual review.

Recommendations

Consider to include the chainId in the verification process of the EIP-712 signatures. This ensures that each signature is unique to the specific chain it was intended for, preventing replay attacks across different chains.

An updated of the NativeMetaTransaction::hashMetaTransaction function could be like this:

File: contracts/meta-transaction/NativeMetaTransaction.sol#L70-L84
function hashMetaTransaction(MetaTransaction memory metaTx)
public
pure
returns (bytes32)
{
return
keccak256(
abi.encode(
META_TRANSACTION_TYPEHASH,
metaTx.nonce,
metaTx.from,
-- keccak256(metaTx.functionSignature)
++ keccak256(metaTx.functionSignature),
++ block.chainid
)
);
}
Updates

Lead Judging Commences

0xbrivan2 Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
0xbrivan2 Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.