Kelp DAO | rsETH - turvy_fuzz's results

A collective DAO designed to unlock liquidity, DeFi and higher rewards for restaked assets through liquid restaking.

General Information

Platform: Code4rena

Start Date: 10/11/2023

Pot Size: $28,000 USDC

Total HM: 5

Participants: 185

Period: 5 days

Judge: 0xDjango

Id: 305

League: ETH

Kelp DAO

Findings Distribution

Researcher Performance

Rank: 31/185

Findings: 2

Award: $143.01

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Awards

140.2525 USDC - $140.25

Labels

bug
2 (Med Risk)
satisfactory
sufficient quality report
duplicate-148

External Links

Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L119 https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L141 https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L54

Vulnerability details

Impact

Function LRTDepositPool:depositAsset() does not check the minimum liquidity amount. This makes users' funds vulnerable to sandwich attacks.

depositAsset() will increase IRSETH(rsETHTokenAddress).totalSupply(), and thus change the exchange rate of RSETH.

return totalETHInPool / rsEthSupply;

Given the current network environment, most transactions in the mempool would be sandwiched. However, users may avoid this attack if they only send tx through Flashbot. I consider this a medium-risk issue.

Proof of Concept

Let's says a user wants to deposit 1000 LST in the pool hoping to get an expected amount of rsETH back. Attackers sees this and front runs trade as follows:

Mint more rsETH with 10,000 of the LST and extremely change the expected rsETH to mint for that LST User's tx enter here and execute depositAsset(). Trade finalize with unwanted rsETH amount given back to the user since no slippage specified

Tools Used

Visual Studio Code

Function LRTDepositPool:depositAsset() should allow users to provide a min return and check the slippage. Always check how much liquidity a user receives in a transaction. A tx would not be sandwiched if it's not profitable.

We could learn a lot about MEV here.

Assessed type

MEV

#0 - c4-pre-sort

2023-11-16T19:20:39Z

raymondfam marked the issue as insufficient quality report

#1 - c4-pre-sort

2023-11-16T19:20:54Z

raymondfam marked the issue as duplicate of #284

#2 - c4-pre-sort

2023-11-16T20:35:24Z

raymondfam marked the issue as not a duplicate

#3 - c4-pre-sort

2023-11-16T20:35:32Z

raymondfam marked the issue as duplicate of #39

#4 - c4-pre-sort

2023-11-16T20:35:38Z

raymondfam marked the issue as sufficient quality report

#5 - c4-pre-sort

2023-11-17T06:43:15Z

raymondfam marked the issue as duplicate of #148

#6 - c4-judge

2023-11-29T19:10:46Z

fatherGoose1 marked the issue as satisfactory

Awards

2.7592 USDC - $2.76

Labels

bug
downgraded by judge
grade-b
insufficient quality report
QA (Quality Assurance)
duplicate-38
Q-05

External Links

Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L70 https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L85 https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L86

Vulnerability details

Impact

LRTOracle:getRSETHPrice() which is executed in all protocol pricing operations, includes all assets supported in the calculation. This asset is added with the addNewSupportedAsset() function, However, this sets the asset permanently and can't be undone if the asset returns same values when strategy is no longer handled and the asset is no longer supported on EigenLayer.

Proof of Concept

_addNewSupportedAsset() function sets assets as supported forever:

function _addNewSupportedAsset(address asset, uint256 depositLimit) private {
        UtilLib.checkNonZeroAddress(asset);
        if (isSupportedAsset[asset]) {
            revert AssetAlreadySupported();
        }
        isSupportedAsset[asset] = true; // @audit
        supportedAssetList.push(asset);
        depositLimitByAsset[asset] = depositLimit;
        emit AddedNewSupportedAsset(asset, depositLimit);
    }

There is no function that can undo this.

LRTOracle:getRSETHPrice()

function getRSETHPrice() external view returns (uint256 rsETHPrice) {

        ...

        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); // @audit
            totalETHInPool += totalAssetAmt * assetER;

            unchecked {
                ++asset_idx;
            }
        }

        return totalETHInPool / rsEthSupply;
    }

Tools Used

Manual Review

Contract should have a removeAsset function

Assessed type

Other

#0 - c4-pre-sort

2023-11-16T23:59:14Z

raymondfam marked the issue as insufficient quality report

#1 - c4-pre-sort

2023-11-16T23:59:31Z

raymondfam marked the issue as duplicate of #38

#2 - c4-judge

2023-12-01T17:45:50Z

fatherGoose1 changed the severity to QA (Quality Assurance)

#3 - c4-judge

2023-12-01T17:47:24Z

fatherGoose1 marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter