Skip to content
This repository has been archived by the owner on Dec 17, 2023. It is now read-only.

Latest commit

 

History

History
98 lines (77 loc) · 4.18 KB

045.md

File metadata and controls

98 lines (77 loc) · 4.18 KB

tsvetanovv

high

It is possible for a user to completely redeem an amount of asset from Iron Bank, but not leave the market

Summary

It is possible for a user to completely Redeem an amount of asset from Iron Bank, but not leave the market. This situation is happening in redeem() function:

function redeem(address from, address to, address market, uint256 amount)
        external
        nonReentrant
        isAuthorized(from)
    {
        DataTypes.Market storage m = markets[market];
        require(m.config.isListed, "not listed");

        _accrueInterest(market, m);

        uint256 userSupply = m.userSupplies[from];
        uint256 totalCash = m.totalCash;
  
        uint256 ibTokenAmount;
        bool isRedeemFull;
        if (amount == type(uint256).max) {  
            ibTokenAmount = userSupply;
            amount = (ibTokenAmount * _getExchangeRate(m)) / 1e18;
            isRedeemFull = true;
        } else {
            ibTokenAmount = (amount * 1e18) / _getExchangeRate(m);
        }
  
        require(userSupply >= ibTokenAmount, "insufficient balance");
        require(totalCash >= amount, "insufficient cash");
  
        // Update storage.
        unchecked {
            m.userSupplies[from] = userSupply - ibTokenAmount;
            m.totalCash = totalCash - amount;
            // Underflow not possible: ibTokenAmount <= userSupply <= totalSupply.
            m.totalSupply -= ibTokenAmount;
        }
  
        // Check if need to exit the market.
        if (isRedeemFull && _getBorrowBalance(m, from) == 0) {
            _exitMarket(market, from);
        }  

        IBTokenInterface(m.config.ibTokenAddress).burn(from, ibTokenAmount); // Only emits Transfer event.
        IERC20(market).safeTransfer(to, amount);

        _checkAccountLiquidity(from);

        emit Redeem(market, from, to, amount, ibTokenAmount);
    }

Vulnerability Detail

Consider the following situation:

  • Alice wants to redeem a full amount of an asset from the Iron Bank and exit the market.
  • To redeem the full amount she needs to put 100 amount of tokens.
  • After Alice does this we go to the following code:
420:        bool isRedeemFull;
421:        if (amount == type(uint256).max) {  
422:            ibTokenAmount = userSupply;
423:            amount = (ibTokenAmount * _getExchangeRate(m)) / 1e18;
424:            isRedeemFull = true;
425:        } else {
426:            ibTokenAmount = (amount * 1e18) / _getExchangeRate(m);
427:        }
  • Alice actually buys all her tokens (100) and wants to get out of the market, but doesn't actually enter the if statement.
  • The condition amount == type(uint256).max checks whether the amount parameter is equal to type(uint256).max, which is the maximum value representable by a uint256 variable, and Alice enters the if statement.
  • Because she does not enter into the if statement, isRedeemFull is never set to true and stays false.
  • Then we go to the following code in which Alice should exit the market but actually does not pass the check because isRedeemFull remains false
440:        // Check if need to exit the market.
441:        if (isRedeemFull && _getBorrowBalance(m, from) == 0) {
442:            _exitMarket(market, from);
443:        }

Impact

If the user not entering the _exitMarket() function means that the user's account will still be considered as having an open position in the market. The outstanding borrow balance will remain, and the user will continue to accrue interest on their borrowings.

Code Snippet

https://github.com/sherlock-audit/2023-05-ironbank/blob/main/ib-v2/src/protocol/pool/IronBank.sol#L406-L451

Tool used

Manual Review

Recommendation

In the if statement, check how many tokens a user can have, not the maximum number. like this if statement is executed and when a user wants to redeem everything isRedeemFull will be set to true.