Liquid Staking

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

Flash Loan Vulnerability in StakingPool Contract

Summary

StakingPool.sol has a flash loan vulnerability that allows users to manipulate their balance of liquid staking tokens. Stems from improper validation in the deposit function, where the contract mints liquid staking tokens to the user's account before updating the totalStaked variable. This enables users to deposit a large amount and withdraw a smaller amount within the same block, resulting in a net increase in their balance of liquid staking tokens.

Vulnerability Details

The StakingPool contract in StakingPool.sol contains a vulnerability that allows users to manipulate their balance of liquid staking tokens by exploiting the deposit function. The issue arises due to improper validation of the relationship between the deposited amount and the total staked amount.

In the deposit function, the contract mints liquid staking tokens to the user's account before updating the totalStaked variable. This allows a user to deposit a large amount of tokens and withdraw a smaller amount within the same block, resulting in a net increase in their balance of liquid staking tokens.

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

function deposit(
address _account,
uint256 _amount,
bytes[] calldata _data
) external onlyPriorityPool {
require(strategies.length > 0, "Must be > 0 strategies to stake");
uint256 startingBalance = token.balanceOf(address(this));
if (_amount > 0) {
token.safeTransferFrom(msg.sender, address(this), _amount);
_depositLiquidity(_data);
// @audit: Minting tokens before updating totalStaked
_mint(_account, _amount); // <-- @audit: Vulnerable line
totalStaked += _amount;
} else {
_depositLiquidity(_data);
}
uint256 endingBalance = token.balanceOf(address(this));
if (endingBalance > startingBalance && endingBalance > unusedDepositLimit)
revert InvalidDeposit();
}

By calling _mint before updating totalStaked, the contract enables users to manipulate their balance and gain more liquid staking tokens than they should have.

Step-by-step demonstrating how the vulnerability can be exploited:

  1. Assume the user has an initial balance of 100 liquid staking tokens.

  2. The user calls the deposit function with an _amount of 1000 tokens.

    • The contract transfers 1000 tokens from the user to itself.

    • The contract mints 1000 liquid staking tokens to the user's account.

    • The totalStaked variable is incremented by 1000.

    • The user now has a balance of 1100 liquid staking tokens.

  3. Within the same block, the user calls the withdraw function with an amount of 800 tokens.

    • The contract burns 800 liquid staking tokens from the user's account.

    • The contract transfers 800 tokens from itself to the user.

    • The totalStaked variable is decremented by 800.

  4. After the deposit and withdraw transactions, the user's balance of liquid staking tokens is 300 (1100 - 800), which is greater than the initial balance of 100.

Impact

  • If multiple users exploit the vulnerability, it can lead to an inflated supply of liquid staking tokens, diluting the value of existing tokens held by other users.

  • If users withdraw more tokens than they initially deposited, it can drain the staking pool's funds, potentially leading to a loss of funds for other users who have legitimately staked their tokens.

Tools Used

Vs

Recommendations

Add proper validation in the deposit function to ensure that the total staked amount after the deposit does not exceed the actual amount of tokens held by the contract.

if (_amount > 0) {
token.safeTransferFrom(msg.sender, address(this), _amount);
_depositLiquidity(_data);
+ require(token.balanceOf(address(this)) >= totalStaked + _amount, "Insufficient liquidity");
_mint(_account, _amount);
totalStaked += _amount;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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