Incorrect calculation of value of deposit leading to over minting of stalk
Report ID
#29063
Report type
Smart Contract
Has PoC?
Yes
Target
https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5
Impacts
Illegitimate minting of protocol native assets
Description
The vulnerability happens when we want to calculate the value of a LP curve deposit. To do this we call LibTokenSilo.beanDenominatedValue(Token, Amount) where to Token is the address of the curve metapool and amount is the number of tokens we want to deposit. This function calls LibBeanMetaCurve.bdv(amount).
The vulnerability happens in when we get the balances by using get_previous_balances this function is called to to protect against flash loans but it still is not enough to protect against other types of market manipulation.
To see why this is problematic assume that currently that using market manipulation[^1] or just waiting for the right time, an attacker starts the block with 1 LP token worth 1.05 beans[^2]. Now to buy stalk with a 5% premium one just buys LP tokens and deposits them instead of immediately depositing the bean.
Note that this also allows to increase the value of current deposits, as one can add 3crv to the pool until the number of tokens is equal then add even more 3crv such that bean will be above peg allowing to call convert with current deposits.
Suggestion for fix
There are to parts of this vulnerability when you convert a deposit and when you just deposit.
Convert
For convert the solution is simple, when converting from beans don't use a bdv function instead just use the number of beans that we used to convert. This solution will protect against every other vulnerability's of this kind.
Deposit
The main problem is that we use one specific time to calculate the value of bdv, to fix this one should use a time waited average of the last deposits. For this one can use get_twap_balances and get_price_cumulative_last of the pool. Thus allowing to find the continues balances of for example the last hour only use one specific point in time that can be both volatile and easily manipulated.
[^1]: this is made easier as the liquidity of the pool has decreased considerably and is now only 165,041$. As of writing the LP is already at 1.05 bean per LP.
[^2]:This can be even bigger historically, this is just a value chosen because as of time of writing this is the value of LP.
Proof of concept
general info about POC
All test were made on block number 19371615 (no market manipulation is used on the previous_balances).
All POC assume that they already have enough 3crv and bean tokens.
Before every POC we will call equalDeposit to make the math easier. This function hurts the exploiter as we get more LP if we add 3crv as liquidity to the metapool when the pool is not balanced. This function also changes the value of bdv, this happens as add_liqudity calls for update that updates previous_balances to the balance of the last block.
function equalDeposit() internal{
uint[2] memory b1 = ICurvePool(CURVE_BEAN_METAPOOL).get_balances();
uint b1_diff = b1[0] / decimals(BEAN) - b1[1] / decimals(THREE_CRV);
IERC20(THREE_CRV).approve(CURVE_BEAN_METAPOOL, b1_diff*decimals(THREE_CRV));
ICurvePool(CURVE_BEAN_METAPOOL).add_liquidity([0, b1_diff *decimals(THREE_CRV)], 0);
}POC 1: Deposit LP coins
Print of POC1(1000)
amount: 1000
bdv of one lp: 1054028
number of coins in balances:
BEAN: 98020
3Crv: 98020
BEAN3CRV-f: 2019
call deposit
bdv of deposit: 2128
print of POC1(10000)
amount: 10000
bdv of one lp: 1054028
number of coins in balances:
BEAN: 98020
3Crv: 98020
BEAN3CRV-f: 20194
call deposit
bdv of deposit: 21285
Note that in both we get a about a 5.4% increase.
POC 2: convert deposit
print of POC2(10000):
depoited into beanstalk: 10001
deposit_amount: 10000
bdv of one lp: 1054028
number of coins in balances:
BEAN: 98020
3Crv: 98020
LP earn with add_liquidity:
BEAN3CRV-f: 10047
number of coins in balances:
BEAN: 98019
3Crv: 108019
bdv before and after: 10000 10690BIC Response
This is not a valid bug report because it describes expected behavior. The report describes expected risks associated with a whitelisted pool having low liquidity and holding Bean exposure over time.
The reported impact in scope is "Illegitimate minting of protocol native assets", but at no point are protocol native assets illegitimately minted.
For these reasons, we are closing the submission and no reward will be issued.