Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: medium
Invalid

Reentrancy Vulnerability in Vault Withdrawal Process Allows Recursive Fund Drainage

Summary

A reentrancy vulnerability exists in the VaultRouterBranch contract's withdrawal mechanism due to improper ordering of state changes and external calls. The VaultRouterBranch.redeem function updates the withdrawal request's fulfillment status after interacting with external ERC4626 vaults, creating a window for recursive attacks. Although currently non-exploitable with supported ERC20 tokens, this flaw becomes critical if the protocol integrates tokens with transfer hooks (e.g., ERC777), potentially enabling attackers to bypass withdrawal limits and drain vault funds through repeated reentrant calls.

Vulnerability Details

The function VaultRouterBranch.redeem (VaultRouterBranch.sol#L486-L570) contains a critical reentrancy vulnerability due to improper state management around external calls:

  1. Insecure State Transition
    The function performs a safety check on withdrawalRequest.fulfilled but updates the flag after making an external ERC4626 redeem() call. This violates the Checks-Effects-Interactions pattern.

  2. Reentrancy Window
    The IERC4626(indexToken).redeem() call ultimately triggers a token transfer to msg.sender. If the index token implements ERC777-like callbacks (or any transfer hook), an attacker could re-enter the redeem function before the fulfilled flag is set.

  3. Protocol Design Risk
    While currently deployed with non-reentrant tokens (WETH/USDC), the system's architecture allows arbitrary ERC4626 vaults via the indexToken parameter. This creates a latent vulnerability that would activate if the protocol ever integrates callback-enabled tokens.

// VaultRouterBranch.redeem()
function redeem(uint128 vaultId, uint128 withdrawalRequestId, uint256 minAssets) external {
// ...
if (withdrawalRequest.fulfilled) revert Errors.WithdrawalRequestAlreadyFulfilled();
// ...
// redeem shares previously transferred to the contract at `initiateWithdrawal` and store the returned assets
address indexToken = vault.indexToken;
uint256 assets =
IERC4626(indexToken).redeem(ctx.sharesMinusRedeemFeesX18.intoUint256(), msg.sender, address(this));
// get the redeem fee
if (ctx.sharesFees > 0) {
IERC4626(indexToken).redeem(
ctx.sharesFees, marketMakingEngineConfiguration.vaultDepositAndRedeemFeeRecipient, address(this)
);
}
// ...
// set withdrawal request to fulfilled
withdrawalRequest.fulfilled = true;
// ...
}

Attack Path:

  1. Initiate a valid withdrawal request

  2. Use a malicious ERC4626 vault that calls back into VaultRouterBranch.redeem during asset transfer

  3. Bypass the withdrawalRequest.fulfilled check multiple times before state update

  4. Drain vault funds through recursive withdrawals

This violates the fundamental CEI (Checks-Effects-Interactions) pattern required for secure smart contract development, regardless of current token support list.

Impact

Medium Severity - While the immediate exploitability is limited by the current ERC20-only token support, this vulnerability creates a critical risk under protocol evolution:

  1. Hypothetical Fund Loss
    If the protocol integrates ERC777-like tokens or callback-enabled vaults in the future, attackers could:

    • Drain vault assets through recursive withdrawals

    • Bypass withdrawal request accounting entirely

  2. Security Practice Violation
    The code structure violates the Checks-Effects-Interactions pattern, a fundamental smart contract security requirement. This creates:

    • Hidden technical debt for future integrations

    • Increased audit surface for new features

  3. Protocol Upgrade Risk
    The latent vulnerability would automatically activate if the protocol:

    • Adds support for yield-bearing tokens with transfer hooks

    • Deploys to chains with novel token standards

    • Integrates third-party ERC4626 vaults

Current Risk Profile

  • Impact Potential: High (theft of vault funds)

  • Likelihood: Medium-Low (requires non-default configuration)

  • Final Rating: Medium (critical flaw mitigated by current token constraints)

This remains a material security finding as it demonstrates improper handling of core financial operations, despite temporary safety from token choices.

Tools Used

Manual Review

Recommendations

Implement the following fixes to eliminate reentrancy risks:

1.Apply CEI Pattern
Modify VaultRouterBranch.redeem to update state before external calls:

function redeem(...) external {
// ... existing checks ...
withdrawalRequest.fulfilled = true; // Move this
IERC4626(indexToken).redeem(...); // Before this
}

2.Add Reentrancy Guard
Use OpenZeppelin's ReentrancyGuard for defense-in-depth:

import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract VaultRouterBranch is ..., ReentrancyGuard {
function redeem(...) external nonReentrant {
// Updated logic
}
}

3.Token Integration Safeguards
Add validation when configuring ERC4626 vaults to reject callback-enabled tokens:

function configureVault(...) internal {
if (hasTransferHooks(token)) revert UnsupportedToken();
}

4.Documentation Update
Add explicit warnings in developer docs about:

  • Risks of ERC777/ERC1363 token integrations

  • Mandatory CEI pattern adherence for stateful functions

5.Enhanced Test Coverage
Implement fork tests simulating:

  • ERC777 token attack vectors

  • Nested reentrancy attempts through multiple vault layers

These changes would cost <1 day of development time while permanently eliminating this class of vulnerability from the protocol.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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