code-423n4 / 2023-12-initcapital-findings

3 stars 3 forks source link

`LendingPool` interest should not accrue when pool's repay is paused #27

Closed c4-bot-8 closed 10 months ago

c4-bot-8 commented 10 months ago

Lines of code

https://github.com/code-423n4/2023-12-initcapital/blob/main/contracts/core/InitCore.sol#L535 https://github.com/code-423n4/2023-12-initcapital/blob/main/contracts/lending_pool/LendingPool.sol#L155-L169

Vulnerability details

Impact

When the canRepay status of pools inside InitCore is paused, users are not allowed to repay their positions when borrowing from the paused pool. However, interests continue to accrue during this pause period, exposing users to potential liquidation risk once the canRepay flag is allowed again.

Proof of Concept

It can be observed that when canRepay flag is set to false inside the config, users are not allowed to repaid his position.

https://github.com/code-423n4/2023-12-initcapital/blob/main/contracts/core/InitCore.sol#L530-L551

    function _repay(IConfig _config, uint16 _mode, uint _posId, address _pool, uint _shares)
        internal
        returns (address tokenToRepay, uint amt)
    {
        // check status
>>>     _require(_config.getPoolConfig(_pool).canRepay && _config.getModeStatus(_mode).canRepay, Errors.REPAY_PAUSED);
        // get position debt share
        uint positionDebtShares = IPosManager(POS_MANAGER).getPosDebtShares(_posId, _pool);
        uint sharesToRepay = _shares < positionDebtShares ? _shares : positionDebtShares;
        // get amtToRepay (accrue interest)
        uint amtToRepay = ILendingPool(_pool).debtShareToAmtCurrent(sharesToRepay);
        // take token from msg.sender to pool
        tokenToRepay = ILendingPool(_pool).underlyingToken();
        IERC20(tokenToRepay).safeTransferFrom(msg.sender, _pool, amtToRepay);
        // update debt on the position
        IPosManager(POS_MANAGER).updatePosDebtShares(_posId, _pool, -sharesToRepay.toInt256());
        // call repay on the pool
        amt = ILendingPool(_pool).repay(sharesToRepay);
        // update debt on mode
        IRiskManager(riskManager).updateModeDebtShares(_mode, _pool, -sharesToRepay.toInt256());
        emit Repay(_pool, _posId, msg.sender, _shares, amt);
    }

However, accrueInterest still can be called and not considering the status of repay inside the InitCore contract.

https://github.com/code-423n4/2023-12-initcapital/blob/main/contracts/lending_pool/LendingPool.sol#L155-L169

    /// @inheritdoc ILendingPool
    function accrueInterest() public {
        uint _lastAccruedTime = lastAccruedTime;
        if (block.timestamp != _lastAccruedTime) {
            uint _totalDebt = totalDebt;
            uint _cash = cash;
            uint borrowRate_e18 = IIRM(irm).getBorrowRate_e18(_cash, _totalDebt);
            uint accruedInterest = (borrowRate_e18 * (block.timestamp - _lastAccruedTime) * _totalDebt) / ONE_E18;
            uint reserve = (accruedInterest * reserveFactor_e18) / ONE_E18;
            if (reserve > 0) {
                _mint(treasury, _toShares(reserve, _cash + _totalDebt + accruedInterest - reserve, totalSupply()));
            }
            totalDebt = _totalDebt + accruedInterest;
            lastAccruedTime = block.timestamp;
        }
    }

This will allow the debt to continue growing while users cannot repay their positions, exposing the risk of getting liquidated once the canRepay status is allowed again by the admin.

Tools Used

Manual review

Recommended Mitigation Steps

Inside accrueInterest, check the canRepay status inside InitCore; if it's paused, return early and do not accrue the interest.

Assessed type

Invalid Validation

c4-judge commented 10 months ago

hansfriese marked the issue as duplicate of #17

c4-judge commented 10 months ago

hansfriese marked the issue as satisfactory