Skip to content

Latest commit

 

History

History
71 lines (45 loc) · 3.07 KB

081.md

File metadata and controls

71 lines (45 loc) · 3.07 KB

Rapid Walnut Wasp

High

Risk of overpayment due to race condition between repayBorrowFresh and liquidateBorrowFresh.

Summary

The Numa protocol has a critical race condition vulnerability between repayBorrowFresh and liquidateBorrowFresh transactions that can lead to double payment scenarios.

Root Cause

The issue arises when a borrower sends a transaction to repay their debt position when it becomes liquidatable, but a liquidateBorrowFresh transaction is executed just before the user's repayBorrowFresh transaction.

The main entry points that can lead to this race condition are:

repayBorrowInternal() - Called by users directly repaying their loans repayBorrowBehalfInternal() - Called by third parties repaying on behalf of borrowers liquidateBorrowInternal() - Called by liquidators

// In CToken.sol
function liquidateBorrowFresh(...) {
    // First repayment happens here
    uint actualRepayAmount = repayBorrowFresh(liquidator, borrower, repayAmount);
    // Seizes collateral
    require(cTokenCollateral.seize(liquidator, borrower, seizeTokens) == NO_ERROR, "token seizure failed");
}

function repayBorrowFresh(...) {
    // Second repayment happens here
    uint accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;
    accountBorrows[borrower].principal = accountBorrowsNew;
}

All these functions ultimately flow through to repayBorrowFresh, which handles the actual debt reduction logic. The critical path occurs in liquidateBorrowFresh where it calls repayBorrowFresh internally before seizing collateral. This creates a window where a user's direct repayment transaction could execute after the liquidation has already processed a repayment.

The state changes happen in repayBorrowFresh where the borrower's debt is reduced:

uint accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;
accountBorrows[borrower].principal = accountBorrowsNew;

If a user's repayBorrowFresh executes after liquidateBorrowFresh but before the transaction is mined, they will process another repayment against their already-reduced debt balance. This results in the borrower paying twice - once through the liquidation and once through their direct repayment.

Internal pre-conditions

Looking at CToken.sol, the race condition can occur naturally, legitimate bots running liquidation operations and regular users trying to save their positions will naturally tend to submit transactions around the same time. The race condition emerges from this normal, expected behavior rather than from any malicious exploitation.

External pre-conditions

No response

Attack Path

No response

Impact

The impact is high , as the users ends up paying twice their debt.

PoC

No response

Mitigation

No response