Project

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

`executeMetaTransaction` Fails Due to Incompatibility with Target Contract Function Signatures

Summary

The executeMetaTransaction function in NativeMetaTransaction.sol attempts to perform meta-transactions by encoding and calling functions with appended data. Specifically, it appends the userAddress to the functionSignature calldata before calling the target function. However, neither NativeMetaTransaction nor MembershipFactory (which inherits NativeMetaTransaction) have functions that accept this appended userAddress. As a result, function calls made through executeMetaTransaction are likely to fail because the appended data prevents the calldata from matching any function signature in these contracts. This design flaw renders executeMetaTransaction non-functional as intended.

Vulnerability Details

The issue arises in the executeMetaTransaction function, where the contract constructs calldata by appending userAddress to functionSignature:

(bool success, bytes memory returnData) = address(this).call{value: msg.value}(
abi.encodePacked(functionSignature, userAddress)
);

This approach is problematic because none of the functions in MembershipFactory or NativeMetaTransaction are designed to handle this appended userAddress parameter. When executeMetaTransaction makes the call, the calldata does not match any function signatures, leading to a call failure.

For example, if functionSignature represents a call to joinDAO(address daoMembershipAddress, uint256 tierIndex), appending userAddress results in a signature mismatch, as there is no function defined as joinDAO(address, uint256, address).

Relevant Code Snippet

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"
);
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;
}

Impact

The executeMetaTransaction function is effectively unusable, as any attempt to call a function with appended data will fail due to a signature mismatch. This renders the meta-transaction functionality non-functional, preventing users from leveraging gasless transactions or relaying through a meta-transaction relay system.

Tools Used

Manual analysis

Recommendations

Opt for a recommended approach to meta-transactions by avoiding the modification of the calldata structure. Instead, set userAddress as an internal state variable (e.g., _currentMetaTransactionUser) specifically for the duration of the meta-transaction call. This variable can then be referenced by overriding _msgSender() to return the meta-transaction sender address whenever _currentMetaTransactionUser is set. This approach preserves the original function signatures, enabling compatibility with the target contract’s functions and ensuring that the transaction context reflects the original user’s identity.

Updates

Lead Judging Commences

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

Support

FAQs

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