LSD Network - Stakehouse contest - datapunk's results

A permissionless 3 pool liquid staking solution for Ethereum.

General Information

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

Stakehouse Protocol

Findings Distribution

Researcher Performance

Rank: 6/92

Findings: 9

Award: $3,540.15

QA:
grade-b

🌟 Selected for report: 1

🚀 Solo Findings: 1

Awards

11.192 USDC - $11.19

Labels

bug
3 (High Risk)
satisfactory
duplicate-251

External Links

Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L29

Vulnerability details

Impact

All Eth can be drained by fake vault addresses. https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L29

Proof of Concept

In batchDepositETHForStaking, _savETHVault is checked for its validity through

SavETHVault savETHPool = SavETHVault(_savETHVaults[i]); require( liquidStakingDerivativeFactory.isLiquidStakingManager(address(savETHPool.liquidStakingManager())), "Invalid liquid staking manager" );

However, an attacker can create a fake contract that retuns a correct liquidStakingNetworkManager, thus passing the check easily. After the check, any ETH in the pool will be sent to an address this fake contract provide:

savETHPool.batchDepositETHForStaking{ value: transactionAmount }( _blsPublicKeys[i], _stakeAmounts[i] );

Tools Used

manual

Always passing liquid staking manager address, checking its real and then requesting either the savETH vault or staking funds vault is a good idea rather than other way around from a giant pool perspective.

#0 - c4-judge

2022-11-21T22:20:40Z

dmvt marked the issue as duplicate of #36

#1 - c4-judge

2022-12-02T21:54:05Z

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:15:15Z

liveactionllama marked the issue as not a duplicate

#4 - C4-Staff

2022-12-22T08:15:33Z

liveactionllama marked the issue as duplicate of #251

Findings Information

🌟 Selected for report: rotcivegaf

Also found by: 0x4non, clems4ever, datapunk

Labels

bug
3 (High Risk)
partial-25
satisfactory
upgraded by judge
edited-by-warden
duplicate-328

Awards

102.5291 USDC - $102.53

External Links

Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/SyndicateRewardsProcessor.sol#L67

Vulnerability details

potential reentrancy risk in _distributeETHRewardsToUserForToken

Impact

Reentrancy risk can potentially be demaging

Proof of Concept

_distributeETHRewardsToUserForToken, which in turn calls (bool success, ) = _recipient.call{value: due}("");. As recipient of ETH, the msg.sender can potentially reenter. _distributeETHRewardsToUserForToken is being used in multiple places, such as batchDepositETHForStaking in SavETHVault beforeTokenTransfer in SavETHVault batchDepositETHForStaking in StakingFundsVault beforeTokenTransfer in StakingFundsVault

Tools Used

manual

Careful examination and consider adding reentrancyGuard to above functions.

#0 - c4-judge

2022-11-21T23:20:40Z

dmvt marked the issue as duplicate of #35

#1 - c4-judge

2022-11-29T15:23:07Z

dmvt marked the issue as satisfactory

#2 - c4-judge

2022-11-29T15:53:48Z

dmvt changed the severity to 3 (High Risk)

#3 - c4-judge

2022-11-29T15:53:54Z

dmvt marked the issue as partial-25

#4 - C4-Staff

2022-12-21T00:17:14Z

JeeberC4 marked the issue as duplicate of #35

#5 - liveactionllama

2022-12-22T09:24:46Z

Duplicate of #328

Findings Information

🌟 Selected for report: datapunk

Labels

bug
3 (High Risk)
judge review requested
primary issue
satisfactory
selected for report
sponsor confirmed
H-21

Awards

2925.3837 USDC - $2,925.38

External Links

Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantMevAndFeesPool.sol#L126

Vulnerability details

Impact

real LPTokens can be transferred out of GiantMevAndFeesPool through fake _stakingFundsVaults provided by an attacker. https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantMevAndFeesPool.sol#L126

Proof of Concept

bringUnusedETHBackIntoGiantPool takes in _stakingFundsVaults, _oldLPTokens, _newLPTokens and rotate _amounts from old to new tokens. The tokens are thoroughly verified by burnLPForETH in ETHPoolLPFactory. However, theres is no checking for the validity of _stakingFundsVaults, nor the relationship between LPTokens and _stakingFundsVaults. Therefore, an attacker can create fake contracts for _stakingFundsVaults, with burnLPTokensForETH, that takes LPTokens as parameters. The msg.sender in burnLPTokensForETH is GiantMevAndFeesPool, thus the attacker can transfer LPTokens that belongs to GiantMevAndFeesPool to any addresses it controls.

Tools Used

manual

Always passing liquid staking manager address, checking its real and then requesting either the savETH vault or staking funds vault is a good idea to prove the validity of vaults.

#0 - dmvt

2022-11-21T22:39:12Z

This one also seems wrong. The only real effect I can see is the attacker's savETH vault getting attacker supplied parameters passed back to it. Leaving open for sponsor review.

#1 - c4-judge

2022-11-21T22:39:17Z

