All incidents

Flashstake LP Share Inflation

Share
Nov 29, 2023 22:29 UTCAttackLoss: 1,599.51 USDC, 0.79 WETHPending manual check1 exploit txWindow: Atomic
Estimated Impact
1,599.51 USDC, 0.79 WETH
Label
Attack
Exploit Tx
1
Addresses
1
Attack Window
Atomic
Nov 29, 2023 22:29 UTC → Nov 29, 2023 22:29 UTC

Exploit Transactions

TX 1Ethereum
0x2b6d0af0dc513a15e325703405739057f9de6ef3f99934b957653b8a3fade4c6
Nov 29, 2023 22:29 UTCExplorer

Victim Addresses

0xa44e79a2c9a8965e7a6fa77bf0ca8faf50e6c73eEthereum

Loss Breakdown

1,599.51USDC
0.785692WETH

Similar Incidents

Root Cause Analysis

Flashstake LP Share Inflation

1. Incident Overview TL;DR

On Ethereum mainnet block 18680255, an unprivileged adversary exploited Flashstake's USDC-WETH FarmingLPToken at 0xa44e79a2c9a8965e7a6fa77bf0ca8faf50e6c73e in transaction 0x2b6d0af0dc513a15e325703405739057f9de6ef3f99934b957653b8a3fade4c6. The attacker used public Balancer and BentoBox flashloans to create two thin Sushiswap pools, deposited a small amount of USDC-WETH LP into the victim contract with attacker-chosen quote paths, minted far more fLP shares than the LP position economically justified, and then redeemed a disproportionate amount of the protocol's pooled LP through emergencyWithdraw.

The root cause is a share-pricing bug in Flashstake's FarmingLPToken. Deposit pricing trusts caller-supplied Sushiswap paths and raw router.getAmountsOut spot quotes, while redemption returns LP pro rata from the global share supply. That lets an attacker overstate the value of a tiny LP deposit, mint inflated shares, and convert those inflated shares into other users' LP principal.

2. Key Background

FarmingLPToken is a pooled wrapper around a Sushiswap LP position managed through Sushi MasterChef. Users receive fLP shares on deposit and later redeem LP according to the contract's global accounting variable withdrawableTotalLPs.

The critical design assumption is that newly minted shares must represent a fair claim on the underlying LP inventory. In this system, the deposit side values LP in SUSHI terms by splitting the LP into reserve slices and calling the Sushiswap router's getAmountsOut along path0 and path1. Because those paths are supplied by the caller, any attacker who can create or control a thin intermediate pool can manipulate the quoted SUSHI value.

This is an ACT opportunity because every dependency in the exploit path is public:

  • Balancer Vault flashloans
  • BentoBox flashloans
  • Sushiswap pair creation and liquidity seeding
  • FarmingLPToken.deposit
  • FarmingLPToken.emergencyWithdraw

The relevant pre-state existed immediately before block 18680255: the victim pool already had outstanding shares and withdrawable USDC-WETH LP inventory, Sushiswap and flashloan venues were live, and the victim code was publicly deployed and verified.

3. Vulnerability Analysis & Root Cause Summary

This incident is an ATTACK case caused by attacker-controlled share valuation. The vulnerable path starts in FarmingLPToken._deposit, where the contract accepts arbitrary path0 and path1 so long as they begin with token0 or token1 and end with sushi. It then values the depositor's LP slices by forwarding those exact paths into router.getAmountsOut.

That valuation is not protocol-controlled. It is a spot quote over the current reserves of every pool on the chosen route, so a caller can mint shares from manipulated reserves instead of fair market value. The same contract later redeems LP through withdraw and emergencyWithdraw using shares * withdrawableTotalLPs / totalShares(), which assumes minted shares were honest in the first place.

The exploit therefore breaks the core invariant that each newly minted share should correspond to a fair claim on pooled LP principal. The code-level breakpoint is the share mint inside _deposit, where the contract mints the quoted SUSHI amount as shares before increasing withdrawableTotalLPs only by the actual LP deposited.

4. Detailed Root Cause Analysis

4.1 Victim Code Path

The key victim logic is the deposit-side valuation and the redemption-side pro-rata LP withdrawal.

Victim share-minting logic (FarmingLPToken):

