Mismanagement of totalShares and leverage positions allows users to withdraw more funds than they should, potentially draining the vault.
The function _withdraw() in PerpetualVault.sol is responsible for handling user withdrawals.
Incorrect Code in _withdraw()
Problem Explanation
Incorrect Withdrawal Calculation:
The contract determines how much a user can withdraw based on shares divided by totalShares. However, if totalShares is not updated correctly during withdrawals, the contract may allow a user to withdraw more than their actual balance.
Example:
Assume totalShares = 1000 and a user owns 500 shares.
If totalDepositAmount = 100 ETH, the user should receive 50 ETH upon withdrawal.
However, if totalShares is not updated correctly, the contract might compute 100 ETH instead, leading to a full depletion of the vault.
Incorrect Collateral Deduction:
The function _createDecreasePosition() will incorrectly deduct collateralDeltaAmount if the calculations for open positions (positionData.collateralAmount * shares / totalShares) do not correctly reflect leverage impact.
If shares is incorrectly updated before collateral deduction, the function will overestimate collateralDeltaAmount, allowing excessive withdrawals.
Leverage Misuse & Manipulation Risk:
If _isLongOneLeverage(beenLong) is incorrectly set, an attacker will exploit long leverage positions to artificially inflate their collateral before withdrawing.
Example:
Assume a user deposits 10 ETH and takes a 10x long position.
The contract will think the user has 100 ETH in collateral.
If _withdraw() does not properly account for leveraged positions, the user could withdraw more ETH than they originally deposited.
Scenario
Step 1: Attacker Deposits and Opens a Leverage Position
The attacker deposits 10 ETH into the vault.
The contract assumes a 10x leverage, treating the attacker’s position as 100 ETH.
Step 2: Attacker Requests a Withdrawal
The attacker calls _withdraw(), expecting to remove 10 ETH.
Due to incorrect calculations in _handleReturn() and _createDecreasePosition(), the function believes the attacker is entitled to withdraw more.
Step 3: Vault Depletion
Instead of 10 ETH, the contract mistakenly allows 100 ETH withdrawal due to incorrect collateral calculations.
The vault is drained, and legitimate users cannot withdraw their funds.
Severity: High
Impact: Full depletion of the vault’s liquidity, leading to financial losses for all users.
Likelihood: High, as the issue could be exploited in a single transaction.
Manual Review
Fix the Withdrawal Calculation
This ensures that the withdrawal does not exceed the available liquidity.
Correct State Updates
Leverage Validation
Ensure _isLongOneLeverage(beenLong) correctly checks if the position is active.
Add Withdrawal Limits
Set max withdrawal thresholds to avoid over-extraction of funds.
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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.