code-423n4 / 2023-11-kelp-findings

13 stars 11 forks source link

More RsETH token can be minted by manipulating the ETH/RsETH price ratio in the Oracle contract #759

Closed c4-submissions closed 11 months ago

c4-submissions commented 11 months ago

Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L52-L79 https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L95-L110

Vulnerability details

Impact

The rsETH mint amount can be increased via oracle price manipulation with flash loan attack

Proof of Concept

The price of the rsETH is quoted using the function getRSETHPrice() in the Oracle contract. This function returns the ratio of the total ETH in the pool over the rsETH total supply:

    function getRSETHPrice() external view returns (uint256 rsETHPrice) {
        address rsETHTokenAddress = lrtConfig.rsETH();
        uint256 rsEthSupply = IRSETH(rsETHTokenAddress).totalSupply();

        if (rsEthSupply == 0) {
            return 1 ether;
        }

        uint256 totalETHInPool;
        address lrtDepositPoolAddr = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL);

        address[] memory supportedAssets = lrtConfig.getSupportedAssetList();
        uint256 supportedAssetCount = supportedAssets.length;

        for (uint16 asset_idx; asset_idx < supportedAssetCount;) {
            address asset = supportedAssets[asset_idx];
            uint256 assetER = getAssetPrice(asset);

            uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset);
            totalETHInPool += totalAssetAmt * assetER;

            unchecked {
                ++asset_idx;
            }
        }

        return totalETHInPool / rsEthSupply;
    }

Thus, in the case of manipulating the aforementioned ratio, by minting a large amount of rsETH tokens and increasing the totalSupply of the rsETH token, this ratio becomes relatively small. Inside the getRsETHAmountToMint() function:

    function getRsETHAmountToMint(
        address asset,
        uint256 amount
    )
        public
        view
        override
        returns (uint256 rsethAmountToMint)
    {
        // setup oracle contract
        address lrtOracleAddress = lrtConfig.getContract(LRTConstants.LRT_ORACLE);
        ILRTOracle lrtOracle = ILRTOracle(lrtOracleAddress);

        // calculate rseth amount to mint based on asset amount and asset exchange rate
        rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();
    }

The rsethAmountToMint would increase as the denominator would decrease. Thus, an attacker can inflate the rsETH minting amount by first minting a large amount of rsETH and then remint rsETH but this time with less token amounts.

Tools Used

Manual Review

Recommended Mitigation Steps

Consider using a price average mechanism (like TWAP) on Chainlink oracle prices or lock the depositAsset() function not to be called twice in a block.

Assessed type

Token-Transfer

c4-pre-sort commented 11 months ago

raymondfam marked the issue as insufficient quality report

c4-pre-sort commented 11 months ago

raymondfam marked the issue as duplicate of #84

c4-judge commented 10 months ago

fatherGoose1 marked the issue as unsatisfactory: Insufficient proof