Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: high
Invalid

Reentrancy Vulnerability in `VaultDepositController:withdraw` Function

Github

Summary

The VaultDepositController:withdraw function is vulnerable to a reentrancy attack. The function performs external calls to transfer tokens before updating its internal state variables, which can be exploited by an attacker to repeatedly withdraw funds. This vulnerability is a common issue in smart contracts when they allow for reentrant function calls before properly updating their state.

Vulnerability Details

The withdraw function is responsible for withdrawing tokens from vaults and transferring them to the caller. The core issue is that the function performs an external call to transfer tokens before updating critical state variables such as totalDeposits, totalPrincipalDeposits, and totalUnbonded.

Here's the relevant code snippet from the withdraw function:

uint256 totalWithdrawn = totalUnbonded - unbondedRemaining;
token.safeTransfer(msg.sender, totalWithdrawn); // External call occurs here
totalDeposits -= totalWithdrawn; // State update after external call
totalPrincipalDeposits -= totalWithdrawn;
totalUnbonded = unbondedRemaining;

The external call to token.safeTransfer() happens before the internal state variables (totalDeposits, totalPrincipalDeposits, and totalUnbonded) are updated. This means that an attacker could call back into the contract (re-enter) before the state is updated, potentially causing the contract to enter an inconsistent state or allowing for multiple withdrawals in a single transaction.

Attack Scenario

  • An attacker initiates a withdrawal request, triggering the withdraw function.

  • The function begins the withdrawal process and makes an external call to transfer tokens to the attacker's address.

  • Before the state variables are updated, the attacker can call back into the withdraw function, effectively restarting the withdrawal process.

  • Since the state hasn't been updated yet, the attacker can withdraw funds again, repeatedly draining the contract of its assets.

Impact

This vulnerability could result in a complete drain of the funds stored in the vaults if an attacker successfully executes a reentrancy attack. Besides allowing fund drainage, this vulnerability could cause a denial-of-service condition for legitimate users who are unable to withdraw funds due to depleted vaults. Repeated exploit attempts could cause the vault system to fail, preventing legitimate operations.

Tools Used

Manual Review

Recommendations

Rearrange the code to update the internal state variables before making any external calls, such as transferring tokens to the caller. This makes sure that even if a reentrant call occurs, the state has already been updated.

uint256 totalWithdrawn = totalUnbonded - unbondedRemaining;
totalDeposits -= totalWithdrawn; // State update after external call
totalPrincipalDeposits -= totalWithdrawn;
totalUnbonded = unbondedRemaining;
token.safeTransfer(msg.sender, totalWithdrawn); // External call occurs here
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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