Attackers Can DOS Balancer Vaults By Bypassing The BPT Threshold
Malicious users can lock up all the leverage vaults offered by Notional causing denial-of-service by bypassing the BPT threshold and subseqently trigger an emergency settlement against the vaults.\\nThe current BPT threshold is set to 20% of the total BTP supply based on the environment file provided during the audit.\\n```\\nFile: BalancerEnvironment.py\\n            "oracleWindowInSeconds": 3600,\\n            "maxBalancerPoolShare": 2e3, # 20%\\n            "settlementSlippageLimitPercent": 5e6, # 5%\\n```\\n\\n```\\nFile: BalancerVaultStorage.sol\\n    function _bptThreshold(StrategyVaultSettings memory strategyVaultSettings, uint256 totalBPTSupply) \\n        internal pure returns (uint256) {\\n        return (totalBPTSupply * strategyVaultSettings.maxBalancerPoolShare) / BalancerConstants.VAULT_PERCENT_BASIS;\\n    }\\n```\\n\\nWhen the total number of BPT owned by the vault exceeds the BPT threshold, no one will be able to enter the vault as per the require check at Line 295-296 within the `TwoTokenPoolUtils._joinPoolAndStake` function.\\n```\\nFile: TwoTokenPoolUtils.sol\\n    function _joinPoolAndStake(\\n        TwoTokenPoolContext memory poolContext,\\n        StrategyContext memory strategyContext,\\n        AuraStakingContext memory stakingContext,\\n        uint256 primaryAmount,\\n        uint256 secondaryAmount,\\n        uint256 minBPT\\n    ) internal returns (uint256 bptMinted) {\\n        // prettier-ignore\\n        PoolParams memory poolParams = poolContext._getPoolParams( \\n            primaryAmount, \\n            secondaryAmount,\\n            true // isJoin\\n        );\\n\\n        bptMinted = BalancerUtils._joinPoolExactTokensIn({\\n            context: poolContext.basePool,\\n            params: poolParams,\\n            minBPT: minBPT\\n        });\\n\\n        // Check BPT threshold to make sure our share of the pool is\\n        // below maxBalancerPoolShare\\n        uint256 bptThreshold = strategyContext.vaultSettings._bptThreshold(\\n            poolContext.basePool.pool.totalSupply()\\n        );\\n        uint256 bptHeldAfterJoin = strategyContext.totalBPTHeld + bptMinted;\\n        if (bptHeldAfterJoin > bptThreshold)\\n            revert Errors.BalancerPoolShareTooHigh(bptHeldAfterJoin, bptThreshold);\\n\\n        // Transfer token to Aura protocol for boosted staking\\n        stakingContext.auraBooster.deposit(stakingContext.auraPoolId, bptMinted, true); // stake = true\\n    }\\n```\\n\\nAnother key point that is critical for this issue is that when the total number of BPT owned by the vault exceeds the BPT threshold, an emergency settlement can be triggered against the vault and anyone can triggered it as it is permissionless. A major side-effect of an emergency settlement is that the vault will be locked up after the emergency settlement. No one is allowed to enter the vault and users are only allowed to exit from the vault by taking their proportional share of cash and strategy tokens. The reason is that after the emergency settlement, there will be some asset cash balance in the vault and this will cause no one to be able to enter the vault due to the require check at Line 218. This side-effect has been verified by reviewing the codebase and clarifying with the sponsors.\\n```\\nFile: VaultState.sol\\n    function enterMaturity(\\n        VaultState memory vaultState,\\n        VaultAccount memory vaultAccount,\\n        VaultConfig memory vaultConfig,\\n        uint256 strategyTokenDeposit,\\n        uint256 additionalUnderlyingExternal,\\n        bytes calldata vaultData\\n    ) internal returns (uint256 strategyTokensAdded) {\\n        // If the vault state is holding asset cash this would mean that there is some sort of emergency de-risking\\n        // event or the vault is in the process of settling debts. In both cases, we do not allow accounts to enter\\n        // the vault.\\n        require(vaultState.totalAssetCash == 0);\\n```\\n\\nIf an attacker could force an emergency settlement on a vault anytime, he would be able to perform a DOS on the vault since the vault will basically be locked up after it. The following demonstrates how this can be performed:\\nAssume that the total supply of BTP in the WETH/stETH Balancer Pool is 100,000 Therefore, the BPT threshold of the vault will be 20,000.\\nAssume that the total number of BPT held by the vault is 19,900.\\nNote that under normal circumstances, it is not possible for the users to exceed the BPT threshold because the transaction will revert if the `bptHeldAfterJoin > bptThreshold` after the user enters the vault.\\nNote that at this point, the emergency settlement CANNOT be triggered against the vault because the vault has not exceeded BPT threshold yet\\nBob (attacker) flash-loans a large amount of ETH from dydx where the fee is almost non-existence (1 Wei Only)\\nBob allocates a portion of his ETH to join the WETH/stETH Balancer Pool. This will cause the total supply of BPT to increase significantly to 200,000.\\nBob allocates a portion of his ETH to enter the vault and causes the total number of BPT held by the vault to increase by 150 from 19,900 to 20,050. This is allowed because the total supply of BPT has increased to 200,000, and thus the BPT threshold has increased to 40,000. Also, Bob does not leverage himself and does not borrow from Notional since the flash loan already provided him with access to a large number of funds, and thus he does not need to pay for any borrowing cost to minimize the cost of this attack.\\nAt this point, due to the inflow of 150 BPT to the Balancer Pool, the total supply of BPT increase from 200,000 to 200,150.\\nAfter entering the vault, Bob exits the WETH/stETH Balancer Pool entirely with all his 100,000 BPT position. This will cause the total supply of BPT to fall back to 100,150. Per my research, there is no exit fee when a Liquidity Provider exits a pool. Also, a Liquidity Provider only suffers a loss due to impermanent loss. However, since all these steps are executed within the same transaction, there is no impermanent loss because no one perform any token swap. Thus, there is no cost incurred by Bob for this step.\\nNote that at this point, the emergency settlement CAN be triggered against the vault because the vault has exceeded the BPT threshold. The total number of BPT held by the vault is 20,050, and the BPT threshold is 20,030 (=100,150 * 0.2).\\nAnyone can trigger the emergency settlement as it is permissionless. Bob triggered an emergency settlement against the vault, and 20 BPT will be sold off in the market so that the vault will not exceed the BPT threshold. It is important to ensure that the number of BPTs to be sold is kept as low as possible so that the total value of the vault will not be reduced by slippage during the trade. This is because Bob still owns the shares of the vault and he wants to get back as much of his original deposit as possible later. This value can be optimized further with Math.\\nAs mentioned earlier, after an emergency settlement, the vault will be locked up. No one is allowed to enter the vault and users are only allowed to exit from the vault by taking their proportional share of cash and strategy tokens.\\nBob proceeds to redeem all his shares from the vault. He will get back all of his deposits minus the 20 BPT slippage loss during the emergency settlement that is split proportionally among all vault shareholders which is insignificant. Note that the Notional's leverage vault does not impose any exit fee.\\nBob proceeds to repay back his loan and pay 1 wei as the fee to dydx.\\nThe cost of attack is 1 wei (flash-loan fee) + 20 BPT slippage loss during the emergency settlement that is split proportionally among all vault shareholders, which is insignificant. The slippage loss during emergency settlement can be minimized by causing the total number of BPT held by the vault to exceed the BPT threshold by the smallest possible value.\\nAll the above steps will be executed within a single block/transaction.
Short term, consider the following measures to mitigate the issue:\\nThe emergency settlement function is permissionless and can be called by anyone. It is recommended to implement access control to ensure that this function can only be triggered by Notional.\\nThere is no withdrawal fee. Also, there is no deposit fee as long as users did not borrow from Notional. Therefore, this attack is mostly profitable. It is recommended to impose a fee on the users of the vault even if the users did not borrow from Notional. All users should be charged a fee for the use of the vault. This will make the attack less likely to be profitable in most cases.\\nUsers can enter and exit the vault within the same transaction/block. This allows the attacker to leverage the flash-loan facility to reduce the cost of the attack to almost nothing. It is recommended to prevent users from entering and exiting the vault within the same transaction/block. If the user entered the vault in this block, he/she could only exit at the next block.\\nLong term, update the implementation of the vault so that the vault will not be locked up after an emergency settlement. After selling off the excess BPT, the vault should allow users to enter the vault as per normal.
Malicious users can lock up all the leverage vaults offered by Notional causing denial-of-service. This results in a loss of funds for the protocol as the vault is no longer generating profit for the protocol, and also a loss of funds for vault users as they cannot realize the profits because they are forced to exit the vault prematurely.\\nThe following are various reasons why someone would want to perform a DOS on Notional vaults:\\nDamage the reputation of Notional, and reduce users' trust in Notional\\nA competitor who is also offering a leverage vault attempts to bring down Notional\\nSomeone who shorted Notional's protocol token
```\\nFile: BalancerEnvironment.py\\n            "oracleWindowInSeconds": 3600,\\n            "maxBalancerPoolShare": 2e3, # 20%\\n            "settlementSlippageLimitPercent": 5e6, # 5%\\n```\\n