Liquid Staking

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

Critical Reentrancy Vulnerability in LSTRewardsSplitter Contract Leading to Denial of Service and Unintended Token Burning

Summary

A critical vulnerability exists in the _splitRewards function of the LSTRewardsSplitter contract, where the lack of reentrancy protection allows an attacker to perform a reentrancy attack. This attack enables the attacker to repeatedly invoke the _splitRewards function during the execution of a token transfer, potentially leading to the unintended burning of user rewards or a denial-of-service (DoS) condition. The vulnerability arises from the use of an ERC777 token (lst) and the absence of safeguards against reentrancy in functions that perform external calls.

Vulnerability Details

Vulnerability Function

function _splitRewards(uint256 _rewardsAmount) private {
for (uint256 i = 0; i < fees.length; ++i) {
Fee memory fee = fees[i];
uint256 amount = (_rewardsAmount * fee.basisPoints) / 10000;
if (fee.receiver == address(lst)) {
IStakingPool(address(lst)).burn(amount);
} else {
lst.safeTransfer(fee.receiver, amount);
}
}
principalDeposits = lst.balanceOf(address(this));
emit RewardsSplit(_rewardsAmount);
}

Vulnerability Description

  • Reentrancy Risk: The _splitRewards function makes external calls to lst.safeTransfer and IStakingPool(address(lst)).burn without any reentrancy protection such as the nonReentrant modifier.

  • ERC777 TokensReceived Hook: The lst token is likely an ERC777 token, which triggers the tokensReceived hook on the recipient contract whenever tokens are transferred. This allows a recipient contract to execute code during the transfer process.

  • Fee Receiver as Malicious Contract: An attacker can set a fee receiver to be a malicious contract that implements the tokensReceived function. Within this function, the attacker can re-enter the _splitRewards function by calling splitRewards() again.

Attack Scenario

  1. Setup:

    • The attacker deploys a malicious contract that implements the tokensReceived function. This function will call the splitRewards function on the LSTRewardsSplitter contract when it receives tokens.

    • The attacker gets this contract added as a fee receiver in the fees array of the LSTRewardsSplitter contract, potentially by social engineering the contract owner or exploiting any insufficient access controls.

  2. Execution:

    • When _splitRewards is called, and it reaches the lst.safeTransfer(fee.receiver, amount); line, the tokensReceived function on the attacker's contract is triggered due to the ERC777 token standard.

    • Inside tokensReceived, the attacker calls splitRewards() again.

    • This reentrancy causes _splitRewards to execute again before the first execution has completed.

  3. Impact:

    • Infinite Loop / Stack Depth Limit: The recursive calls can lead to an infinite loop or reach the EVM's call stack depth limit, causing the transaction to fail.

    • Denial of Service: Legitimate users are unable to receive their rewards because the transaction reverts or consumes excessive gas.

    • Burning Excessive Tokens: If the burn function is called multiple times due to reentrancy, more tokens than intended may be burned, reducing the total supply and harming token holders.

Impact

  • Denial of Service (DoS): The reentrancy attack can prevent the successful execution of the _splitRewards function, blocking reward distribution to all users.

  • Unintended Token Burning: Excessive or unintended burning of LST tokens reduces the total supply, adversely affecting the value of tokens held by users.

  • Financial Loss: Users may suffer financial losses due to the decreased value of their holdings or inability to access their rewards.

  • Loss of Trust: Exploitation of this vulnerability undermines confidence in the smart contract's security and the platform's reliability.

Tools Used

Manual Code Review: Careful examination of the smart contract code to identify potential vulnerabilities.

  • Solidity Documentation: Understanding of the ERC777 token standard and its tokensReceived hook.

  • Security Analysis Tools: Utilization of static analysis tools like Slither or MythX to detect reentrancy vulnerabilities.

  • Testing Frameworks: Using frameworks like Truffle or Hardhat to simulate the attack scenario in a controlled environment.

Recommendations

Implement Reentrancy Guards:

  • Use ReentrancyGuard: Import OpenZeppelin's ReentrancyGuard contract and apply the nonReentrant modifier to the _splitRewards function and any other functions that make external calls and modify state.

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract LSTRewardsSplitter is Ownable, ReentrancyGuard {
// ...
function _splitRewards(uint256 _rewardsAmount) private nonReentrant {
// Function body
}
}

Update State Variables Before External Calls:

  • Adjust principalDeposits Early: Update critical state variables like principalDeposits before making any external calls to prevent state inconsistencies during reentrancy.

function _splitRewards(uint256 _rewardsAmount) private nonReentrant {
principalDeposits = lst.balanceOf(address(this)) - _rewardsAmount;
// Rest of the function
}
  • Limit or Validate Fee Receivers:

    • Disallow Contracts as Fee Receivers: Ensure that fee receivers are not contracts, or if they are, that they do not implement malicious behavior.

    • Validate Addresses: Implement checks to prevent adding the LST token address or other sensitive addresses as fee receivers.

  • Use Safe Transfer Methods Appropriately:

    • Avoid Using safeTransfer with ERC777 Tokens: Since safeTransfer can trigger the tokensReceived hook, consider using transfer or a lower-level call when transferring ERC777 tokens, while ensuring compliance with the token standard.

      lst.transfer(fee.receiver, amount);
  • Implement Pull Over Push Mechanism:

    • Pull Payments: Instead of pushing tokens to fee receivers, update their balances and allow them to withdraw funds themselves. This minimizes external calls within critical functions.

  • Limit the Size of Loops:

    • Cap Fees Array Length: Set an upper limit on the number of fee receivers to prevent unbounded loops that can lead to DoS due to gas limits.

  • Thorough Testing and Auditing:

    • Security Audits: Engage professional auditors to review the smart contracts for vulnerabilities.

    • Comprehensive Testing: Write unit tests and integration tests that simulate attack scenarios, including reentrancy attacks.

Updates

Lead Judging Commences

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

Support

FAQs

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