#47115 [SC-Critical] Missing Settlement Status Validation in Loan Operations

Submitted on Jun 9th 2025 at 02:57:53 UTC by @Catchme for IOP | Term Structure Institutional

  • Report ID: #47115

  • Report Type: Smart Contract

  • Report severity: Critical

  • Target: https://github.com/term-structure/tsi-contract/blob/main/src/Settlement.sol

  • Impacts:

    • Protocol insolvency

    • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Description

Brief/Intro

Critical settlement status validation is missing in multiple loan operations in the Settlement contract, allowing users to perform operations on unsettled loans. This vulnerability enables attackers to steal funds, manipulate loan states, and bypass the intended settlement process flow, potentially resulting in significant direct financial losses to both lenders and borrowers.

Vulnerability Details

In the Settlement contract's architecture, loans must first be settled through the settle() function before any loan operations can be performed. This function executes the core fund transfers and marks the loan as settled by setting loans[loanId].settled = true. However, several critical loan operations fail to verify this settlement prerequisite:

// Example of missing validation in liquidate() function - lines 335-356
function liquidate(string memory _loanId, uint256 liquidationAmt) external nonReentrant {
    bytes32 loanId = _loanId.toBytes32();
    LoanInfo memory loanInfo = loans[loanId];
    // Missing validation: if (!loanInfo.settled) revert LoanNotSettled(loanId);
    
    (uint256 collateralToLiquidator, uint256 collateralToProtocol) =
        loanInfo.liquidate(loanId, _oracle, _minimumDebtValue, liquidationAmt);
        
    // Transfers happen without settlement confirmation
    IERC20(loanInfo.debtTokenAddr).safeTransferFrom(msg.sender, loanInfo.lender, liquidationAmt);
    IERC20(loanInfo.collateralTokenAddr).safeTransferFrom(loanInfo.lender, msg.sender, collateralToLiquidator);
    IERC20(loanInfo.collateralTokenAddr).safeTransferFrom(loanInfo.lender, _feeCollector, collateralToProtocol);
    // ...
}

The affected functions include:

  • repay() (line 277)

  • removeCollateral() (line 296)

  • addCollateral() (line 307)

  • liquidate() (line 335)

  • delivery() (line 324)

This contrasts with addCollateralBeforeSettle() and settle() which correctly implement settlement status validation:

Impact Details

This vulnerability creates multiple attack vectors with direct financial impact:

  1. Direct Fund Theft via Operations on Unsettled Loans:

    • Attackers can call liquidate() on unsettled loans, extracting collateral tokens from lenders who approved allowances expecting the normal settlement flow

    • Users can call addCollateral() on unsettled loans, causing immediate token transfers without loan establishment

    • Both scenarios result in immediate and irrecoverable financial loss

  2. Protocol State Corruption:

    • Premature repay() calls can manipulate loan parameters on unsettled loans

    • removeCollateral() can be called before proper collateralization is established

    • These operations break the protocol's accounting system and create inconsistent state

Proof of Concept

POC

Was this helpful?