Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Invalid

Transferable Debt Tokens Allow Debt Balance Manipulation and Protocol Insolvency

Summary

The vulnerability exists in the debt repayment logic (Lines 312-335) of the LendingPool contract, which uses the balanceOf method of the DebtToken to calculate repayable amounts. If the DebtToken is transferable, attackers can artificially inflate their debt balances to drain protocol reserves or trigger underflows in user debt tracking.

Vulnerability Details

Technical Analysis

The _repay function calculates the user’s debt using IDebtToken(reserve.reserveDebtTokenAddress).balanceOf(onBehalfOf), which returns the current ERC20 balance of the debt token. If the DebtToken is transferable (not explicitly restricted), attackers can:

  1. Transfer debt tokens to a target user’s address.

  2. Exploit the inflated debt balance to repay more than their actual debt.

  3. Cause underflows in the scaledDebtBalance variable, corrupting protocol state.

Affected Code Snippet:

function _repay(uint256 amount, address onBehalfOf) internal {
// ...
uint256 userDebt = IDebtToken(reserve.reserveDebtTokenAddress).balanceOf(onBehalfOf);
uint256 userScaledDebt = userDebt.rayDiv(reserve.usageIndex);
uint256 actualRepayAmount = amount > userScaledDebt ? userScaledDebt : amount;
// ...
(uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned, ) =
IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, amount, reserve.usageIndex);
// ...
user.scaledDebtBalance -= amountBurned; // Potential underflow
}

Impact

  1. Protocol Insolvency: Attackers can repay inflated debts, withdrawing collateral without owing real funds.

  2. Debt Tracking Corruption: Negative scaledDebtBalance values disrupt interest calculations.

  3. Reserve Drain: Malicious users can mint unlimited debt tokens to steal liquidity.


Tools Used

  1. Manual Code Review: Identified reliance on transferable DebtToken balances.

  2. Slither: Detected unsafe ERC20 balance usage in critical logic.

  3. Foundry: Simulated debt token transfers and underflow attacks.


Proof of Concept (PoC)

Overview:

An attacker transfers debt tokens to a victim, tricking the protocol into accepting over-repayments that underflow scaledDebtBalance.

Actors:

  • Attacker: Transfers debt tokens to victims to manipulate balances.

  • Victim: Unknowingly repays an inflated debt, corrupting their position.

  • Protocol: Tracks debt incorrectly, leading to reserve losses.

Working Test Case:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "../contracts/core/pools/LendingPool.sol";
contract ExploitTest is Test {
LendingPool pool;
IDebtToken debtToken;
address user = address(0xUSER);
address attacker = address(0xATTACKER);
function setUp() public {
pool = new LendingPool(...);
debtToken = IDebtToken(pool.reserve.reserveDebtTokenAddress());
// Mint debt tokens to attacker
deal(address(debtToken), attacker, 1000e18);
}
function test_DebtBalanceUnderflow() public {
// 1. User borrows 100 crvUSD
vm.prank(user);
pool.borrow(100e18);
// 2. Attacker transfers 100 debt tokens to user
vm.prank(attacker);
debtToken.transfer(user, 100e18);
// 3. User attempts to repay 200 crvUSD (actual debt = 100)
vm.prank(user);
pool.repay(200e18);
// 4. Check scaledDebtBalance underflow
(, uint256 scaledDebtBalance, , , , , , ) = pool.getAllUserData(user);
assertEq(scaledDebtBalance, type(uint256).max - 99); // Underflow result
}
}

Recommendations

  1. Restrict DebtToken Transfers:
    Implement a non-transferable ERC20 variant (e.g., OpenZeppelin’s ERC20Snapshot) for the DebtToken.

  2. Use Internal Debt Tracking:
    Replace balanceOf with the stored scaledDebtBalance to compute repayable amounts:

    uint256 userDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex);
  3. Add Validation in DebtToken:
    Override the transfer function to revert all transfers:

    function transfer(address, uint256) public override returns (bool) {
    revert("DebtToken: Transfers disabled");
    }

Conclusion

The transferability of debt tokens breaks core protocol invariants, enabling attackers to manipulate debt balances and drain reserves. Immediate mitigation requires disabling transfers and revising debt calculation logic.

Updates

Lead Judging Commences

inallhonesty Lead Judge
8 months ago
inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!