dmvt marked the issue as primary issue

#2 - c4-sponsor

2022-11-28T16:40:15Z

vince0656 marked the issue as sponsor confirmed

#3 - vince0656

2022-11-28T16:40:46Z

Duplicate of #365, #364, #363

#4 - c4-sponsor

2022-11-28T16:41:07Z

vince0656 requested judge review

#5 - vince0656

2022-11-28T16:41:20Z

please see notes about duplicates above

#6 - dmvt

2022-12-02T21:58:30Z

I've nullified the other 3 since all four are from the same warden.

#7 - c4-judge

2022-12-02T22:29:10Z

dmvt marked the issue as satisfactory

#8 - c4-judge

2022-12-02T22:29:13Z

dmvt marked the issue as selected for report

Findings Information

🌟 Selected for report: 0xdeadbeef0x

Also found by: bin2chen, datapunk, hihen, koxuan

Labels

bug
2 (Med Risk)
partial-25
satisfactory
duplicate-74

Awards

22.1463 USDC - $22.15

External Links

Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantPoolBase.sol#L34

Vulnerability details

Impact

GiantPoolBase keeps track of idleETH, which can be potentially wrong if Eth are sent directly into the contract not through depositETH. When that happens, idleETH is incorrect.

Proof of Concept

function depositETH(uint256 _amount) public payable { idleETH += msg.value; }

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantPoolBase.sol#L34

Tools Used

manual

add a receive function:

receive() external payable { depositETH(msg.sender); }

#0 - c4-judge

2022-11-21T22:44:43Z

dmvt marked the issue as duplicate of #74

#1 - c4-judge

2022-11-21T22:44:50Z

dmvt marked the issue as partial-25

#2 - c4-judge

2022-11-30T11:50:44Z

dmvt marked the issue as satisfactory

Findings Information

🌟 Selected for report: aphak5010

Also found by: arcoun, datapunk, unforgiven, wait, yixxas

Labels

bug
2 (Med Risk)
downgraded by judge
partial-50
satisfactory
duplicate-98

Awards

33.2194 USDC - $33.22

External Links

Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#L66

Vulnerability details

Impact

User's lpTokenETH and _lpTokens will be out-of-sync https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantSavETHVaultPool.sol#66

Proof of Concept

In withdrawDETH, vault and token are checked for their validity through

_assertUserHasEnoughGiantLPToClaimVaultLP(token, amount);

which, in turn, checks

require(_amount >= MIN_STAKING_AMOUNT, "Invalid amount"); require(_token.balanceOf(address(this)) >= _amount, "Pool does not own specified LP");

and

require(vault.isDETHReadyForWithdrawal(address(token)), "dETH is not ready for withdrawal");

However, if the website gets hacked and when users interact with withdrawDETH, they might be supplying fake _savETHVaults and _lpTokens as specified by the attacker. These fake contracts can return anything needed to pass the above checks easily. After theses checks, users' legitimate lpTokenETH will be burned:

lpTokenETH.burn(msg.sender, amount);

but only fake lpTokens will be burned: getDETH().transfer(msg.sender, dETHReceivedFromAllSavETHVaults);

Tools Used

manual

Always passing liquid staking manager address, checking its real and then requesting either the savETH vault or staking funds vault is a good idea to prove the validity of vaults.

#0 - c4-judge

2022-11-21T22:21:37Z

dmvt marked the issue as duplicate of #98

#1 - c4-judge

2022-11-30T12:31:04Z

dmvt marked the issue as satisfactory

#2 - c4-judge

2022-11-30T12:31:13Z

dmvt marked the issue as partial-50

#3 - c4-judge

2022-11-30T12:31:20Z

dmvt changed the severity to 2 (Med Risk)

Findings Information

🌟 Selected for report: aphak5010

Also found by: Aymen0909, Trust, datapunk, zaskoh

Labels

2 (Med Risk)
partial-50
duplicate-160

Awards

44.2926 USDC - $44.29

External Links

Judge has assessed an item in Issue #373 as M risk. The relevant finding follows:

N2. ETH not accumulated in previewAccumulatedETH https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantMevAndFeesPool.sol#L91 supposed to have accumulated += ... Although it is an external view function, depending on its usages, it may present more issues to the callers.

#0 - c4-judge

2022-12-02T22:00:20Z

dmvt marked the issue as duplicate of #160

#1 - c4-judge

2022-12-02T22:00:34Z

dmvt marked the issue as partial-50

Findings Information

🌟 Selected for report: gz627

Also found by: datapunk

Labels

bug
2 (Med Risk)
satisfactory
duplicate-317

Awards

303.7898 USDC - $303.79

External Links

Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/SavETHVault.sol#L200

Vulnerability details

Impact

If trusted manager supplies a wrong address, the funds might be non-retrievable.

Proof of Concept

in withdrawETHForStaking, the manager can supply any _smartWallet to withdraw ETH, which the _smartWallet can be very easily checked against OwnableSmartWalletFactory, since it keeps track of them in mapping(address => bool) public walletExists; Despite the fact this function is only callable by the trusted manager, it is quite be a scary mistake if wrong address is used.

