Open code423n4 opened 3 years ago
It is true that a flash loan could be used to manipulate the value of a liquidity token’s cash and fCash claims. This issue can potentially cause accounts to be liquidated which shouldn’t be, but not for the reasons stated in this issue. I’ll explain what actually can go wrong, and why the fix is simple and non-invasive.
First, to restate the issue: The manipulator could borrow or lend a large amount to a liquidity pool, which would change the amount of cash and fCash sitting in that pool and the corresponding cash and fCash claims of a liquidity token associated with that pool. This could change the liquidity token’s net value within the space of a transaction despite the fact that the oracleRate used to value fCash is lagged and manipulation resistant.
But it is not true that this manipulation could decrease the value of a liquidity token - in fact it could only increase a liquidity token’s value. By borrowing or lending a large amount using a flash loan, the interest rate that the attacker would receive would deviate from the oracleRate in favor of the liquidity provider. If the attacker executed a large lend order, the interest rate on the loan would be significantly below the oracleRate. This would mean that the liquidity providers had borrowed at a below-market rate and that the net value of that trade would be positive for them. Conversely if the attacker executed a large borrow order, the interest rate on the loan would be significantly above the oracleRate. Again, this would mean that the net value of that trade would be positive for the liquidity providers because they would effectively be lending at an above-market rate. In either case, the value of the liquidity token would increase, not decrease.
However, even though the value of a liquidity token could only increase during such an attack, the collateral value of the liquidity token could decrease once the haircuts were applied in the free collateral calculation. The reason for this is that fCash claims are effectively double-haircut (once by the liquidity token haircut and once by the fCash haircut), whereas cash claims are only haircut once (by the liquidity token haircut). This means that even though the attack would increase the value of the liquidity token without haircuts, once you consider the haircuts applied in the free collateral calculation, the collateral value of the liquidity token can be decreased and accounts could become undercollateralized and eligible for liquidation.
Remediation: The immediate remediation for this issue is to restrict providing liquidity to the nToken account exclusively. In the longer term, we will plan to add TWAPs to determine the collateral value of liquidity token cash and fCash claims. This immediate remediation will be fine for now though, and will not degrade the system for two reasons:
The team does not anticipate users providing liquidity directly outside of the nToken (we don’t even offer a way to do it within the UI for example). Only nToken holders receive NOTE incentives, not direct liquidity providers.
The nToken accounts are safe from this attack because the maximum amount that an attacker could temporarily decrease the collateral value of liquidity tokens could never be enough to cause the nToken accounts to become undercollateralized, and therefore they would never be at risk of liquidation due to this attack. The TLDR here is that this attack can’t actually decrease the collateral value of liquidity tokens all that much, and so for an account to be vulnerable they would have to be running quite close to minimum collateralization. This will never happen for the nToken because it doesn’t borrow, it just provides liquidity and always maintains very healthy collateralization levels.
Again, I gave it some thought and I think that this is high risk. Keeping as is.
Handle
cmichel
Vulnerability details
Vulnerability Details
The liquidity token value (
AssetHandler.getLiquidityTokenValue
) is the sum of the value of the individual claims on cash (underlying or rather cTokens) and fCash. The amount to redeem on each of these is computed as the LP token to redeem relative to the total LP tokens, seeAssetHandler.getCashClaims
/AssetHandler.getHaircutCashClaims
:This means the value depends on the current market reserves which can be manipulated. You're essentially computing a spot price (even though the individual values use a TWAP price) because you use the current market reserves which can be manipulated.
See the "How do I tell if I’m using spot price?" section on https://shouldiusespotpriceasmyoracle.com/.
Impact
The value of an LP token is computed as
assetCashClaim + assetRate.convertFromUnderlying( presentValue(fCashClaim) )
where(assetCashClaim, fCashClaim)
depend on the current market reserves which can be manipulated by an attacker via flashloans. Therefore, an attacker trading large amounts in the market either increases or decreases the value of an LP token.If the value decreases, they can try to liquidate users borrowing against their LP tokens / nTokens. If the value increases, they can borrow against it and potentially receive an under-collateralized borrow this way, making a profit.
The exact profitability of such an attack depends on the AMM as the initial reserve manipulation and restoring the reserves later incurs fees and slippage. In constant-product AMMs like Uniswap it's profitable and several projects have already been exploited by this, like warp.finance. However, Notional Finance uses a more complicated AMM and the contest was too short for me to do a more thorough analysis. It seems like a similar attack could be possible here as described by the developers when talking about a different context of using TWAP oracles:
Recommendation
Do not use the current market reserves to determine the value of LP tokens. Think about how to implement a TWAP oracle for the LP tokens themselves, instead of combining it from the two TWAPs of the claimables.