DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: medium
Invalid

User withdrawals can be indefinitely locked by attacker using sandwich attacks to prevent keeper's WETH-to-alETH swaps

Summary

Users' funds can become indefinitely locked in the strategy when their alETH is converted to WETH in the Transmuter, as an attacker can prevent the keeper from ever successfully swapping WETH back to alETH through a sandwich attack. Importantly, users cannot directly claim WETH from the Transmuter as the position belongs to the Strategy, making them entirely dependent on the keeper's ability to perform the conversion back to alETH.

Vulnerability Details

The strategy's withdrawal mechanism depends on the keeper being able to claim WETH from the Transmuter and swap it back to alETH at a profitable rate. The only way for strategy to claim WETH from transmuter is by keeper calling claimAndSwap. However, the keeper's claimAndSwap transaction is vulnerable to MEV sandwich attack that can force it to always fail. This attack is particulary applicable in public mempool chains like Ethereum, so StrategyMainnet.sol is affected.

Here's the relevant code in StrategyMainnet.sol:

function claimAndSwap(
uint256 _amountClaim,
uint256 _minOut,
uint256 _routeNumber
) external onlyKeepers {
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
require(_minOut > _amountClaim, "minOut too low");
router.exchange(
routes[_routeNumber],
swapParams[_routeNumber],
_amountClaim,
_minOut,
pools[_routeNumber],
address(this)
);
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Slippage too high");
transmuter.deposit(asset.balanceOf(address(this)), address(this));
}

The function requires that the swap must result in more alETH than the WETH being swapped ((balAfter - balBefore) >= _minOut and _minOut > _amountClaim). An attacker can monitor the mempool for keeper's transactions and sandwich them to manipulate the alETH price, making it impossible for the swap to meet this requirement.

This creates a situation where:

  • Early depositors have their alETH converted to WETH in the Transmuter

  • They attempt to withdraw but can't without taking significant losses because their funds are in WETH

  • They must wait for keeper to successfully execute claimAndSwap since they cannot directly access the WETH in Transmuter

  • But attacker can prevent every keeper attempt through sandwich attacks

  • The funds remain indefinitely locked unless users accept large losses

Here's the sequence of events that showcases the vulnerability:

  1. Alice deposits 100 alETH into the strategy. Strategy deposits the funds to transmuter

  2. After enough time, transmuter converts Alice's 100 alETH to 100 WETH (owned by Strategy)

  3. Alice tries to withdraw 100 alETH

  4. The withdrawal would fail without accepting large loss because strategy only has access to WETH and Alice cannot directly claim this WETH from Transmuter

  5. Keeper attempts to call claimAndSwap to convert 100 WETH back to alETH at a premium

  6. Attacker sees keeper's transaction in mempool:

    • Executes a trade before keeper to push alETH price to a point where it is not trading at a premium

    • Keeper's transaction fails due to _minOut > _amountClaim requirement

    • Attacker closes position after keeper's transaction

  7. This can be repeated for every keeper attempt, effectively locking Alice's funds until she accepts a significant loss

Impact

Depositor's may be locked for a prolonged (or potentially indefinite) period

Tools Used

Manual review

Recommendations

One way around this problem would be to introduce emergency mechanism in the Strategy that allows it to return WETH directly to users based on their share of total assets when normal withdrawals become impractical.

Updates

Lead Judging Commences

inallhonesty Lead Judge
11 months ago

Appeal created

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
goran Submitter
10 months ago
goran Submitter
10 months ago
inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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