DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Reentrancy Vulnerability in `_handleReturn` Function in PerpetualVault Contract

Reentrancy Vulnerability in _handleReturn Function in PerpetualVault Contract

Description:

The _handleReturn function interacts with external contracts via the _transferToken call and potentially through the IGmxProxy(gmxProxy).refundExecutionFee call. If these external calls are not properly secured against reentrancy attacks, an attacker could exploit this by recursively calling back into _handleReturn or other functions within the same contract, leading to unintended behavior or financial loss.

Impact:

An attacker might be able to drain funds from the contract or manipulate its internal state by exploiting a reentrancy vulnerability. This can result in significant financial losses for users and undermine the integrity of the smart contract system.

Proof of Concept:

Consider the following scenario where _transferToken is used to send tokens to an untrusted recipient:

  1. An attacker deploys a malicious contract that implements a fallback function which calls back into _handleReturn.

  2. The attacker deposits collateral into the contract, ensuring their depositId triggers the vulnerable _transferToken call.

  3. During execution of _handleReturn, when _transferToken is invoked, it transfers tokens to the attacker's malicious contract.

  4. The malicious contract’s fallback function executes and calls back into _handleReturn, allowing the attacker to withdraw additional funds before the global state is updated (delete flowData; delete flow;).

This recursive call allows the attacker to bypass the intended business logic, withdrawing more than they should or causing undesired side effects such as double-spending.

// Malicious contract example
// Malicious Contract
contract Attacker {
PerpetualVault public vulnerableContract;
```Solidity
constructor(address _vulnerableContractAddress) {
vulnerableContract = YourContract(_vulnerableContractAddress);
}
// Fallback function to perform reentrancy attack
receive() external payable {
if (address(vulnerableContract).balance >= 1 ether) {
vulnerableContract._handleReturn(0, true, false); // Recursive call
}
}
function attack() external payable {
require(msg.value >= 1 ether);
vulnerableContract._handleReturn(0, true, false); // Initial call
}

Recommended Mitigation:

To prevent reentrancy attacks, implement the Checks-Effects-Interactions pattern. Update all mutable states before making any external calls. In your case, ensure that swapProgressData, flowData, and flow are deleted prior to invoking _transferToken or refundExecutionFee.

Here’s how you can modify the _handleReturn function:

function _handleReturn(uint256 withdrawn, bool positionClosed, bool refundFee) internal {
(uint256 depositId) = flowData;
uint256 shares = depositInfo[depositId].shares;
uint256 amount;
// Effects - Update state variables first
delete swapProgressData;
delete flowData;
delete flow;
if (positionClosed) {
amount = collateralToken.balanceOf(address(this)) * shares / totalShares;
} else {
uint256 balanceBeforeWithdrawal = collateralToken.balanceOf(address(this)) - withdrawn;
amount = withdrawn + balanceBeforeWithdrawal * shares / totalShares;
}
if (amount > 0) {
// Interactions - Perform external calls after updating states
_transferToken(depositId, amount);
}
emit Burned(depositId, depositInfo[depositId].recipient, depositInfo[depositId].shares, amount);
_burn(depositId);
if (refundFee) {
uint256 usedFee = callbackGasLimit * tx.gasprice;
if (depositInfo[depositId].executionFee > usedFee) {
try IGmxProxy(gmxProxy).refundExecutionFee(depositInfo[depositId].owner, depositInfo[depositId].executionFee - usedFee) {} catch {}
}
}
}

Additionally, consider using OpenZeppelin's ReentrancyGuard library, which provides a simple and effective mechanism to protect against reentrancy vulnerabilities. You can import the library and use the nonReentrant modifier to prevent reentrancy attacks in your smart contract.

Updates

Lead Judging Commences

n0kto Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

n0kto Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

Support

FAQs

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