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

User can buy loan for much less than it actually costs

Summary

The only requirement for a user to buy a loan is to have his pool, but his pool may consist of
{ loanToken = {doge coin address} (real world value less than 0.1 USD); } and set a pool balance of 100.
While the actual loan from the selling pool is {loanToken = {eth address}, collateralToken = {btc address} and the loan is for value of 1 (eth), which is a way more expensive than 100 doge coin.

Vulnerability Details

PoC

  • 1 - Alice creates a pool {loanToken: ETH, collateralToken: BTC} with {maxLoanRatio : 10 * 1e18}

  • 2 - Alice fill the pool balance with 10 ETH ($18 846.10).

  • 3 - Bob borrow 10 ETH for collateral of 1 BTC ($29 000.00) from Alice's pool.

  • 4 - AliceFromProfile2 creates a pool {loanToken: DOGE, collateral: DOGE} with same other values as the first pool.

  • 5 - AliceFromProfile2 fill the pool with 100 DOGE ($8).

  • 6 - Alice create an auction for the loan of Bob.

  • 7 - AliceFromProfile2 buy her own loan of 10 ETH ($18846.10) for 10 DOGE ($0.80).
    Now Alice is able to withdraw her 10 ETH ($18846.10), because the system points that her pool balance is full and don't have outstanding loans.

  • 8 - Currently Bob collateral of 1 BTC ($29 000.00) could be adopted by AliceFromProfile2 for the cost of 10 DOGE ($0.80).

Impact

There are a lot of problems here. Even if we assume that borrower will always pay back and receive his collateral. Everyone is happy? Not quite still.
Smart Alice could reuse one valuable asset, which costs a lot more than the one with which she is buying her loan after that. Imagine she is only using 1 of her ETH and then buy the loan for less than a dollar and her pool balance is back to 1 ETH. Now imagine doing that over and over again, because everything she needs per 1 ETH is 1 DOGE ... This way she could very fast drain Lender.sol pool of the valuable assets and also obtain interests for assets that in reality she didn't possessed.

Tools Used

Manual Analysis

Recommendations

  • One solution is to make sure one could buy others loans, only if the assets in his pool are the same as those in the seller, or at least loanToken.

function buyLoan(uint256 loanId, bytes32 poolId) public {
// get the loan info
Loan memory loan = loans[loanId];
// validate the loan
if (loan.auctionStartTimestamp == type(uint256).max)
revert AuctionNotStarted();
if (block.timestamp > loan.auctionStartTimestamp + loan.auctionLength)
revert AuctionEnded();
// Check if the tokens are the same
if(loan.loanToken != pools[poolId].loanToken) revert TokenMismatch();
if(loan.collateralToken != pools[poolId].collateralToken) revert TokenMismatch();
// calculate the current interest rate
uint256 timeElapsed = block.timestamp - loan.auctionStartTimestamp;
uint256 currentAuctionRate = (MAX_INTEREST_RATE * timeElapsed) /
loan.auctionLength;
// validate the rate
if (pools[poolId].interestRate > currentAuctionRate)
revert RateTooHigh();
// calculate the interest
(uint256 lenderInterest, uint256 protocolInterest) = _calculateInterest(
loan
);

Support

FAQs

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