MorpheusAI

MorpheusAI
Foundry
22,500 USDC
View results
Submission Details
Severity: high
Invalid

Cross Function Reentrancy Attack in `_stake` and `_withdraw`

Summary

In the _stake function of a smart contract, the variables balanceAfter_ and amount_ are updated after a call to safeTransferFrom. This procedure exposes the contract to reentrancy attacks, allowing an attacker to potentially exploit the _stake function multiple times under specific conditions.

Vulnerability Details

The _stake function updates state variables such as balanceAfter_ and amount_ after executing a safeTransferFrom call. This logic is intended to adjust the stake amount based on the actual token balance transferred into the contract. However, the placement of these updates creates a vulnerability:

function _stake(address user_, uint256 poolId_, uint256 amount_, uint256 currentPoolRate_) private {
...
if (pool.isPublic) {
uint256 balanceBefore_ = IERC20(depositToken).balanceOf(address(this));
IERC20(depositToken).safeTransferFrom(_msgSender(), address(this), amount_);
uint256 balanceAfter_ = IERC20(depositToken).balanceOf(address(this));
amount_ = balanceAfter_ - balanceBefore_;
require(userData.deposited + amount_ >= pool.minimalStake, "DS: amount too low");
totalDepositedInPublicPools += amount_;
}
...
}

This design flaw allows an attacker to re-enter the stake function by calling it again after the safeTransferFrom but before the balanceAfter is updated. This scenario becomes exploitable especially when the attacker already has a stake in the pool and stakes again after the withdrawLockPeriod has ended. They can then bypass this period, allowing for immediate withdrawal and potentially exploiting both _stake and _withdraw functions.

Example Scenario:

Alice stakes at block timestamp 310256.
The withdrawLockPeriod ends at block timestamp 311244.
Alice stakes again at block timestamp 311244.
Alice is able to bypass the withdrawLockPeriod, exploiting the smart contract.

Impact

The vulnerability poses a significant risk of loss of funds for the protocol and its users, undermining the security and trust in the system.

Tools Used

Manual analysis was employed to identify and assess the vulnerability.

Recommendations

To mitigate this vulnerability, it's recommended to update the stake function to ensure that the balanceAfter and amount_ variables are updated before the safeTransferFrom call. This adjustment prevents the potential for reentrancy by locking in the state before external interactions:

function _stake
(address user_, uint256 poolId_, uint256 amount_, uint256 currentPoolRate_) private {
...
if (pool.isPublic) {
uint256 balanceBefore_ = IERC20(depositToken).balanceOf(address(this));
+ uint256 balanceAfter_ = IERC20(depositToken).balanceOf(address(this));
+ amount_ = balanceAfter_ - balanceBefore_;
IERC20(depositToken).safeTransferFrom(_msgSender(), address(this), amount_);
- uint256 balanceAfter_ = IERC20(depositToken).balanceOf(address(this));
- amount_ = balanceAfter_ - balanceBefore_;
require(userData.deposited + amount_ >= pool.minimalStake, "DS: amount too low");
totalDepositedInPublicPools += amount_;
}
...
}

This change ensures that the contract's state is correctly updated before interacting with external contracts, significantly reducing the risk of reentrancy attacks.

Updates

Lead Judging Commences

inallhonesty Lead Judge
over 1 year ago
inallhonesty Lead Judge over 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.