Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: high
Invalid

Initialize function can be called multiple times

Summary

The StakingPool contract's initialize function is vulnerable to multiple initializations, allowing the token address to be modified after the initial contract deployment. This breaks the assumption that the token address remains constant throughout the contract's lifecycle and can lead to inconsistencies and potential loss of funds for users.

Vulnerability Details

This violates the expected behavior of the token address remaining constant throughout the contract's lifecycle.

The vulnerability stems from the initialize function, which is responsible for setting up the contract's initial state, including the token address. However, the current implementation of the initialize function lacks proper access control and can be called multiple times with different token addresses.

https://github.com/Cyfrin/2024-09-stakelink/blob/f5824f9ad67058b24a2c08494e51ddd7efdbb90b/contracts/core/StakingPool.sol#L65-L78

/// @audit initialize function can be called multiple times, allowing token address to be changed
function initialize(
address _token,
string memory _liquidTokenName,
string memory _liquidTokenSymbol,
Fee[] memory _fees,
uint256 _unusedDepositLimit
) public initializer {
__StakingRewardsPool_init(_token, _liquidTokenName, _liquidTokenSymbol);
for (uint256 i = 0; i < _fees.length; i++) {
fees.push(_fees[i]);
}
require(_totalFeesBasisPoints() <= 4000, "Total fees must be <= 40%");
unusedDepositLimit = _unusedDepositLimit;
}

The initialize function takes the _token address as a parameter and passes it to the __StakingRewardsPool_init function to set the token address. However, there is no mechanism in place to prevent the initialize function from being called multiple times with different token addresses.

As a result, an attacker can exploit this vulnerability by calling the initialize function multiple times with different token addresses, effectively changing the token address used by the StakingPool contract after its initial deployment.

To reproduce:

  1. Call the initialize function with a specific token address, e.g., initialize(address(0x1234), "LiquidToken", "LT", new Fee[](0), 1000).

  2. Verify that the token() function returns the address 0x1234.

  3. Call the initialize function again with a different token address, e.g., initialize(address(0x5678), "LiquidToken", "LT", new Fee[](0), 1000).

  4. Verify that the token() function now returns the address 0x5678, demonstrating that the token address has been changed.

Impact

If the initialize function is called multiple times with different token addresses, it will update the token address each time, leading to an inconsistent state of the contract. This can cause confusion and errors for users who interact with the contract expecting a specific token address.

  • Users may accidentally send the wrong tokens to the contract due to the changed token address, resulting in a loss of funds.

Tools Used

Manual Review

Recommendations

+bool private initialized;
function initialize(
address _token,
string memory _liquidTokenName,
string memory _liquidTokenSymbol,
Fee[] memory _fees,
uint256 _unusedDepositLimit
-) public initializer {
+) public {
+ require(!initialized, "Contract has already been initialized");
+ initialized = true;
__StakingRewardsPool_init(_token, _liquidTokenName, _liquidTokenSymbol);
for (uint256 i = 0; i < _fees.length; i++) {
fees.push(_fees[i]);
}
require(_totalFeesBasisPoints() <= 4000, "Total fees must be <= 40%");
unusedDepositLimit = _unusedDepositLimit;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.