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: 121/246
Findings: 3
Award: $24.89
🌟 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
Staking is vulnerable to the first depositor exploit where a malicious user can stake a very small amount such as 1 wei.
They could then send inflate the value of their share by send a large amount of underlying to the contract.
Now other stakers would unfairly receive very little shares when they stake. The attacker would also gain an inflated amount when they unstake because they already have inflated shares.
uint256 totalSupply = totalSupply(); uint256 preDepositPrice; // Price of safETH in regards to ETH if (totalSupply == 0) preDepositPrice = 10 ** 18; // initializes with a price of 1 else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;
Premint a certain number of shares before the first staker, alternatively require a minimum deposit for the first staker.
#0 - c4-pre-sort
2023-04-01T10:00:01Z
0xSorryNotSorry marked the issue as low quality report
#1 - elmutt
2023-04-10T19:51:10Z
fails to demonstrate how the exploit works
#2 - c4-sponsor
2023-04-10T19:51:24Z
elmutt marked the issue as sponsor disputed
#3 - c4-judge
2023-04-20T09:08:33Z
Picodes marked the issue as duplicate of #454
#4 - c4-judge
2023-04-21T16:21:04Z
Picodes marked the issue as duplicate of #1098
#5 - c4-judge
2023-04-24T21:25:14Z
Picodes marked the issue as satisfactory
🌟 Selected for report: __141345__
Also found by: 0xWaitress, 0xbepresent, AkshaySrivastav, Bauer, Haipls, HollaDieWaldfee, Lirios, MiloTruck, SaeedAlipoor01988, UdarTeam, adriro, bytes032, ck, d3e4, hihen, hl_, kaden, ladboy233, lopotras, m_Rassska, peanuts, reassor, volodya
8.2654 USDC - $8.27
When withdrawal of a derivative fails, a revert happens. If the reverts are persisitent e.g due to an exploited derivative contract, this could prevent unstaking as the whole unstaking function would continue reverting.
Since the contract's (derivatives[i].balance()
still returns some positive value, this issue would become persisitent.
for (uint256 i = 0; i < derivativeCount; i++) { // withdraw a percentage of each asset based on the amount of safETH uint256 derivativeAmount = (derivatives[i].balance() * _safEthAmount) / safEthTotalSupply; if (derivativeAmount == 0) continue; // if derivative empty ignore derivatives[i].withdraw(derivativeAmount); }
An attempt at rebalancing would encounter the same issue as withdrawing is still done in a loop.
Provide an alternative unstaking function that can be used in such cases where the derivative with an issue can be skipped or left out.
#0 - c4-pre-sort
2023-04-03T11:19:53Z
0xSorryNotSorry marked the issue as low quality report
#1 - c4-pre-sort
2023-04-04T21:47:26Z
0xSorryNotSorry marked the issue as duplicate of #770
#2 - c4-judge
2023-04-24T18:30:06Z
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
There are currently separate functions for pausing and unpausing
function setPauseStaking(bool _pause) external onlyOwner { pauseStaking = _pause; emit StakingPaused(pauseStaking); } /** @notice - Owner only function that enables/disables the unstake function @param _pause - true disables unstaking / false enables unstaking */ function setPauseUnstaking(bool _pause) external onlyOwner { pauseUnstaking = _pause; emit UnstakingPaused(pauseUnstaking); }
This is unnecessary as one function is enough, the stakingPaused
variable alone can be used to check were staking is paused or unpaused.
function setMaxSlippage(uint256 _slippage) external onlyOwner { maxSlippage = _slippage; }
Currently there are no restrictions on the amount of slippage that can be set. To prevent unintended effects, the input should be validated for acceptable minimum and maximum slippage.
totalWeight
should never be 0function adjustWeight( uint256 _derivativeIndex, uint256 _weight ) external onlyOwner { weights[_derivativeIndex] = _weight; uint256 localTotalWeight = 0; for (uint256 i = 0; i < derivativeCount; i++) localTotalWeight += weights[i]; totalWeight = localTotalWeight; emit WeightChange(_derivativeIndex, _weight); }
When the weights are being set or adjusted, it would be good practice to ensure the totalWeight
isn't 0 in a scenario where either by malice or mistake, all weights are set to 0.
_contractAddress
of a derivative cannot be set to 0function addDerivative( address _contractAddress, uint256 _weight ) external onlyOwner { derivatives[derivativeCount] = IDerivative(_contractAddress); weights[derivativeCount] = _weight; derivativeCount++; uint256 localTotalWeight = 0; for (uint256 i = 0; i < derivativeCount; i++) localTotalWeight += weights[i]; totalWeight = localTotalWeight; emit DerivativeAdded(_contractAddress, _weight, derivativeCount); }
When adding a derivative, a check should be done to prevent _contractAddress
being set to 0 by mistake.
uint256 ethAmount = (msg.value * weight) / totalWeight;
If msg.value
is a low value and a derivative has low weight, there are instances where the ethAmount
will be zero.
A suggestion would be to enforce a minimum weight to total weight ratio for derivatives.
#0 - c4-sponsor
2023-04-10T20:18:10Z
elmutt marked the issue as sponsor confirmed
#1 - c4-judge
2023-04-24T18:45:03Z
Picodes marked the issue as grade-b