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

Forcing a borrower to pay a huge debt via the giveLoan()

Summary

The Lender::giveLoan() can be used by a rogue lender to force a borrower to pay a huge debt to repay their loan. Otherwise, their loan's collateral tokens will be seized.

Vulnerability Details

Even if allowing a lender to give a loan to another pool (via the giveLoan()) is a feature, a rogue lender can execute the giveLoan() to force the loan's principal debt to compound with the loan's interests (lenderInterest + protocolInterest).

After executing the giveLoan(), the loan's debt will become bigger. Specifically, the old debt will be compounded with the loan's interests to become the new bigger debt.

function giveLoan(
uint256[] calldata loanIds,
bytes32[] calldata poolIds
) external {
for (uint256 i = 0; i < loanIds.length; i++) {
...
// calculate the interest
(
uint256 lenderInterest,
uint256 protocolInterest
) = _calculateInterest(loan);
@> uint256 totalDebt = loan.debt + lenderInterest + protocolInterest;
...
// update the loan with the new info
loans[loanId].lender = pool.lender;
loans[loanId].interestRate = pool.interestRate;
loans[loanId].startTimestamp = block.timestamp;
loans[loanId].auctionStartTimestamp = type(uint256).max;
@> loans[loanId].debt = totalDebt;
...
}
}
  • Old debt will be compounded with the loan's interests: https://github.com/Cyfrin/2023-07-beedle/blob/658e046bda8b010a5b82d2d85e824f3823602d27/src/Lender.sol#L381

  • New bigger debt: https://github.com/Cyfrin/2023-07-beedle/blob/658e046bda8b010a5b82d2d85e824f3823602d27/src/Lender.sol#L420

Proof of Concept

Let's do a simple math for demonstration. Assume that:

  • Debt: $5

  • InterestRate: 0.1 (10%) per second

  • BorrowingLength: 10 seconds

Scenario 1: a borrower borrows the debt for 10 seconds and repays their loan

// Just a simple math for PoC
// InterestToPay already includes both the lenderFee and protocolFee
InterestToPay = InterestRate * Debt * BorrowingLength
= 0.1 * $5 * 10
= $5
TotalDebt = Debt + InterestToPay
= $5 + $5
= $10

Scenario 2: a borrower borrows the debt for 10 seconds and repays their loan but at the 5th second, a lender executes the giveLoan()

// Just a simple math for PoC
// InterestFirst5Sec already includes both the lenderFee and protocolFee
InterestFirst5Sec = InterestRate * Debt * BorrowingLength
= 0.1 * $5 * 5
= $2.5
TotalDebtAfter5Sec = Debt + InterestFirst5Sec
= $5 + $2.5
= $7.5 (this will become a new debt)
// InterestSecond5Sec already includes both the lenderFee and protocolFee
InterestSecond5Sec = InterestRate * NewDebt * BorrowingLength
= 0.1 * $7.5 * 5
= $3.75
TotalDebtAfter10Sec = NewDebt + InterestSecond5Sec
= $7.5 + $3.75
= $11.25

As you can see, a borrower must pay more debt to repay their loan. Imagine the case that an attacker (rogue lender) executes the giveLoan() multiple times; the borrower will be forced to pay a huge debt to repay their loan. Otherwise, their loan's collateral tokens will be seized.

Impact

An attacker (rogue lender) can execute the giveLoan() multiple times through the same pool or other Sybil pools (the pools generated by different lender addresses, but the same attacker controls them).

As a result, a borrower will be forced to pay a huge debt to repay their loan. Otherwise, their loan's collateral tokens will be seized. Hence, this issue is considered high severity.

Tools Used

Manual Review

Recommendations

Since an attacker (rogue lender) can exploit the issue through their controllable Sybil pools, we cannot address the issue by disallowing a lender to execute the giveLoan() on the same pool.

Thus, a borrower should have choices for mitigating the issue when borrowing a loan.

Two example choices:

  1. A borrower can disallow giving their loan to another pool via the giveLoan(). In this case, a lender could execute the startAuction() only.

  2. A borrower can allow giving their loan to another pool via the giveLoan(). In this case, a borrower should be able to limit the maximum times the giveLoan() can execute on their loan.

Support

FAQs

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