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: 102/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/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTOracle.sol#L70-L71 https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTOracle.sol#L78 https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTDepositPool.sol#L47-L51 https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTDepositPool.sol#L79
Attacker is able transfer some amount of an underlying asset directly to LRTDepositPool
(or a NodeDelegator, the goal of attacker is to increase the balance of protocol for an underlying asset) and inflate the rsETH price.
Note: getTotalAssetDeposits returns the total asset (from token X
for example) present in protocol:
(contract balance of LRTDepositPool for asset X
+ amount of asset X that are transferred to NodeDelegators
+ amount of asset X that are deposited into strategies
)
This is how the price of rsETH is calculated (see LRTOracle#getRSETHPrice):
for (uint16 asset_idx; asset_idx < supportedAssetCount;) { // <-- Assume supportedAssetCount is 1 and there is only 1 asset `X` address asset = supportedAssets[asset_idx]; // <-- address of asset `X` uint256 assetER = getAssetPrice(asset); // <-- price of asset `X` uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset); // <-- amount of asset `X` owned by Protocol (Attacker can directly transfer some amount of asset `X` to LRTDepositPool, this increases balance of LRTDepositPool contract, so when LRTDepositPool's balance increases, as a result, Protocol's balance also increases and rsETH price will be manipulated) totalETHInPool += totalAssetAmt * assetER; // <-- (number of asset `X` owned by Protocol) * (price of asset `X`) unchecked { ++asset_idx; } } return totalETHInPool / rsEthSupply; // <-- (value of asset `X` (value in $dollar) owned by the Protocol) / (number of rsETH minted) = rsETH_price
(Everything is commented in code)
So what will happen if attacker transfers some amount of asset X
(for example 1000) directly to LRTDepositPool (without minting any rsETH) ?
The answer is: totalSupply of rsETH will remain the same, but the totalETHInPool
will be a higher amount (because totalETHInPool
depends on getTotalAssetDeposits
and getTotalAssetDeposits depends on the contract balance of LRTDepositPool
for asset X
) -> which means the rsETH price will be higher than before and inflated.
Let's imagine an example:
stETH
(we know there are more than one, but let's for now assume there is only one to make the example easier to understand) and price of stETH
is $2000.stETH
(getTotalAssetDeposits
returns 2) and minted 2 rsETH (totalSupply of rsETH is 2), in other words, someone has deposited 2 stETH and minted 2 rsETH.Now:
totalETHInPool = 4000$ (because 2 * 2000$)
rsEthSupply = 2
rsETH_price = totalETHInPool / rsEthSupply = $4000 / 2 = $2000
getTotalAssetDeposits
returns 4 (it means the protocol owns 4 stETH)Now:
totalETHInPool = 8000$ (because 4 * 2000$)
rsEthSupply = 2
rsETH_price = totalETHInPool / rsEthSupply = $8000 / 2 = $4000
Manual Review
getTotalAssetDeposits() should not be dependent of contract balance for an underlying asset, consider adding a state variable which tracks how much balance is deposited through protocol.
Invalid Validation
#0 - c4-pre-sort
2023-11-15T21:10:12Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-11-15T21:10:22Z
raymondfam marked the issue as duplicate of #42
#2 - c4-judge
2023-12-01T16:53:52Z
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/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTConfig.sol#L80-L89 https://github.com/code-423n4/2023-11-kelp/blob/c5fdc2e62c5e1d78769f44d6e34a6fb9e40c00f0/src/LRTConfig.sol#L73-L75
There is no way for MANAGER to remove an asset from supportedAssets.
X
).X
(for example eigenlayer governance decides to remove an asset), MANAGER is not able to remove that asset (asset X
) from supportedAssets.Manual Review
Consider adding a function that MANAGER is able to remove some asset from supportedAssets.
Context
#0 - c4-pre-sort
2023-11-15T21:09:18Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2023-11-15T21:09:24Z
raymondfam marked the issue as duplicate of #38
#2 - c4-judge
2023-12-01T17:45:49Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#3 - c4-judge
2023-12-01T17:46:48Z
fatherGoose1 marked the issue as grade-b