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
Rank: 25/185
Findings: 3
Award: $147.67
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Krace
Also found by: 0xDING99YA, 0xrugpull_detector, Aamir, AlexCzm, Aymen0909, Banditx0x, Bauer, CatsSecurity, GREY-HAWK-REACH, Madalad, Phantasmagoria, QiuhaoLi, Ruhum, SBSecurity, SandNallani, SpicyMeatball, T1MOH, TheSchnilch, adam-idarrha, adriro, almurhasan, ast3ros, ayden, bronze_pickaxe, btk, chaduke, ck, crack-the-kelp, critical-or-high, deth, gumgumzum, jasonxiale, joaovwfreire, ke1caM, m_Rassska, mahdirostami, mahyar, max10afternoon, osmanozdemir1, peanuts, pep7siup, peter, ptsanev, qpzm, rouhsamad, rvierdiiev, spark, twcctop, ubl4nk, wisdomn_, zach, zhaojie
4.6614 USDC - $4.66
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L119-L144
Attacker can first deposit small amount of asset token to get shares, and front-run other depositors' transactions and inflate shares price by large "donation", thus attacker may withdraw more loan tokens than he initially owned.
User can get share by depositing asset to Lending vault, the amount of minted shares is calculated as:
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(); }
Let's assume: 1.Alice wants to invoke the depositAsset() function to deposit 1.5 ether worth of steth. 2.Bob sees Alice's transaction in mempool and front-runs by first depositing 1 wei to the LRTDepositPool and then get 1 share; 3.Bob then transfers 1 ether worth of steth directly to the pool, inflates rsETH price to (1 ether worth of steth + 1); 4.Alice's deposit transaction gets confirmed and Alice get 1 share; 5.If there is a withdrawal feature, Bob can withdraw from the pool and receive 1.5 ether worth of steth, resulting in a profit of 0.5 ether worth of steth.
Consider minting a minimal amount of pool tokens during the first deposit and sending them to zero address, this increases the cost of the attack. Uniswap V2 uses the value 1000 as it is small enough to don't hurt the first minter, while still increasing the cost of this attack by 1000x. https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Pair.sol#L119-L121
Other
#0 - c4-pre-sort
2023-11-15T21:02:53Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-11-15T21:03:03Z
raymondfam marked the issue as duplicate of #42
#2 - c4-judge
2023-12-01T16:53:47Z
fatherGoose1 marked the issue as satisfactory
🌟 Selected for report: max10afternoon
Also found by: 0xMilenov, 0xbrett8571, 0xhacksmithh, 0xmystery, Aymen0909, Bauer, Daniel526, PENGUN, Pechenite, Shaheen, adriro, anarcheuz, btk, ck, ge6a, glcanvas, hals, turvy_fuzz
140.2525 USDC - $140.25
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L119-L144
The current implementation poses a risk where depositors may experience unfavorable outcomes when depositing assets to receive rseth. This risk is associated with the reliance on oracle-reported prices during the calculation of rsethAmountMinted. If the oracle's price is manipulated, depositors may receive a significantly lower amount of rseth than expected, leading to potential financial losses.
The LRTDepositPool.depositAsset()
function allows users to deposit a specified amount of a supported asset into the smart contrac.Inside the fuction, it calculates rsethAmountMinted
based on the price retrieved from an oracle. If the oracle's reported price is manipulated, it poses a significant risk to users.
function _mintRsETH(address _asset, uint256 _amount) private returns (uint256 rsethAmountToMint) { (rsethAmountToMint) = getRsETHAmountToMint(_asset, _amount); address rsethToken = lrtConfig.rsETH(); // mint rseth for user IRSETH(rsethToken).mint(msg.sender, rsethAmountToMint); }
In such a scenario, users might receive an unfairly low amount of assets in return for their deposit , resulting in potential financial loss.
Vscode
Function depositAsset()
should have an input parameter uint256 minimumOutputTokens and the function should revert if require(rsethAmountMinted >= minimumOutputTokens, "Too little rseth received.");
Other
#0 - c4-pre-sort
2023-11-15T20:39:44Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-11-15T20:39:55Z
raymondfam marked the issue as primary issue
#2 - raymondfam
2023-11-15T20:41:48Z
Probably medium at best due to the low likelihood of occurrence.
#3 - c4-pre-sort
2023-11-17T06:43:24Z
raymondfam marked the issue as duplicate of #148
#4 - c4-judge
2023-11-29T19:21:56Z
fatherGoose1 changed the severity to 2 (Med Risk)
#5 - c4-judge
2023-11-29T19:22:02Z
fatherGoose1 marked the issue as satisfactory
🌟 Selected for report: max10afternoon
Also found by: 0xMilenov, 0xbrett8571, 0xhacksmithh, 0xmystery, Aymen0909, Bauer, Daniel526, PENGUN, Pechenite, Shaheen, adriro, anarcheuz, btk, ck, ge6a, glcanvas, hals, turvy_fuzz
140.2525 USDC - $140.25
https://github.com/code-423n4/2023-11-kelp/blob/main/src/NodeDelegator.sol#L51-L68
The absence of slippage protection within the depositAssetIntoStrategy function poses a significant risk to the protocol. This oversight leaves the protocol vulnerable to potential financial losses, as malicious actors can manipulate share prices during deposit transactions. The impact extends beyond financial implications, as the efficiency of the protocol in managing assets within the strategy pool is compromised.
The function NodeDelegator.depositAssetIntoStrategy()
is used to deposit a specified asset into its associated strategy.
Within the function, the protocol utilizes the eigenlayerStrategyManagerAddress.depositIntoStrategy() function to deposit funds into the strategy pool, acquiring corresponding shares. However, a crucial concern arises as the function lacks protection against slippage. In the absence of safeguards, malicious actors manipulating the price of shares can cause the protocol to receive fewer shares than anticipated, resulting in financial losses for the protocol.
function depositAssetIntoStrategy(address asset) external override whenNotPaused nonReentrant onlySupportedAsset(asset) onlyLRTManager { address strategy = lrtConfig.assetStrategy(asset); IERC20 token = IERC20(asset); address eigenlayerStrategyManagerAddress = lrtConfig.getContract(LRTConstants.EIGEN_STRATEGY_MANAGER); uint256 balance = token.balanceOf(address(this)); emit AssetDepositIntoStrategy(asset, strategy, balance); IEigenStrategyManager(eigenlayerStrategyManagerAddress).depositIntoStrategy(IStrategy(strategy), token, balance); }
Vscode
Function depositAssetIntoStrategy()
should have an input parameter uint256 minimumOutputShares and the function should revert if require(shares >= minimumOutputTokens, "Too little shares received.");
Other
#0 - c4-pre-sort
2023-11-15T21:04:34Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-11-15T21:04:43Z
raymondfam marked the issue as duplicate of #39
#2 - c4-pre-sort
2023-11-17T06:42:52Z
raymondfam marked the issue as duplicate of #148
#3 - c4-judge
2023-11-29T19:21:54Z
fatherGoose1 changed the severity to 2 (Med Risk)
#4 - c4-judge
2023-12-01T17:49:13Z
fatherGoose1 marked the issue as satisfactory
🌟 Selected for report: m_Rassska
Also found by: 0x1337, 0xAadi, 0xHelium, 0xLeveler, 0xblackskull, 0xbrett8571, 0xepley, 0xffchain, 0xluckhu, 0xmystery, 0xrugpull_detector, 0xvj, ABAIKUNANBAEV, Aamir, AerialRaider, Amithuddar, Bauchibred, Bauer, CatsSecurity, Cryptor, Daniel526, Draiakoo, Eigenvectors, ElCid, GREY-HAWK-REACH, Inspecktor, Juntao, King_, LinKenji, Madalad, MaslarovK, Matin, MatricksDeCoder, McToady, Noro, PENGUN, Pechenite, Phantasmagoria, RaoulSchaffranek, SBSecurity, SandNallani, Shaheen, Soul22, Stormreckson, T1MOH, Tadev, TeamSS, TheSchnilch, Topmark, Tumelo_Crypto, Udsen, Yanchuan, ZanyBonzy, _thanos1, adeolu, adriro, alexfilippov314, almurhasan, amaechieth, anarcheuz, ayden, baice, bareli, boredpukar, bronze_pickaxe, btk, cartlex_, catellatech, chaduke, cheatc0d3, circlelooper, codynhat, crack-the-kelp, critical-or-high, debo, deepkin, desaperh, dipp, eeshenggoh, evmboi32, ge6a, gesha17, glcanvas, gumgumzum, hals, hihen, hunter_w3b, jasonxiale, joaovwfreire, ke1caM, leegh, lsaudit, marchev, merlinboii, niser93, osmanozdemir1, paritomarrr, passion, pep7siup, phoenixV110, pipidu83, poneta, ro1sharkm, rouhsamad, rvierdiiev, sakshamguruji, seerether, shealtielanz, soliditytaker, spark, squeaky_cactus, stackachu, supersizer0x, tallo, taner2344, turvy_fuzz, twcctop, ubl4nk, wisdomn_, xAriextz, zach, zhaojie, zhaojohnson, ziyou-
2.7592 USDC - $2.76
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L162-L176
The protocol faces a critical issue as it lacks a removal mechanism for NodeDelegatorContracts in the nodeDelegatorQueue. The absence of this feature presents two main concerns: firstly, the continuous accumulation of contracts in the array may lead to escalated gas consumption and potential out-of-gas scenarios. Secondly, the protocol is exposed to security risks, as compromised NodeDelegatorContracts cannot be promptly removed, putting users' assets at potential risk.
The protocol lacks a mechanism for removing NodeDelegatorContracts from the queue, leading to a continuous accumulation of data in the nodeDelegatorQueue array. This absence of a removal feature can result in potential issues:
function addNodeDelegatorContractToQueue(address[] calldata nodeDelegatorContracts) external onlyLRTAdmin { uint256 length = nodeDelegatorContracts.length; if (nodeDelegatorQueue.length + length > maxNodeDelegatorCount) { revert MaximumNodeDelegatorCountReached(); } for (uint256 i; i < length;) { UtilLib.checkNonZeroAddress(nodeDelegatorContracts[i]); nodeDelegatorQueue.push(nodeDelegatorContracts[i]); emit NodeDelegatorAddedinQueue(nodeDelegatorContracts[i]); unchecked { ++i; } } }
The ever-growing nodeDelegatorQueue array, without the ability to remove contracts, may lead to increased gas consumption during contract interactions. Transaction costs could escalate, and users might face challenges interacting with the protocol due to potential gas limits.
The inability to remove NodeDelegatorContracts poses a security risk, as a compromised or malicious NodeDelegatorContract could persist indefinitely in the queue. In the event of a security vulnerability or breach in a NodeDelegatorContract, users' assets may be at risk, and the protocol's integrity could be compromised. Protocol Scalability:
Vscode
It is advisable to implement a corresponding removal or deactivation mechanism for NodeDelegatorContracts to address these concerns.
Other
#0 - c4-pre-sort
2023-11-15T21:03:40Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2023-11-15T21:03:51Z
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:46:46Z
fatherGoose1 marked the issue as grade-b