You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Jan 12, 2025. It is now read-only.
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelMediumA Medium severity issue.RewardA payout will be made for this issue
An attacker can prevent anyone from unstaking in MlumStaking.sol
Summary
MlumStaking.sol: addToPosition() can be used by a user to increase his staked amount. It calls _requireOnlyOperatorOrOwnerOf() which should restrict the access to this function and allow only the owner of the staking position to call it. However anyone can still do so.
Vulnerability Detail
Let's look at the implementation of _requireOnlyOperatorOrOwnerOf():
function _requireOnlyOperatorOrOwnerOf(uint256tokenId) internalview {
// isApprovedOrOwner: caller has no rights on tokenrequire(ERC721Upgradeable._isAuthorized(msg.sender, msg.sender, tokenId), "FORBIDDEN");
}
It calls _isAuthorized on the ERC721Upgradeable contract:
/** * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in * particular (ignoring whether it is owned by `owner`). * * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this * assumption. */function _isAuthorized(addressowner, addressspender, uint256tokenId) internalviewvirtualreturns (bool) {
return
spender !=address(0) &&
(owner == spender ||isApprovedForAll(owner, spender) ||_getApproved(tokenId) == spender);
}
The warning message in the comments explains that this function doesn't verify if owner is the actual owner of the token (staking position).
_requireOnlyOperatorOrOwnerOf() calls this function with msg.sender for both the owner and the spender parameter. With these inputs _isAuthorized() will return true:
spender != address(0) (true) and owner == spender (true).
As you can see no matter who msg.sender is this will always return true.
This means that there is no access control whatsoever and even if you are not the owner of the staking position you can still call addToPosition().
Impact
Here is part of the implementation for addToPostion():
This function not only increases the amount of staked tokens for a user but it also adjusts the position.lockDuration of his stake by averaging out his previous stake and the amount he is adding now. It also sets the position.startLockTime to the current block.timestamp. position.lockDuration and position.startLockTime are both used to determine if a user should be able to withdraw his staked tokens:
By knowing all this we can come to the conclusion that an attacker can call addToPosition() using anyones tokenId (staking position) as input and prevent them from ever withdrawing by increasing their staked position by a small amount. This will increase the duration of their stake every time and they won't be able to withdraw.
Adjust the _requireOnlyOperatorOrOwnerOf() function:
function _requireOnlyOperatorOrOwnerOf(uint256 tokenId) internal view {
// isApprovedOrOwner: caller has no rights on token
- require(ERC721Upgradeable._isAuthorized(msg.sender, msg.sender, tokenId), "FORBIDDEN");+ require(ERC721Upgradeable._isAuthorized(_ownerOf(tokenId), msg.sender, tokenId), "FORBIDDEN");
}
sherlock-admin4
changed the title
Damaged Mandarin Flamingo - An attacker can prevent anyone from unstaking in MlumStaking.sol
0xboriskataa - An attacker can prevent anyone from unstaking in MlumStaking.solJul 29, 2024
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelMediumA Medium severity issue.RewardA payout will be made for this issue
0xboriskataa
High
An attacker can prevent anyone from unstaking in
MlumStaking.sol
Summary
MlumStaking.sol: addToPosition()
can be used by a user to increase his staked amount. It calls_requireOnlyOperatorOrOwnerOf()
which should restrict the access to this function and allow only the owner of the staking position to call it. However anyone can still do so.Vulnerability Detail
Let's look at the implementation of
_requireOnlyOperatorOrOwnerOf()
:It calls
_isAuthorized
on theERC721Upgradeable
contract:The warning message in the comments explains that this function doesn't verify if
owner
is the actual owner of the token (staking position)._requireOnlyOperatorOrOwnerOf()
calls this function withmsg.sender
for both theowner
and thespender
parameter. With these inputs_isAuthorized()
will return true:As you can see no matter who
msg.sender
is this will always returntrue
.This means that there is no access control whatsoever and even if you are not the owner of the staking position you can still call
addToPosition()
.Impact
Here is part of the implementation for
addToPostion()
:This function not only increases the amount of staked tokens for a user but it also adjusts the
position.lockDuration
of his stake by averaging out his previous stake and the amount he is adding now. It also sets theposition.startLockTime
to the currentblock.timestamp
.position.lockDuration
andposition.startLockTime
are both used to determine if a user should be able to withdraw his staked tokens:By knowing all this we can come to the conclusion that an attacker can call
addToPosition()
using anyones tokenId (staking position) as input and prevent them from ever withdrawing by increasing their staked position by a small amount. This will increase the duration of their stake every time and they won't be able to withdraw.Code Snippet
https://github.com/sherlock-audit/2024-06-magicsea/blob/main/magicsea-staking/src/MlumStaking.sol#L140-L143
https://github.com/sherlock-audit/2024-06-magicsea/blob/main/magicsea-staking/src/MlumStaking.sol#L398
https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/token/ERC721/ERC721Upgradeable.sol#L208-L219
Tool used
Manual Review
Recommendation
Adjust the
_requireOnlyOperatorOrOwnerOf()
function:Duplicate of #378
The text was updated successfully, but these errors were encountered: