20,000 USDC
View results
Submission Details
Severity: high
Valid

A malicious user can freeze all the auctioned loans.

Summary

The startAuction can be started by loan lender and any user can buy the loan using a poolId with enough balance and interest rate less than current auction rate but the loans can be compromised by using poolId that doesn't belong to the msg.sender calling the function.

Vulnerability Details

As in the buyLoan() there is no check to verify that poolId provided belongs to the msg.sender calling that function, A malicious user with no pools can use a poolId that doesn't belong to him but has enough poolBalance for loan and interestRate greater than currentAuctionRate calculated to acquire the loan and become the lender.

Impact

If a malicious user who may not have a pool or may have a pool with 0 outstanding loan and little pool balance becomes the lender of the loan, with poolId that doesn't belongs to him, it will cause the following issues:

  1. The poolBalance of poolId that the malicious user provided is decreased and is lost forever.

https://github.com/Cyfrin/2023-07-beedle/blob/658e046bda8b010a5b82d2d85e824f3823602d27/src/Lender.sol#L489C1-L490C53

src/Lender.sol
489: _updatePoolBalance(poolId, pools[poolId].poolBalance - totalDebt);
490: pools[poolId].outstandingLoans += totalDebt;
  1. The loan borrower can neither repay nor refinance his/her loan as poolId is obtained from:

poolId = keccak256(abi.encode(lender, loanToken, collateralToken));

the poolId of the malicious user wouldn't exist or if existed would not have enough outstanding loan and pool balance and revert because both repay() and refinance() decrease the poolBalance and outstandingLoands for the poolId of the lender:

_updatePoolBalance(poolId, pools[poolId].poolBalance + loan.debt + lenderInterest);
pools[poolId].outstandingLoans -= loan.debt;

If malicious user had a poolId, the outstandingLoan will be less than loan.debt which will surely revert with underflow and If malicious user didn't have a poolId, pools[poolId].outstandingLoans would also be 0, and it would also revert with underflow causing both refinance and repay to revert.

  1. Loan also can't be seized as it also decreases the outstandingLoans of the poolId in the seizeLoan() which will surely revert:

pools[poolId].outstandingLoans -= loan.debt;

Hence, loan is stuck and freezed forever, meaning borrower won't be able to retrieve his/her collateral and the poolId which the malicious user provided will also lose poolBalance.

Tools Used

Manual Analysis

Recommendations

In the buyLoan() function, after loans[loanId].lender is updated to msg.sender i.e

https://github.com/Cyfrin/2023-07-beedle/blob/658e046bda8b010a5b82d2d85e824f3823602d27/src/Lender.sol#L518

Add the following check after the above line:

if (poolId != getPoolId(msg.sender, loan.loanToken, loan.collateralToken)) revert poolIdConfig();

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.