Boost _ Folks Finance 33816 - [Smart Contract - Critical] Attacker can get unlimited loan for some minimum deposit due to the incorrect calculation of user health in getLoanLiquidity
Submitted on Tue Jul 30 2024 02:49:29 GMT-0400 (Atlantic Standard Time) by @OxAnmol for Boost | Folks Finance
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Protocol insolvency
Description
Brief/Intro
UserLoanLogic:getLoanLiquidity is used in the function isLoanOverCollateralized to calculate the user's available collateral value when they borrow or withdraw. The user's asset value depends on the length of the userLoan.colsPool[], which can be manipulated with minimal deposits. Because of this, collateral will be calculated incorrectly, leading to undesirable borrowing.
Vulnerability Details
There is no restriction on the minimal amount of collateral deposit for users. When a user deposits tokens, the fAmount is calculated based on the depositInterestIndex. If this index is greater than the user-provided amount * 1e18 (which is generally always the case), then the fAmount is rounded down to 0.
If the fAmount is zero, based on the logic of UserLoanLogic:increaseCollateral, the loanId (deposit token) is pushed to the userLoan.colsPool[].
functionincreaseCollateral(LoanManagerState.UserLoanstorage loan,uint8 poolId,uint256 fAmount) external {// if the balance was previously zero, add pool to list of user loan collateralsif (loan.collaterals[poolId].balance ==0) loan.colPools.push(poolId); loan.collaterals[poolId].balance += fAmount;}
Now, when a user’s fAmount is non-zero, it is also pushed to the colsPool[], and loan.collaterals[poolId].balance is incremented to a non-zero value.
When the user goes to borrow, their collateral value is calculated based on the balance stored in the mapping by looping through the userLoan.colsPool[]. This will inflate the value of the user's collaterals because colsPool[] may contain the same poolId multiple times, and for each poolId, the collateral value is calculated assuming they all have a non-zero amount stored in the loan.collaterals[poolId].balance mapping.
Because of this, a user can borrow a large amount and potentially drain the whole pool with this inflated collateral value for some minimal deposit.
User deposit 1 wei or 0 (if the token doesn’t revert on 0 deposits) 10 times(can be any number).
Because of toFAmount calculation the 1 wei is rounded to 0 fAmount.
As the amount is 0 the poolId is pushed to userLoan.colsPool[] , this array will now contain [poolId…n] and loan.collaterals[poolId].balance is 0.
Now the attacker deposit some actual amount let’s say 1e18(ETH). userLoan.colsPool[] now contains [poolId…n + poolId] and loan.collaterals[poolId].balance is updated to 1e18(1 ETH).
user now and borrow 7.5 ETH with just 1 ETH deposit, if we only considered the 10 empty deposits.
This happend because in getLoanLiquidity , protocol is assuming that colsPool[] only contains unique poolId .
As you can see that attacker have a power to drain almost all the available funds in the system with minimal collateral deposit.
Impact Details
This attack can lead to the loss of all the user funds and lead protocol to insolvency.