Liquid Staking

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

Invalid strategy indexes leading to improper interactions with the strategy array in `StakingPool.sol`

Summary

The Staking Pool contract allows interaction with multiple strategies for depositing and withdrawing staked tokens. However, insufficient validation of strategy index values in functions like strategyDeposit, strategyWithdraw, and removeStrategy introduces potential vulnerabilities. An attacker or an unexpected sequence of events could exploit these weaknesses, leading to improper interactions with the strategy array, which could cause incorrect operations, break the pool’s logic, or result in loss of funds.

Vulnerability Details

The contract interacts with an array of strategies via their indexes in various functions, such as strategyDeposit, strategyWithdraw, and removeStrategy. The functions do check if the provided index is less than the length of the strategies array, but there’s no robust validation for checking whether the actual strategy address at that index is valid. A compromised or erroneous sequence of actions (e.g., removing a strategy during a withdrawal) could result in incorrect interactions with invalid or non-existent strategies.

This issue could allow for unintended behaviors, such as:

  • Accessing a removed strategy or an uninitialized address.

  • Incorrectly handling funds if the invalid strategy does not behave as expected (e.g., a zero address or an address without expected code).
    In this scenario, an attacker can potentially manipulate the strategy order or pass an invalid index to interact with non-existent strategies, causing a malfunction.

Realistic attack scenario:

  1. A legitimate user adds a new strategy.

  2. The attacker or owner reorders the strategies in such a way that the indexes are misaligned (e.g., a new strategy is pushed in between).

  3. The attacker calls removeStrategy or strategyWithdraw with the old index of the strategy (which is now pointing to a different strategy or an invalid address).

  4. The contract processes an operation on an invalid or unintended strategy, which could result in unintended token transfers or leaving the pool in an inconsistent state.

Here's how you can simulate the issue in Hardhat:

  1. Contract setup:
    Start by deploying the contract with several strategies and add some token deposits to simulate normal operations.

// Solidity code snippets in Hardhat test setup
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Invalid Strategy Index PoC", function () {
let stakingPool, strategy1, strategy2, token, owner, attacker;
beforeEach(async function () {
[owner, attacker] = await ethers.getSigners();
const Token = await ethers.getContractFactory("MockERC20");
token = await Token.deploy();
await token.deployed();
const Strategy = await ethers.getContractFactory("MockStrategy");
strategy1 = await Strategy.deploy(token.address);
strategy2 = await Strategy.deploy(token.address);
const StakingPool = await ethers.getContractFactory("StakingPool");
stakingPool = await StakingPool.deploy();
await stakingPool.initialize(token.address, "StakeToken", "STK", [], 1000);
await stakingPool.addStrategy(strategy1.address);
await stakingPool.addStrategy(strategy2.address);
// Attacker gets some tokens
await token.mint(attacker.address, 1000);
});
it("Exploiting invalid strategy index", async function () {
// Initial deposit into strategies
await token.connect(attacker).approve(stakingPool.address, 1000);
await stakingPool.connect(attacker).deposit(attacker.address, 500, []);
// Remove the first strategy and reorder the list
await stakingPool.connect(owner).removeStrategy(0, [], []);
// Now, attacker tries to call a strategy operation with an invalid index
await expect(stakingPool.connect(attacker).strategyWithdraw(0, 100, []))
.to.be.revertedWith("Strategy does not exist");
// Attacker can potentially cause issues by trying to withdraw from a now-invalid strategy index
});
});

Steps:

  1. Deploy contract: Set up the Staking Pool and two strategies (strategy1 and strategy2).

  2. Add strategies: Use the contract's addStrategy() function to add strategies.

  3. Perform a deposit: Simulate a user depositing into the pool.

  4. Remove and reorder strategies: Simulate the owner removing a strategy, leaving an invalid index reference.

  5. Test invalid access: After the strategy is removed, attempt to call strategyWithdraw() with the invalid index.

Impact

  1. If invalid strategies are accessed, funds could be locked, lost, or transferred incorrectly.

  2. Interacting with an invalid strategy (e.g., an uninitialized address or zero address) could cause the pool to malfunction, breaking its staking, deposit, or withdrawal operations.

  3. In a worst-case scenario, the attacker could exploit this vulnerability to perform malicious actions, such as blocking withdrawals or causing other strategies to be drained.

Tools Used

Manual review.

Recommendations

  1. Ensure that the address of the strategy at the provided index is not a zero address and that it matches the expected format for a strategy contract (e.g., via a contract interface check).

  2. Before performing operations, ensure the index and its corresponding strategy have been fully validated.

  3. Consider using mappings with unique IDs for strategies rather than arrays to eliminate index-related risks.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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