DeFiFoundry
20,000 USDC
View results
Submission Details
Severity: high
Invalid

Malicious sablier sender can transfer withdrawable tokens in sablier to staking contract

Summary

If one stream recipient stakes his stream NFT into staking contract, the related stream sender can withdraw this stream's withdraw-able Fjord token into staking contract. These Fjord tokens will be taken as the reward. The stakers will share this rewards. And the stream recipient will lose his withdraw-able Fjord token in sablier stream.

Vulnerability Details

Sablier stream receipt can stake his stream NFT into staking contract. After that, the initial stream receipt cannot withdraw Fjord tokens from Sablier stream. Because only NFT's owner or the stream sender can trigger the sablier withdraw() function in Sablier, and the initial stream receipt has transferred the NFT to Staking contract.

The problem is that the sablier stream sender can still trigger withdraw() function in sablier. The only limitation in Sablier is that the sablier stream sender can only withdraw the withdrawable Fjord tokens to the NFT's owner. If one Sablier receipt stakes NFT in staking contract, the new NFT owner is the staking contract. This means that the sablier stream sender can withdraw sablier stream Fjord tokens into staking contract.
The result is that the sablier receipt will lose their withdrawable Fjord tokens. And these tokens will be taken as the rewards in staking contract. Stakers will get profit from this.

function stakeVested(uint256 _streamID) external checkEpochRollover redeemPendingRewards {
...
//INTERACT
// msg.sender must own this NFT.
sablier.transferFrom({ from: msg.sender, to: address(this), tokenId: _streamID });
points.onStaked(msg.sender, _amount);
emit VestedStaked(msg.sender, currentEpoch, _streamID, _amount);
}
function withdraw(
uint256 streamId,
address to,
uint128 amount
)
public
override
noDelegateCall
updateMetadata(streamId)
{
...
// Checks: `msg.sender` is the stream's sender, the stream's recipient, or an approved third party.
if (!_isCallerStreamSender(streamId) && !_isCallerStreamRecipientOrApproved(streamId)) {
revert Errors.SablierV2Lockup_Unauthorized(streamId, msg.sender);
}
// Checks: if `msg.sender` is the stream's sender, the withdrawal address must be the recipient.
if (_isCallerStreamSender(streamId) && to != _ownerOf(streamId)) {
revert Errors.SablierV2Lockup_InvalidSenderWithdrawal(streamId, msg.sender, to);
}
...
// Checks, Effects and Interactions: make the withdrawal.
_withdraw(streamId, to, amount);
}

Impact

The result is that the sablier receipt will lose their withdrawable Fjord tokens. And these tokens will be taken as the rewards in staking contract. Stakers will get profit from this.

Tools Used

Manual

Recommendations

We need to do some record in onStreamWithdrawn(). Once we received some withdrawable Fjord tokens, we need to record them and allow the NFT staker to withdraw this and exclude this from the totalRewards.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 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.