Platform: Code4rena
Start Date: 11/11/2022
Pot Size: $90,500 USDC
Total HM: 52
Participants: 92
Period: 7 days
Judge: LSDan
Total Solo HM: 20
Id: 182
League: ETH
Rank: 48/92
Findings: 3
Award: $118.49
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: c7e7eff
Also found by: 0x4non, 9svR6w, HE1M, Jeiwan, Trust, aphak5010, arcoun, cccz, clems4ever, corerouter, koxuan, rotcivegaf, unforgiven
40.8568 USDC - $40.86
In the _distributeETHRewardsToUserForToken()
function, the claimed
array is updated with the claimed rewards of each user. This array is also used to calculate how much awards a user can withdraw.
There is an error when the array is updated: instead of increasing the value with the withdrawn awards (due
), the value is replaced with the withdrawn awards (due
). The new value will then be lower than the expected one, allowing a user to withdraw more than due by withdrawing several times.
uint256 due = ((accumulatedETHPerLPShare * balance) / PRECISION) - claimed[_user][_token]; if (due > 0) { claimed[_user][_token] = due; // should be "+= due" [...] }
The issue can also prevent a user to interact with the contract and withdraw his own assets because _distributeETHRewardsToUserForToken()
will revert if the calculated due
is higher than the ETH balance in the contract.
Proof of concept using GiantMevAndFeesPool.claimRewards()
but other paths are available.
lpTokenETH
balance on the GiantMevAndFeesPool
contract, allowing him to withdraw awardsclaimRewards()
function on the GiantMevAndFeesPool
contract with the following parameters:
_recipient
: user's address_stakingFundsVaults
: array with at least one available stacking fund. Note that the user can also provide an alternate contract which does not revert on claimRewards(address,bytes[])
as there is no verification here_blsPublicKeysForKnots
: related BLS keys (if using an available stacking fund)claimed[user][lpTokenEth]
is now set to a lower value than expectedclaimRewards()
at a later time, due rewards will be miscalculated and be higher than expected
_distributeETHRewardsToUserForToken()
is called at other places in the contract, this will prevent the user to do some actions like withdrawing his own assetsManual review.
Fix the _distributeETHRewardsToUserForToken()
function to increase claimed[_user][_token]
with due
instead of updating it with due
.
#0 - c4-judge
2022-11-21T21:38:36Z
dmvt marked the issue as duplicate of #59
#1 - c4-judge
2022-11-29T16:47:54Z
dmvt marked the issue as satisfactory
#2 - C4-Staff
2022-12-21T05:47:22Z
JeeberC4 marked the issue as duplicate of #147
🌟 Selected for report: Jeiwan
Also found by: 0xdeadbeef0x, 9svR6w, JTJabba, Lambda, Trust, arcoun, banky, bin2chen, bitbopper, c7e7eff, clems4ever, datapunk, fs0c, hihen, imare, immeas, perseverancesuccess, ronnyx2017, satoshipotato, unforgiven, wait
11.192 USDC - $11.19
https://github.com/code-423n4/2022-11-stakehouse/blob/main/contracts/liquid-staking/GiantSavETHVaultPool.sol#L48-L58 https://github.com/code-423n4/2022-11-stakehouse/blob/main/contracts/liquid-staking/GiantMevAndFeesPool.sol#L42-L51
Due to a lack of validation of input parameters in batchDepositETHForStaking()
, it is possible to use an alternate contract as a vault and withdraw all available Ether from the contract. The issue is present in both GiantSavETHVaultPool
and GiantMevAndFeesPool
contracts.
The only validation done is on a value returned by the user-provided address, this can be easily defeated by implementing the related funtion and returning a valid value.
The function will then call the batchDepositETHForStaking()
function on the user-provided address, with the requested Ether amount. If the address implements this function as a payable function, Ether will be transfered to it.
liquidStakingManager()
and return the address of a valid Liquid Staking Manager when calledliquidStakingNetworkManager()
and return the address of a valid Liquid Staking Manager for when calledbatchDepositETHForStaking(bytes[],uint256[])
as a payable function to not throw when called with an Ether valueGiantSavETHVaultPool.batchDepositETHForStaking()
with the following parameters
_savETHVaults
: array of size 1 with the created contract_ETHTransactionAmounts
: array of size 1 with available Ether (using idleETH()
)_blsPublicKeys
: array of size 1 with any byte[]
value_stakeAmounts
: array of size 1 with any uint256[]
valueGiantMevAndFeesPool.batchDepositETHForStaking()
with the following parameters
_stakingFundsVault
: array of size 1 with the created contract_ETHTransactionAmounts
: array of size 1 with available Ether (using idleETH()
)_blsPublicKeyOfKnots
: array of size 1 with any byte[]
value_amounts
: array of size 1 with any uint256[]
valueManual review.
User-provided contract addresses must be checked to avoid a malicious usage.
#0 - c4-judge
2022-11-21T21:38:59Z
dmvt marked the issue as duplicate of #36
#1 - c4-judge
2022-11-29T15:44:54Z
dmvt marked the issue as satisfactory
#2 - C4-Staff
2022-12-21T05:40:16Z
JeeberC4 marked the issue as duplicate of #36
#3 - C4-Staff
2022-12-22T08:17:46Z
liveactionllama marked the issue as not a duplicate
#4 - C4-Staff
2022-12-22T08:17:58Z
liveactionllama marked the issue as duplicate of #251
66.4388 USDC - $66.44
Due to a lack of validation of input parameters in GiantPoolBase.withdrawLPTokens()
, it is possible to exchange Giant LP tokens with any ERC20 token owned by the target contract, not only Vault LP token as expected by the function. This issue impacts any contract inherited from GiantPoolBase
: GiantMevAndFeesPool
and GiantSavETHVaultPool
.
During its lifetime, a contract can receive various ERC20 tokens: airdrops, user error, gifts, spam, ... Some of them may have a tradable value and should be kept by the operators of the contract.
If the raw value (as uint256 value, without decimal) of a token is greater than the raw value of the Giant LP token, a user may abuse the lack of validation of withdrawLPTokens()
to exchange them and make a profit.
Limitations:
GiantMevAndFeesPool
, target token must support self-transfer as done in _onWithdraw()
(GiantMevAndFeesPool.sol#L184
). Most ERC tokens support that.GiantMevAndFeesPool
or GiantSavETHVaultPool
contract owns 1000 FOO tokens. This is an example token with decimals=12 and a value of $5 per token ($5000 for the 1000 FOO tokens).depositETH()
function and now have 0.001 Giant LP Tokens (0.001*10^18 = 1000000000000000)withdrawLPTokens()
with the following parameters:
_lpTokens
: array of size 1 with the FOO token address_amounts
: array of size 1 with the MIN_STAKING_AMOUNT amountManual review.
User-provided token addresses must be checked to avoid a malicious usage.
#0 - c4-judge
2022-11-21T21:39:36Z
dmvt marked the issue as duplicate of #98
#1 - c4-judge
2022-11-30T12:31:40Z
dmvt marked the issue as satisfactory