function _deposit(
    uint256 amountLP,
    address[] calldata path0,
    address[] calldata path1,
    uint256 amountMin,
    address beneficiary
) internal {
    if (path0[0] != token0 || path0[path0.length - 1] != sushi) revert InvalidPath();
    if (path1[0] != token1 || path1[path1.length - 1] != sushi) revert InvalidPath();

    IERC20(lpToken).safeTransferFrom(msg.sender, address(this), amountLP);

    uint256 total = IUniswapV2Pair(lpToken).totalSupply();
    (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(lpToken).getReserves();
    uint256 amount = UniswapV2Utils.quote(router, (reserve0 * amountLP) / total, path0) +
        UniswapV2Utils.quote(router, (reserve1 * amountLP) / total, path1);

    _mint(beneficiary, amount);
    withdrawableTotalLPs += amountLP;
}

Victim quote helper (UniswapV2Utils):

function quote(
    address router,
    uint256 amountIn,
    address[] memory path
) internal view returns (uint256 amountOut) {
    if (path.length < 2) return amountIn;

    uint256[] memory amountsOut = IUniswapV2Router02(router).getAmountsOut(amountIn, path);
    return amountsOut[amountsOut.length - 1];
}

Victim redemption logic (FarmingLPToken):

function emergencyWithdraw(address beneficiary) external override nonReentrant {
    uint256 shares = sharesOf(msg.sender);
    uint256 amountLP = (shares * withdrawableTotalLPs) / totalShares();
    IMasterChef(masterChef).withdraw(pid, amountLP);

    IERC20(lpToken).safeTransfer(beneficiary, amountLP);

    _burn(msg.sender, shares);
    withdrawableTotalLPs -= amountLP;
}

The mismatch is explicit: minting uses attacker-controlled spot routes, while redemption assumes shares were minted from a trusted valuation basis.

4.2 Exploit Sequence

The collected seed trace shows the following deterministic sequence inside transaction 0x2b6d0af0dc513a15e325703405739057f9de6ef3f99934b957653b8a3fade4c6:

  1. The adversary EOA 0x9d44f1a37044500064111010632a8a59003701c8 calls helper contract 0x4bc691601b50b3e107b89d5ea172b40a9dbc6251.
  2. The helper takes public flashloans of 800000000 USDC and 500000000000000000 WETH from Balancer, then 400000000000000000000000 SUSHI from BentoBox.
  3. The helper creates thin Sushiswap pools:
    • USDC-AST with 2000000 USDC units and 10000 AST units
    • SUSHI-AST with 400000000000000000000000 SUSHI and 10000 AST units
  4. The helper mints a small USDC-WETH LP position of 25156809832 LP units.
  5. The helper calls FarmingLPToken.deposit with manipulated paths:
    • USDC -> AST -> SUSHI
    • WETH -> SUSHI
  6. The victim contract queries getAmountsOut over those attacker-controlled pools and mints 132925896953047092702474 fLP shares.
  7. The helper immediately calls emergencyWithdraw and receives 20144470251713 LP units, far above the LP amount it deposited.
  8. The helper burns the redeemed LP and unwinds the manipulation pools, repays both flashloans, and keeps the residual profit.

Representative trace evidence from the seed transaction:

0xd9e1...::addLiquidity(USDC, AST, 2000000, 10000, ...)
0xd9e1...::addLiquidity(SUSHI, AST, 400000000000000000000000, 10000, ...)
0xd9e1...::getAmountsOut(1999999, [USDC, AST, SUSHI])
0xa44e79...::deposit(...)
emit Deposit(param0: 132925896953047092702474, param1: 25156809832, param2: 0x4Bc691...)
emit EmergencyWithdraw(param0: 127556102597101835622284, param1: 20144470251713, param2: 0x4Bc691...)

4.3 Breakpoint and Invariant Failure

The invariant is:

Newly minted fLP shares must correspond to a fair, protocol-controlled economic contribution of LP principal.

The concrete breakpoint is the _mint(beneficiary, amount) call in _deposit. The contract treats manipulated spot quotes as if they were trustworthy valuation inputs and records the resulting share count into the same supply later used for pro-rata LP redemption.

The trace and balance diff show the numerical consequence:

  • LP deposited into Flashstake: 25156809832
  • Shares minted: 132925896953047092702474
  • LP redeemed through emergencyWithdraw: 20144470251713
  • Net LP extracted beyond the depositor's own contribution: 20119313441881

This is exactly the broken-accounting step that lets attacker-inflated shares drain pooled LP inventory from pre-existing depositors.

5. Adversary Flow Analysis

The adversary cluster consists of:

  • EOA 0x9d44f1a37044500064111010632a8a59003701c8, which submitted the exploit transaction and later received residual fLP shares.
  • Helper contract 0x4bc691601b50b3e107b89d5ea172b40a9dbc6251, which executed the flashloans, pool creation, manipulated deposit, LP redemption, unwinding, and repayment.

The primary victim is Flashstake's USDC-WETH FarmingLPToken at 0xa44e79a2c9a8965e7a6fa77bf0ca8faf50e6c73e. Sushi MasterChef at 0xc2edad668740f1aa35e4d8f227fb8e17dca888cd is a supporting protocol component because it holds the pooled LP position withdrawn during redemption.

Operationally, the attacker used a single-transaction accounting attack:

  • Stage 1, funding: obtain flash liquidity from Balancer and BentoBox.
  • Stage 2, setup: create attacker-controlled thin pools that satisfy the victim's only path restrictions.
  • Stage 3, realization: deposit a small LP position, mint inflated shares, redeem a disproportionate amount of pooled LP, burn the LP back into USDC and WETH, repay the loans, and keep the remainder.

The transaction remained permissionless end to end. No privileged key, whitelist, governance action, or private state was required.

6. Impact & Losses

The exploit depleted the victim pool's withdrawable USDC-WETH LP inventory and transferred value to the attacker after all flashloan repayments.

Measured losses from the victim-side LP unwind were:

  • 1599512305 raw USDC units (1599.512305 USDC)
  • 785691973475409634 raw WETH units (0.785691973475409634 WETH)

The helper contract retained positive post-repayment balances, including 1597512305 raw USDC units and 654886372151164260 raw WETH units. The seed balance diff also shows that the attacker EOA paid gas and received 5369794355945257080190 residual fLP shares transferred from the helper in the same transaction.

7. References

The validation and report are based on the following concrete sources:

  • Seed exploit transaction: 0x2b6d0af0dc513a15e325703405739057f9de6ef3f99934b957653b8a3fade4c6
  • Victim contract: Flashstake FarmingLPToken at 0xa44e79a2c9a8965e7a6fa77bf0ca8faf50e6c73e
  • Victim LP pair: USDC-WETH Sushiswap pair at 0x397ff1542f962076d0bfe58ea045ffa2d347aca0
  • Victim reward venue: Sushi MasterChef at 0xc2edad668740f1aa35e4d8f227fb8e17dca888cd
  • Seed trace evidence showing flashloans, manipulated pool creation, getAmountsOut, Deposit, EmergencyWithdraw, LP burn, and profit realization
  • Seed balance-diff evidence showing attacker profit, victim-pair reserve depletion, and LP movement