Open c4-bot-9 opened 4 months ago
msg.sender
is the owner that stakes the service, and it's exactly the same owner that is able to unstake it. What is the scenario that msg.sender
was able to be the owner of the service before staking it, but is no longer able to receive ERC721 token when unstaking?
Our protocol assumes a valid ERC721 standard used in all the possible service registry contracts. If someone decides to use a custom broken ERC721 contract that is able to mint tokens to the contract, but cannot receive one by the transfer, this is out of scope of our protocol.
kupermind (sponsor) disputed
After several communication rounds we can accept the issue, however the declared severity completely does not match the issue. There is zero risk factor for the protocol, only the user stupidity that have not checked the staking contract requirements.
None of well-known contract wallets reject the contract ERC721 support. This must be an artificially created scenario when the user deliberately uses the contract without such a support. If one is talking about outdated Safe, for example, there is a way to upgrade the Safe version, and then provide the correct fallbackHandler contract address to deal with ERC721 contracts.
0xA5DF changed the severity to 2 (Med Risk)
0xA5DF marked the issue as satisfactory
0xA5DF marked the issue as selected for report
I agree that this is less likely to happen and somewhat on the user to ensure that their contract can receive ERC721 However, I think that the likelihood is sufficient to consider this as med, given the high impact.
For transparency, per discord discussion with the Olas sponsor (kupermind) the labeling has been updated to sponsor acknowledged.
Fixed
Lines of code
https://github.com/code-423n4/2024-05-olas/blob/3ce502ec8b475885b90668e617f3983cea3ae29f/registries/contracts/staking/StakingBase.sol#L864
Vulnerability details
The
StakingBase
contract allows users to stake services represented by ERC721 tokens. These services are freely transferable, meaning they could be staked by contracts that do not implement theERC721TokenReceiver
interface.When a user calls
unstake()
to withdraw their staked service, the contract attempts to transfer the ERC721 token back to themsg.sender
(which must match the original depositor) usingsafeTransferFrom()
:However, if the owner is a contract that does not support receiving ERC721 tokens, this transfer will fail. As a result, the service associated with
serviceId
will be permanently locked in theStakingBase
contract, and the original owner will be unable to recover it.Impact
Staked services will become permanently irrecoverable if the owner cannot receive ERC721 tokens.
Proof of Concept
stake(serviceId)
on theStakingBase
contract from a smart contract that is not anERC721TokenReceiver
, and cannot be upgraded to support this (e.g. a Safe with restricted privileges). The service is transferred from the user to the contract.unstake(serviceId)
, thesafeTransferFrom()
call in the function reverts.StakingBase
contract indefinitely, and the user is unable to recover it.Tools Used
Manual review
Recommended Mitigation Steps
Consider using
transferFrom()
instead ofsafeTransferFrom()
when transferring the service back to the owner inunstake()
. This will allow the transfer to succeed even if the recipient is not an ERC721 receiver.Assessed type
ERC721