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: 100/185
Findings: 2
Award: $7.42
🌟 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#L151-L157 https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L95-L110 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#L71-L89
Here's attack scenario when rsETH contract is just deployed.
rsETH
.LRTOracle.getRSETHPrice()
.rsETH
to be minted because of inflated RsETH price.https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L151-L157
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); }
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L95-L110
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(); }
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L52-L79
function getRSETHPrice() external view returns (uint256 rsETHPrice) { ... for (uint16 asset_idx; asset_idx < supportedAssetCount;) { ... uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset); ... } return totalETHInPool / rsEthSupply; }
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L71-L89
function getAssetDistributionData(address asset) public view override onlySupportedAsset(asset) returns (uint256 assetLyingInDepositPool, uint256 assetLyingInNDCs, uint256 assetStakedInEigenLayer) { // Question: is here the right place to have this? Could it be in LRTConfig? assetLyingInDepositPool = IERC20(asset).balanceOf(address(this)); ... }
First depositor of rsETH can steal subsequent depositor's fund.
Manual Review
LRTDepositPool._mintRsETH
should mint specific amount to zero address when total supply is zero.
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L151-L157
function _mintRsETH(address _asset, uint256 _amount) private returns (uint256 rsethAmountToMint) { + if (IRSETH(rsethToken).totalSupply() == 0) + IRSETH(rsethToken).mint(address(0), MINIMUM_AMOUNT); (rsethAmountToMint) = getRsETHAmountToMint(_asset, _amount); address rsethToken = lrtConfig.rsETH(); // mint rseth for user IRSETH(rsethToken).mint(msg.sender, rsethAmountToMint); }
Invalid Validation
#0 - c4-pre-sort
2023-11-16T03:27:02Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-11-16T03:27:09Z
raymondfam marked the issue as duplicate of #42
#2 - c4-judge
2023-12-01T17:02:36Z
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/LRTConfig.sol#L109-L122 https://github.com/code-423n4/2023-11-kelp/blob/main/src/NodeDelegator.sol#L121-L124
Same strategy can be set for different assets like stETH or wstETH.
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L109-L122
function updateAssetStrategy( address asset, address strategy ) external onlyRole(DEFAULT_ADMIN_ROLE) onlySupportedAsset(asset) { UtilLib.checkNonZeroAddress(strategy); if (assetStrategy[asset] == strategy) { revert ValueAlreadyInUse(); } assetStrategy[asset] = strategy; }
In that case, getAssetBalance() might return sum of asset balances assigned to strategy.
https://github.com/code-423n4/2023-11-kelp/blob/main/src/NodeDelegator.sol#L121-L124
function getAssetBalance(address asset) external view override returns (uint256) { address strategy = lrtConfig.assetStrategy(asset); return IStrategy(strategy).userUnderlyingView(address(this)); }
It might cause double accounting of strategy balance.
Manual Review
Should check if strategy is already assigned to asset.
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTConfig.sol#L109-L122
+ mapping(address strategy => address asset) public override strategyAsset; function updateAssetStrategy( address asset, address strategy ) external onlyRole(DEFAULT_ADMIN_ROLE) onlySupportedAsset(asset) { UtilLib.checkNonZeroAddress(strategy); if (assetStrategy[asset] == strategy) { revert ValueAlreadyInUse(); } assetStrategy[asset] = strategy; + if (strategyToAsset[strategy] == asset) { + revert ValueAlreadyInUse(); + } + strategyToAsset[strategy] = asset; }
Invalid Validation
#0 - c4-pre-sort
2023-11-16T03:27:43Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2023-11-16T03:28:05Z
raymondfam marked the issue as duplicate of #69
#2 - c4-judge
2023-11-29T20:58:12Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#3 - c4-judge
2023-11-29T21:01:20Z
fatherGoose1 marked the issue as grade-b