📄

Report #15045

Report Date
December 22, 2022
Status
Closed
Payout

Price manipulation attack tricking the system into minting

Report Info

Report ID

#15045

Target

Report type

Smart Contract

Impacts

  • Illegitimate minting of protocol native assets
  • Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)

Has PoC?

No

Description

Severity: high / medium

Class: Illegitimate minting of protocol native assets / Griefing

The TWAP computation in LibCurveOracle.sol uses the current balance of the curve pool to compute the TWAP.

cum_balances = IMeta3CurveOracle(C.curveMetapoolAddress()).get_price_cumulative_last();
        balances = IMeta3CurveOracle(C.curveMetapoolAddress()).get_balances();
        uint256 lastTimestamp = IMeta3CurveOracle(C.curveMetapoolAddress()).block_timestamp_last();

        cum_balances[0] = cum_balances[0].add(
            balances[0].mul(block.timestamp.sub(lastTimestamp))
        );
        cum_balances[1] = cum_balances[1].add(
            balances[1].mul(block.timestamp.sub(lastTimestamp))
        );

This makes the computation vulnerable to manipulation. Note also the Curve meta pool documentation which explicitly describes not using the current balance:

/The Curve TWAP is greatly inspired by [Uniswap TWAP architecture](https://uniswap.org/docs/v2/core-concepts/oracles/) , in that the price is a cumulative value over time, which reduces balance shifts due to flash loans, but also records the balances based on the previous block, to avoid recording flashloan data./

Attack

The attack is simple:

  1. Buy up a lot of BEAN from the curve pool
  2. Call sunrise()
  3. Sell beans again

This attack costs approximately 30-40k in fees depending on the price of BEAN. (This cost is an approximation, see the recommendation section which describes that PoS makes this attack cheaper with multi-block manipulation).

The cost of the attack steeply increases the further BEAN is from its peg.

Impact

A malicious user can trick bean into believing its value is above peg, even when it’s below making it mint and distribute tokens.

They can benefit from this in various ways:

  1. They can be interested in griefing the protocol
  2. They can somehow short bean
  3. They can have unripe pods that ripen
  4. They can hold stalks and receive beans
  5. They can have fertiliser/sprouts

Recommendation

Omit using the current balance in computing the TWAP.

The following is an example draft function that highlights how this might be done:

    function twap()
        internal
        view
        returns (uint256[2] memory balances, uint256[2] memory cum_balances)
    {
        cum_balances = IMeta3CurveOracle(C.curveMetapoolAddress()).get_price_cumulative_last();
        uint256 lastTimestamp = IMeta3CurveOracle(C.curveMetapoolAddress()).block_timestamp_last();

        AppStorage storage s = LibAppStorage.diamondStorage();
        Storage.Oracle storage o = s.co;

        uint256 deltaTimestamp = lastTimestamp.sub(o.timestamp);

        balances[0] = cum_balances[0].sub(o.balances[0]).div(deltaTimestamp);
        balances[1] = cum_balances[1].sub(o.balances[1]).div(deltaTimestamp);
    }

Multi-block manipulation

Note that multi-block manipulation has become more feasible with the switch to PoS.

See also: [Statistical analysis on Ethereum k-consecutive block proposal probabilities and case study - Alvaro Revuelta](https://alrevuelta.github.io/posts/ethereum-mev-multiblock)

In short, with PoS it occurs that the same actor ( or group of colluding actors ) is given the ability to build consecutive blocks. As a result miners would still be able to benefit from the attack, and are able to perform the attack with a lower cost both now and after the fix.

It’s near impossible to “fix” this issue for a TWAP, but feasible to get to a level of acceptable risk. Your two main options are likely to add more liquidity (easier said than done) or tweak the stable curve parameters.

Uniswap has an interesting article on this: [Uniswap v3 TWAP Oracles in Proof of Stake](https://uniswap.org/blog/uniswap-v3-oracles#what-about-multiple-block-manipulations).

BIC Response

The reported issue is in part accounted for as a result of EBIP-2 (which implemented a cap on the absolute value of time-weighted average deltaB) and is otherwise known and acknowledged by the BIC.

From the Solution section:

Putting limits on the Oracle significantly decreases the maximum effect of such an attack in the short term given the difficulty of repeated manipulation. This functions as a short term solution. BEAN:3CRV liquidity should be moved to a Well with a multi-block MEV resistant oracle once Wells are released.

From the Remaining Vulnerability section:

The high cost to execute the attack and limited exposure to Beanstalk make the attack unattractive, but not impossible to execute. Therefore, it is important to migrate to a pool with a multi-block-MEV-resistant on-chain oracle for BEAN:3CRV.