Platform: Code4rena
Start Date: 24/03/2023
Pot Size: $49,200 USDC
Total HM: 20
Participants: 246
Period: 6 days
Judge: Picodes
Total Solo HM: 1
Id: 226
League: ETH
Rank: 30/246
Findings: 4
Award: $222.85
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: monrel
Also found by: 0xRajkumar, 0xfusion, AkshaySrivastav, Bahurum, Brenzee, Cryptor, Dug, Haipls, Koolex, Krace, MiloTruck, RaymondFam, RedTiger, ToonVH, Tricko, Vagner, aga7hokakological, anodaram, bart1e, bin2chen, bytes032, carrotsmuggler, ck, d3e4, giovannidisiena, igingu, juancito, mahdirostami, mert_eren, n33k, nemveer, parsely, pavankv, sashik_eth, shaka, sinarette, ulqiorra, yac
3.4908 USDC - $3.49
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L221-L223 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L71-L75
The function stake uses the balances of each derivative contract to calculate underlyingvalue which is then used to calculate the amount of SafEth to mint to a user. However, this makes the contract vulnerable to an inflation attack where a user can manipulate the mint function by making a direct donation to any and all of the derivative contracts, making the ETH price of SafEth more than what it should be.
The function stake calculates the variable underlying value with the following formula
underlyingValue += (derivatives[i].ethPerDerivative(derivatives[i].balance()) * derivatives[i].balance()) / 10 ** 18;
This variable is used to calculate the amount of Safeth to mint to a user
else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;
and here
uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
So when preDepositPrice rises, then the amount to mint to a user goes down.
The function to get the balances in the each derivative is shown here which is the same across all 3 current derivatives:
function balance() public view returns (uint256) { return IERC20(rethAddress()).balanceOf(address(this)); }
So a malicious user can direct deposit Reth, SfrxEth, or WstEth into the contract to inflate underlying value and forcibly reduce the amount of SafEth to mint. For example Bob makes a malicious donation to the derivative contracts. Alice calls stake after depositing some ETH and expecting some SafETH back. However, Alice receives 0 SafETH because Bob has inflated predepositprice. Bob can then make his own larger deposit and steal Alice's rewards by calling unstake.
Manual Review
Use a mapping instead of balanceOf to track balances
#0 - c4-pre-sort
2023-04-04T13:53:08Z
0xSorryNotSorry marked the issue as duplicate of #454
#1 - c4-judge
2023-04-21T16:21:03Z
Picodes marked the issue as duplicate of #1098
#2 - c4-judge
2023-04-24T21:24:00Z
Picodes marked the issue as satisfactory
195.1013 USDC - $195.10
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L107-L114 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L108
The function withdraw in Reth.sol uses the burn function in the Reth contract, in order to swap Reth for ETH.
However, theReth contract does not have a lot of liquidity. As of this writing the amount of ETH in the Reth contract is only 536 ETH as shown here
https://etherscan.io/address/0xae78736Cd615f374D3085123A210448E74Fc6393
And the totalsupply of Reth is 216546.916776976708709502 in ETH
In addition, the price of Reth is $1,894.35 as of this writing. In the event that the Reth contract is compromised, there will not be enough liquidity in the contract to cover all users meaning that the function withdraw will revert.
See Impact
Manual Review
Just like the deposit function, add a check to swap Reth for ETH in uniswap if the burn function from the Reth contract fails.
I am listing this as a medium because user funds will be lost if the Reth contract is compromised
#0 - c4-pre-sort
2023-04-04T19:56:45Z
0xSorryNotSorry marked the issue as duplicate of #210
#1 - c4-judge
2023-04-21T16:36:40Z
Picodes changed the severity to 3 (High Risk)
#2 - c4-judge
2023-04-21T16:36:50Z
Picodes marked the issue as satisfactory
11.1318 USDC - $11.13
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L48-L56 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L84-L88
The function stake uses a for loop to cycle through all of the derivatives and their respective weights in order to calculate the amount to deposit into each derivative. However, neither the derivatives or their weights are defined in the initialize function. That means that if the admin does not call rebalanceToWeights function or the adjustweight function before a user calls the function stake, the function stake will not work properly for users due to a miscalculation in the variable ethamount.
The function stake calculates the amount of ETH to deposit into each derivative contract like this
However, since the derivatives and their weights are not called, ethamount will return 0. Reth derivative contract has a minimum deposit of 0.01, making the entire stake function revert.
Manual Review
In the initialize function, set the derivatives and their respective weights. Alternatively, pause both stake and unstake in the initialize function until the derivatives and weights have been set.
#0 - c4-pre-sort
2023-04-01T11:03:31Z
0xSorryNotSorry marked the issue as low quality report
#1 - c4-pre-sort
2023-04-04T19:11:10Z
0xSorryNotSorry marked the issue as duplicate of #363
#2 - c4-judge
2023-04-21T16:29:01Z
Picodes changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-04-21T16:32:04Z
Picodes marked the issue as satisfactory
🌟 Selected for report: brgltd
Also found by: 0x3b, 0xAgro, 0xGusMcCrae, 0xNorman, 0xRajkumar, 0xSmartContract, 0xTraub, 0xWagmi, 0xWaitress, 0xffchain, 0xhacksmithh, 0xkazim, 0xnev, 3dgeville, ArbitraryExecution, Aymen0909, BRONZEDISC, Bason, Bloqarl, BlueAlder, Brenzee, CodeFoxInc, CodingNameKiki, Cryptor, DadeKuma, DevABDee, Diana, Dug, Englave, Gde, Haipls, HollaDieWaldfee, Ignite, Infect3d, Jerry0x, Josiah, Kaysoft, Koko1912, KrisApostolov, Lavishq, LeoGold, Madalad, PNS, Rappie, RaymondFam, RedTiger, Rickard, Rolezn, Sathish9098, SunSec, T1MOH, UdarTeam, Udsen, Viktor_Cortess, Wander, adriro, ak1, alejandrocovrr, alexzoid, arialblack14, ayden, bin2chen, brevis, btk, c3phas, carlitox477, catellatech, ch0bu, chaduke, ck, climber2002, codeslide, descharre, dingo2077, ernestognw, fatherOfBlocks, favelanky, georgits, helios, hl_, inmarelibero, juancito, ks__xxxxx, lopotras, lukris02, m_Rassska, mahdirostami, maxper, nadin, navinavu, nemveer, p_crypt0, peanuts, pipoca, pixpi, qpzm, rbserver, reassor, roelio, rotcivegaf, scokaf, siddhpurakaran, slvDev, smaul, tnevler, tsvetanovv, turvy_fuzz, vagrant, wen, yac, zzzitron
13.1298 USDC - $13.13
Weight can be any uint. This can cause some issues if weight is set too high
If totalweights is too high (e.g. 1000 * (10**18)), then ethamount will revert in some situations
Recommendation: Make sure that weight are not denominated too high
Recommendation: Give users lag time to withdraw if they do not agree with the weight rebalance
#0 - c4-sponsor
2023-04-10T19:35:25Z
elmutt marked the issue as sponsor acknowledged
#1 - c4-judge
2023-04-24T18:47:19Z
Picodes marked the issue as grade-b