sherlock-audit / 2024-09-predict-fun-judging

5 stars 4 forks source link

Nave - Borrowers Will Have The Ability To Extend The Loan Duration As They Like #291

Open sherlock-admin2 opened 1 month ago

sherlock-admin2 commented 1 month ago

Nave

Medium

Borrowers Will Have The Ability To Extend The Loan Duration As They Like

Summary

The missing check for loan and proposal duration will cause borrower to extend their loan duration as they like.

Root Cause

In PredictDotLoan::_refinance, below conditions have been fulfilled. PredictDotLoan::_refinance

But there is 1 missing conditions to be fulfilled which is to check the loan.startTime + loan.minimumDuration must be greater than or equal block.timestamp + proposal.duration.

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

  1. The borrower call acceptLoanOffer with proposal.duration = 604800 (1 week) which create a new loan with minimumDuration of 604800.
  2. After 6 days has passed, the borrower need 1 more week.
  3. The borrower decided to refinance with current loan to new loan (Proposal) with the duration of 604800.
  4. The refinance went succesfully because there are no checks around the duration between loan and the proposal.
  5. Borrower has an additional loan duration of 7 days.
  6. Borrower done it multiple times until the collateral price drops or they don't want to repay the debt to get their collateral anymore.

Impact

The borrower has the ability to extend their loan duration as much as they want. Moreover, this condition allow borrowers to refinance their loan and chose the longer duration to extend their loan duration even until forever without being liquidated.

PoC

No response

Mitigation

function _refinance(
        Refinancing calldata refinancing
    ) private returns (uint256 id, Loan memory newLoan, uint256 protocolFee) {
        Proposal calldata proposal = refinancing.proposal; // @e the proposal for the new loanOffer
        _assertProposalIsLoanOffer(proposal);

        Loan storage loan = loans[refinancing.loanId];

        _assertLoanStatus(loan.status, LoanStatus.Active);

        address borrower = loan.borrower;
        _assertLenderIsNotBorrower(borrower, proposal.from);

        _assertNewLenderIsNotTheSameAsOldLender(proposal.from, loan.lender);

        _assertNotExpired(proposal.validUntil);

        _assertMatchingProtocolFeeBasisPoints(proposal.protocolFeeBasisPoints);

+       if (loan.startTime + loan.minimumDuration < block.timestamp + proposal.duration) revert UnexpectedNewLoanDuration();

        if (msg.sender != borrower) {
            if (loan.startTime + loan.minimumDuration > block.timestamp + proposal.duration) {
                revert UnexpectedDurationShortening();
            }
        }

        uint256 positionId = _derivePositionId(proposal);
        if (positionId != loan.positionId) {
            revert PositionIdMismatch();
        }

        _assertQuestionPriceUnavailable(proposal.questionType, proposal.questionId);

        _assertValidInterestRatePerSecond(proposal.interestRatePerSecond);