Tools Used

manual

check supplied _smartWallet against walletExists from OwnableSmartWalletFactory

#0 - c4-judge

2022-11-21T22:42:24Z

dmvt marked the issue as duplicate of #317

#1 - c4-judge

2022-12-02T19:56:21Z

dmvt marked the issue as satisfactory

Findings Information

🌟 Selected for report: Trust

Also found by: Lambda, datapunk

Labels

bug
2 (Med Risk)
downgraded by judge
partial-25
satisfactory
duplicate-422

Awards

45.5685 USDC - $45.57

External Links

Lines of code

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/ETHPoolLPFactory.sol#L111 https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/ETHPoolLPFactory.sol#L80

Vulnerability details

Impact

Deposits tolpToken can be made such as it will never reach 24 ether as required

Proof of Concept

Funds can be deposited into lpToken through either _depositETHForStaking or 'rotateLPTokens', both have: require(_amount >= MIN_STAKING_AMOUNT, "Min amount not reached"); Both also requires that the total deposit per lpToken does not exceed 24 ether: _depositETHForStaking: require(lpToken.totalSupply() + _amount <= maxStakingAmountPerValidator require(_amount <= maxStakingAmountPerValidator and rotateLPTokens: require(_amount + _newLPToken.totalSupply() <= 24 ether, "Not enough mintable tokens");.

An attacker can monitor deposits and front-running them to make the totalSupply in the range of (23.999, 24). In such cases, users would have to withdraw their ETH to correct.

Tools Used

manual

in deposit and rotate, adding require(amount % MIN_STAKING_AMOUNT == 0); will prevent such attacks.

#0 - c4-judge

2022-11-21T22:40:29Z

dmvt marked the issue as duplicate of #140

#1 - c4-judge

2022-11-30T14:01:02Z

dmvt changed the severity to 2 (Med Risk)

#2 - c4-judge

2022-11-30T14:01:27Z

dmvt marked the issue as partial-25

#3 - c4-judge

2022-11-30T14:01:56Z

dmvt marked the issue as satisfactory

#4 - C4-Staff

2022-12-21T00:06:23Z

JeeberC4 marked the issue as duplicate of #422

N1. dividing before multiplication

The way claims are calculated, it is dividing by _numOfShares first, then multiply by _balanceOfSende: uint256 newAccumulatedETH = accumulatedETHPerLPShare + ((unprocessed * PRECISION) / _numOfShares); ((newAccumulatedETH * _balanceOfSender) / PRECISION) https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/ETHPoolLPFactory.sol#43 However, since PRECISION is present, I am not sure how much the impact is. Just fruit for thought to potentially refactor the formula

N2. ETH not accumulated in previewAccumulatedETH

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantMevAndFeesPool.sol#L91 supposed to have accumulated += ... Although it is an external view function, depending on its usages, it may present more issues to the callers.

N3. in _addPriorityStakers, duplicated stakers are pass the dup check

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/syndicate/Syndicate.sol#L588 checks if (i > 0 && staker < _priorityStakers[i-1]) revert DuplicateArrayElements(); while it should check if (i > 0 && staker <= _priorityStakers[i-1]) revert DuplicateArrayElements();

N4. incorrect comment

require(stakingFundsLP.totalSupply() == 4 ether, "DAO staking funds vault balance must be at least 4 ether"); should be: require(stakingFundsLP.totalSupply() == 4 ether, "DAO staking funds vault balance must have 4 ether"); https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/LiquidStakingManager.sol#L940

N5. redundant calls to updateAccumulatedETHPerLP

in StakingFundsVault, the last thing _claimFundsFromSyndicateForDistribution does is to call updateAccumulatedETHPerLP, yet everywhere _claimFundsFromSyndicateForDistribution is called, it is followed by updateAccumulatedETHPerLP. The 2nd does nothing, so can be removed.

N6. unstake in Syndicate does not check for 0 amount.

https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/syndicate/Syndicate.sol#L259 There is no minimum value for _sETHAmount to be unstaked. When 0 amount is supplied, it should be checked and skipped early.

N7. not checking against address(0)

Add require(_pool != address(0)) to https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/GiantLP.sol#L25

Add require(_liquidStakingManager != address(0)) to https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/OptionalGatekeeperFactory.sol#L12

Add require(_manager != address(0)) to https://github.com/code-423n4/2022-11-stakehouse/blob/4b6828e9c807f2f7c569e6d721ca1289f7cf7112/contracts/liquid-staking/OptionalHouseGatekeeper.sol#L14

N8. wrong error message

if (_recipient == address(this)) revert ZeroAddress(); if (getSlotRegistry().currentSlashedAmountOfSLOTForKnot(blsPubKey) != 0) revert InvalidNumberOfCollateralizedOwners();

N9. make contract GiantPoolBase abstract

#0 - c4-judge

2022-12-02T21:59:32Z

dmvt marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter