Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crazy Cyan Worm - Missing Slippage Protection in WeightedIndex.debond Enables Flash Loan-Powered Sandwich Attacks #565

Open
sherlock-admin3 opened this issue Feb 17, 2025 · 0 comments

Comments

@sherlock-admin3
Copy link
Contributor

Crazy Cyan Worm

Medium

Missing Slippage Protection in WeightedIndex.debond Enables Flash Loan-Powered Sandwich Attacks

Summary

The missing minimum output validation in WeightedIndex.debond will cause partial loss of funds for users withdrawing from the index as attackers can manipulate withdrawal ratios through flash loan-powered sandwich attacks. By front-running a user's WeightedIndex.debond transaction with a large WeightedIndex.bond operation funded by flash loans, attackers artificially inflate the index's total supply, reducing the victim's share percentage and withdrawn token amounts. This allows attackers to profit from the manipulated asset ratios while victims receive significantly less value than market rates.

Root Cause

The WeightedIndex.debond function in WeightedIndex.sol#L185-L191 lacks critical slippage protection mechanisms, enabling sandwich attacks through:

    function debond(uint256 _amount, address[] memory, uint8[] memory) external override lock noSwapOrFee {
        uint256 _amountAfterFee = _isLastOut(_amount) || REWARDS_WHITELIST.isWhitelistedFromDebondFee(_msgSender())
            ? _amount
            : (_amount * (DEN - _fees.debond)) / DEN;
@>      uint256 _percSharesX96 = (_amountAfterFee * FixedPoint96.Q96) / _totalSupply;
				
				// ...
				
        for (uint256 _i; _i < _il; _i++) {
@>          uint256 _debondAmount = (_totalAssets[indexTokens[_i].token] * _percSharesX96) / FixedPoint96.Q96;
            if (_debondAmount > 0) {
                _totalAssets[indexTokens[_i].token] -= _debondAmount;
@>              IERC20(indexTokens[_i].token).safeTransfer(_msgSender(), _debondAmount);
            }
        }
				
				// ...
    }
  1. Manipulable Share Calculation:

    • The _percSharesX96 ratio (L185) uses real-time _totalSupply that attackers can inflate via front-run WeightedIndex.bond calls
    • Withdrawal amounts are calculated based on current _totalAssets balances that can be altered through flash loan attacks
  2. Absence of Minimum Output Checks:

    • No parameters allow users to specify minimum acceptable amounts for each withdrawn token
    • Direct transfers at L191 execute regardless of actual asset values relative to market prices
  3. Attack Vector:

    • Attacker front-runs user's debond with large bond to manipulate _totalSupply and asset ratios
    • User's calculated _percSharesX96 yields fewer assets than expected
    • Attacker reverses position post-user-transaction through debond, profiting from manipulated ratios

The core vulnerability lies in the withdrawal process relying entirely on manipulable contract state without user-defined safety thresholds for asset outputs.

Internal Pre-conditions

  1. debond function calculates withdrawals using real-time _totalSupply and _totalAssets
  2. No minimum amount validation exists for individual token withdrawals
  3. bond function allows arbitrary supply inflation without cooldown/limits

External Pre-conditions

  1. Attacker has access to flash loan facilities
  2. Index contains sufficient liquidity in underlying tokens (e.g., $1M TVL)
  3. At least 2 tokens exist in the index with liquid markets

Attack Path

  1. Victim initiates debond of 100,000 index tokens (worth $100k at fair value)
  2. Attacker front-runs transaction with:
    a. $500k flash loan
    b. bond call injecting 500,000 index tokens via manipulated pricing
  3. Victim's transaction executes:
    • _totalSupply inflated from 1M to 1.5M tokens
    • _percSharesX96 becomes 6.66% instead of 10%
    • Receives $66.6k worth of assets instead of $100k
  4. Attacker back-runs with debond of 500k tokens:
    • Withdraws $333k using same manipulated ratios
    • Repays flash loan with $500k principal
  5. Net profit: $333k (withdrawn) - $500k (loan) + $500k (repayment) + $33.4k (victim loss) = $33.4k profit

Impact

Victim suffers 33.4% immediate financial loss ($100k ➔ $66.6k) due to artificial supply inflation. Attack scales linearly with victim position size - a $1M debond under same conditions would yield $666k loss. Losses become permanent when attacker arbitrages manipulated assets against real markets.

PoC

No response

Mitigation

Implement mandatory slippage protection in the debond function through:

  1. Minimum Output Parameters:

    • Add a uint256[] calldata minAmounts parameter for per-token minimums
    • Require withdrawn amounts ≥ specified minimums
  2. Validation Layer:

for (uint256 i; i < indexTokens.length; ++i) {
		uint256 debondAmount = ...; // Existing calculation
		require(debondAmount >= minAmounts[i], "Below minimum");
}
  1. Parameter Consistency Checks:
		require(minAmounts.length == indexTokens.length, "Invalid input length");
  1. Front-running Resistance:
    • Use deadline parameters to invalidate stale transactions
    • Implement TWAP-based checks for critical ratios

This matches the existing bond function's _amountMintMin pattern, creating symmetric protection for deposit/withdrawal operations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant