Skip to content

Latest commit

 

History

History
58 lines (49 loc) · 3.13 KB

065.md

File metadata and controls

58 lines (49 loc) · 3.13 KB

Powerful Honeysuckle Anteater

High

Adversary could always keep the USDT reserves as 0 by using flashloan attack on the redeemUSDT function

Summary

The redeemUSDT() function in CDS allows users to exchange USDA for USDT. However, there is no limit on how much an individual user can withdraw, and the function is not restricted to dCDS depositors. This means an adversary could use a flash loan to drain all the USDT, consistently keeping the USDT reserves empty.

Root Cause

redeemUSDT() just checks if we have the USDa balance we want to exchange reference:

    function redeemUSDT(
        CDSInterface.Interfaces memory interfaces,
        uint256 burnedUSDaInRedeem,
        uint128 usdaAmount,
        uint64 usdaPrice,
        uint64 usdtPrice
    ) external returns (uint256) {
        if (usdaAmount == 0) revert CDSInterface.CDS_NeedsMoreThanZero();
@>>     if (interfaces.usda.balanceOf(msg.sender) < usdaAmount) {
            revert CDSInterface.CDS_Insufficient_USDa_Balance();
        }
        // Increment burnedUSDaInRedeem
        burnedUSDaInRedeem += usdaAmount;
        IGlobalVariables.OmniChainData memory omniChainData = interfaces.globalVariables.getOmniChainData();
        omniChainData.burnedUSDaInRedeem += usdaAmount; //
        // burn usda
        bool transfer = interfaces.usda.burnFromUser(msg.sender, usdaAmount);
        if (!transfer) revert CDSInterface.CDS_TransferFailed(IBorrowing.AssetName.USDa);
        // calculate the USDT USDa ratio
       uint128 usdtAmount = ((usdaPrice * usdaAmount) / usdtPrice);

        interfaces.treasury.approveTokens(IBorrowing.AssetName.TUSDT, address(interfaces.cds), usdtAmount);
        // Transfer usdt to user
@>>     bool success = interfaces.usdt.transferFrom(address(interfaces.treasury), msg.sender, usdtAmount);
        if (!success) revert CDSInterface.CDS_TransferFailed(IBorrowing.AssetName.TUSDT);
        interfaces.globalVariables.setOmniChainData(omniChainData);
        return burnedUSDaInRedeem;
    }

Attack Path

  1. Adversary takes ETH flashloan.
  2. Adversary swaps the ETH for USDA on a Uniswap or other liquidity pool.
  3. Adversary calls redeemUSDT() in CDS contract and gets all the USDT out, by depositing USDA
  4. Adversary sells USDT for ETH to repay the loan.
  5. Done. For small amount of fees paid for the flashloan, the adversary just emptied the whole USDT reserve of the treasury.

Impact

This limits the ability of the protocol to control the peg stability mechanisms. The USDT redeemability was added exactly for this particular reason: https://docs.autonomint.com/autonomint/autonomint-1/autonomint/stablecoin-peg-stability#utilizing-usdt-deposited-in-dcds-for-immediate-re-deemability

Managing Stablecoin Demand 5.Immediate re-deemability - Users can re-deem USDa to get USDT immediately in case the price falls below $1

Mitigation

Consider adding limit on how much can be withdrawn per transaction, as well as per user. Also, consider limiting the function only to dCDS depositors.