code-423n4 / 2024-06-size-validation

1 stars 0 forks source link

Any existing loan can be bought by anyone #708

Closed c4-bot-6 closed 4 months ago

c4-bot-6 commented 4 months ago

Lines of code

https://github.com/code-423n4/2024-06-size/blob/8850e25fb088898e9cf86f9be1c401ad155bea86/src/libraries/AccountingLibrary.sol#L81

Vulnerability details

Impact

Any existing loan can be bought because the default value of forSale is set to true.

Proof of Concept

In AccountingLibrary::createCreditPosition and AccountingLibrary::createDebtAndCreditPositions the value of forSale parameter is set to true

    function createDebtAndCreditPositions(
        State storage state,
        address lender,
        address borrower,
        uint256 futureValue,
        uint256 dueDate
    ) external returns (CreditPosition memory creditPosition) {
        DebtPosition memory debtPosition =
            DebtPosition({borrower: borrower, futureValue: futureValue, dueDate: dueDate, liquidityIndexAtRepayment: 0});

        uint256 debtPositionId = state.data.nextDebtPositionId++;
        state.data.debtPositions[debtPositionId] = debtPosition;

        emit Events.CreateDebtPosition(debtPositionId, borrower, lender, futureValue, dueDate);

        creditPosition = CreditPosition({
            lender: lender,
            credit: debtPosition.futureValue,
            debtPositionId: debtPositionId,
@>          forSale: true       // value is set to true
        });

        uint256 creditPositionId = state.data.nextCreditPositionId++;
        state.data.creditPositions[creditPositionId] = creditPosition;
        state.validateMinimumCreditOpening(creditPosition.credit);
        state.validateTenor(dueDate - block.timestamp);

        emit Events.CreateCreditPosition(creditPositionId, lender, debtPositionId, RESERVED_ID, creditPosition.credit);

        state.data.debtToken.mint(borrower, futureValue);
    }
    function createCreditPosition(State storage state, uint256 exitCreditPositionId, address lender, uint256 credit)
        external
    {
        CreditPosition storage exitCreditPosition = state.getCreditPosition(exitCreditPositionId);
        if (exitCreditPosition.credit == credit) {
            exitCreditPosition.lender = lender;

            emit Events.UpdateCreditPosition(
                exitCreditPositionId, lender, exitCreditPosition.credit, exitCreditPosition.forSale
            );
        } else {
            uint256 debtPositionId = exitCreditPosition.debtPositionId;

            reduceCredit(state, exitCreditPositionId, credit);

            CreditPosition memory creditPosition =
@>              CreditPosition({lender: lender, credit: credit, debtPositionId: debtPositionId, forSale: true}); //forSale is set to true

            uint256 creditPositionId = state.data.nextCreditPositionId++;
            state.data.creditPositions[creditPositionId] = creditPosition;
            state.validateMinimumCreditOpening(creditPosition.credit);

            emit Events.CreateCreditPosition(creditPositionId, lender, debtPositionId, exitCreditPositionId, credit);
        }
    }

Setting forSale to true results in anyone buying this loan from the original owner. This will insolvent the protocol as anyone will buy out anyone's loan offer at any time without the permission of the original lender.

Tools Used

Manual review

Recommended Mitigation Steps

set forSale to false as default. Users can set it to true using SetUserConfiguration

Assessed type

Other