When the withdraw() function is called in the lendingPool, it calls the withdraw() in the reserveLibrary which makes sure aomunt >= 1 and then further calls the burn function in the RToken.sol. The issue lies in the fact that the amount burnt is amount.rayDiv(liquidationIndex) which can be 0 but the crvUSD transferred is of amount amount
. Users can manipulate this error and drain the entire lending pool.
Assume the following scenario:
A user has a non-zero balance (by depositing some amount first into the lendingPool). Current LiquidationIndex = 3e27 (note that liquidationIndex always increases and never decreases so over time it will reach this amount).
The user calls the withdraw with amount = 1.
the burn function in the RToken makes sure that 1 <= balanceOf(user) (which is satisfied since he has deposited first).
Then it calls the _burn(from, amount.toUint128());
function which internally tries to burn amount.rayDiv(liquidationIndex) => 1.rayDiv(3e27) => (1*RAY + (3e27/2))/3e27 => (1e27+1e27)/3e27 => 0
(this is the calculation in the WADRAYMATH with all the rounding)
So 0 amount will be burnt from the user's internal balance.
Then 1 _assetAddress
token will be transferred to the user.
Thus essentially the user has withdrawn 1 wei crvUSD without reducing his balance. The user can continue using this method to completely drain the lending pool and thus the other users funds.
The complete draining of the lending Pool. Note that as the liquidation index increases this attack becomes more profitable for the attacker. Even if the attack is not profitable for the user, the attacker can make the other users funds = 0. This should make the bug a HIGH severity one.
manual Review
Make sure that the burnt amount ! = 0.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.