MorpheusAI

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

users can withdraw all its deposited tokens of a Pool and will still have huge number of Tokens due to underflow resulting in complete drainage of Contracts Fund/Tokens

Summary

During withdraw of token via withdraw(uint256 poolId_, uint256 amount_) function in case of public pool , which internally calls _withdraw(address user_, uint256 poolId_, uint256 amount_, uint256 currentPoolRate_) , Attacker/user with some deposited amount in pool can withdraw all its deposited token and set the newDeposited_ to Large value due to underflow of deposited_ - amount_ value, and thus can drain all the tokens from the contract . This is possible in case when amount > depositedTokenCOntractBalance and userData.deposited / deposited_ < depositedTokenCOntractBalance .

Vulnerability Details

In the function below

function _withdraw(address user_, uint256 poolId_, uint256 amount_, uint256 currentPoolRate_) private {
Pool storage pool = pools[poolId_];
PoolData storage poolData = poolsData[poolId_];
UserData storage userData = usersData[user_][poolId_];
uint256 deposited_ = userData.deposited;
require(deposited_ > 0, "DS: user isn't staked");
i)--> if (amount_ > deposited_) {
amount_ = deposited_;
}
uint256 newDeposited_;
ii) --> if (pool.isPublic) {
require(
block.timestamp < pool.payoutStart ||
(block.timestamp > pool.payoutStart + pool.withdrawLockPeriod &&
block.timestamp > userData.lastStake + pool.withdrawLockPeriodAfterStake),
"DS: pool withdraw is locked"
);
uint256 depositTokenContractBalance_ = IERC20(depositToken).balanceOf(address(this)); //@audit
iii) --> if (amount_ > depositTokenContractBalance_) {
amount_ = depositTokenContractBalance_;
}
iv)--> newDeposited_ = deposited_ - amount_;
require(amount_ > 0, "DS: nothing to withdraw");
require(newDeposited_ >= pool.minimalStake || newDeposited_ == 0, "DS: invalid withdraw amount");
} else {
newDeposited_ = deposited_ - amount_;
}
uint256 pendingRewards_ = _getCurrentUserReward(currentPoolRate_, userData);
// Update pool data
poolData.lastUpdate = uint128(block.timestamp);
poolData.rate = currentPoolRate_;
poolData.totalDeposited -= amount_;
// Update user data
userData.rate = currentPoolRate_;
userData.deposited = newDeposited_;
userData.pendingRewards = pendingRewards_;
if (pool.isPublic) {
totalDepositedInPublicPools -= amount_;
IERC20(depositToken).safeTransfer(user_, amount_);
}
emit UserWithdrawn(poolId_, user_, amount_);
}

// // @audit overflow , ex deposited - 50 , amount - 1000, depositedTokenCOntractBalance - 500
Lets say
pool = public
deposited_ = 50
amount_ = 1000
depositedTokenContractBalance = 500

at i) amount_ > deposited_
thus , amount = 50;
at ii) pool is public , thus goes inside if function
Now lets say , depositedTokenContractBalance = 500
at iii) amount_ > depositedTokenContractBalance
thus , amount_ = 1000;
at iv) newDeposited_ = deposited_ - amount_;
newDeposited = 50 - 1000 (Underflow)
thus newDeposited = veryLarge Number .

Thus Attacker will not only withdraw all its tokens but will gain large amount of tokens as a result of underflow , which can drain all the tokens of Contract .

Impact

Critical

Tools Used

Hardhat, remix

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.