Project

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

Vulnerability in Meta Transaction Execution: Reentrancy Attack in executeMetaTransaction

Summary:

The smart contract NativeMetaTransaction, which supports meta transactions (transactions that allow users to submit transactions via relayers), contains a critical vulnerability in the executeMetaTransaction function. This vulnerability is a reentrancy attack that can occur when the contract interacts with external contracts, specifically when using the low-level call function to execute the user’s function signature. The contract does not adequately protect against reentrancy, which could be exploited by an attacker to manipulate the state or drain funds.

Location of Vulnerability:

The vulnerability resides in the executeMetaTransaction function, specifically at the following line:

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

This line of code makes an external call to address(this) using the call function, which is vulnerable to reentrancy.


Vulnerability Details:

The contract executes meta transactions, allowing a user to sign a transaction off-chain and have a relayer submit it to the blockchain. The relayer pays for the gas to execute the transaction, and the user can execute functions via executeMetaTransaction.

The executeMetaTransaction function performs a low-level call to the contract itself using the call method. The key part that opens up the vulnerability is the use of the call function to execute arbitrary user-defined function signatures:

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

This line allows the contract to delegate execution to another function, but it does so without checking for reentrancy. If the functionSignature points to a function that makes another call back to executeMetaTransaction (or any other function that interacts with external contracts), it could lead to a reentrancy attack.

Reentrancy attacks occur when an external call to another contract (or even to the contract itself) can trigger a function call that re-enters the contract in an unexpected manner. This could allow an attacker to re-execute executeMetaTransaction multiple times within the same transaction, potentially exploiting vulnerabilities like manipulating state or draining funds.

Reentrancy Flow:

  1. A malicious user submits a meta transaction with a crafted function signature.

  2. The contract executes the transaction, making an external call using call.

  3. If the function signature executes a call back into executeMetaTransaction, the same code could be executed repeatedly before the nonce is updated, allowing the attacker to bypass the nonce check and potentially exploit the contract.

This is critical because the contract doesn't have any protection against reentrancy, which can lead to unintended behavior such as:

  • State manipulation: Reentrancy can bypass nonce increments, causing the same transaction to be executed multiple times.

  • Funds draining: If the contract is transferring funds or assets, an attacker can potentially call back into the contract multiple times, draining the contract’s balance.


Impact:

The impact of this vulnerability is high. If exploited, the attacker can:

  • Bypass nonce checks: The nonce is incremented after the function call, but reentrancy can allow an attacker to reuse the same nonce multiple times, causing unauthorized execution of multiple transactions under the same signature.

  • Potential funds theft: If the contract interacts with external contracts that allow the transfer of funds, the attacker could drain the contract’s funds.

  • State manipulation: Since the contract does not update its state or enforce any reentrancy protection, the attacker could manipulate the contract's internal state in unintended ways.

This vulnerability could lead to serious financial losses if the contract holds significant assets or interacts with valuable external contracts.


Tools Used:

  1. Slither: A static analysis tool for Solidity that analyzes code for common vulnerabilities, including reentrancy. Slither did not specifically highlight this vulnerability but would help identify general issues related to function calls and external contract interactions.

  2. MythX: A smart contract security analysis service that checks for various vulnerabilities, including reentrancy. However, it may miss certain reentrancy vulnerabilities if the contract uses call in a non-standard way.

  3. Manual Review: An in-depth manual review of the smart contract code, paying attention to external function calls, state changes, and the use of call, led to the identification of this reentrancy risk.


Recommendations:

Fix 1: Use Reentrancy Guards

To protect against reentrancy attacks, the contract should implement a reentrancy guard. The most common way to do this is by using a mutex (a flag that ensures that a function cannot be entered again before the first execution is complete).

Here is an example of how to implement a reentrancy guard:

bool private locked;
modifier nonReentrant() {
require(!locked, "ReentrancyGuard: reentrant call");
locked = true;
_;
locked = false;
}
function executeMetaTransaction(
address userAddress,
bytes memory functionSignature,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) public payable nonReentrant 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;
}

Fix 2: Use transfer or send instead of call for Ether Transfers

If the contract is transferring Ether, replace the low-level call with transfer or send. These functions limit the gas forwarded to the recipient, thus preventing reentrancy. If the contract is not meant to transfer Ether, ensure the msg.value is not being passed incorrectly.

Fix 3: Audit and Test External Interactions

Before interacting with external contracts, ensure they are trusted and safe. Implement checks on external calls and ensure they cannot cause reentrancy or other unexpected behaviors. Using well-established libraries like OpenZeppelin's ReentrancyGuard can help mitigate these risks.


Conclusion:

The NativeMetaTransaction contract contains a severe reentrancy vulnerability in the executeMetaTransaction function due to the use of the low-level call method. This vulnerability could lead to attackers bypassing the nonce check, draining funds, or manipulating contract state.

By implementing reentrancy guards, using safer function calls like transfer for Ether transfers, and ensuring thorough auditing of external interactions, this vulnerability can be mitigated.

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.