Flow

Sablier
FoundryDeFi
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

Zero address transfer vulnerability in SablierFlowBase contract

Summary

In SablierFlowBase contract contains an issue where its _update function allows NFT transfers to the zero address (burning), despite documentation explicitly stating this should be prevented. This could lead to accidental loss of stream NFTs.

Vulnerability Details

https://github.com/Cyfrin/2024-10-sablier/blob/963bf61b9d8ffe3eb06cbcf1c53f0ab88dbf0eb0/src/abstracts/SablierFlowBase.sol#L312-L330

function _update(
address to,
uint256 streamId,
address auth
)
internal
override
updateMetadata(streamId)
returns (address)
{
address from = _ownerOf(streamId);
if (from != address(0) && !_streams[streamId].isTransferable) {
revert Errors.SablierFlowBase_NotTransferable(streamId);
}
return super._update(to, streamId, auth);
}

The function's documentation (line 304) states:
https://github.com/Cyfrin/2024-10-sablier/blob/963bf61b9d8ffe3eb06cbcf1c53f0ab88dbf0eb0/src/abstracts/SablierFlowBase.sol#L304

/// @dev The transferable flag is ignored if the current owner is 0, as the update in this case is a mint and
/// is allowed. Transfers to the zero address are not allowed, preventing accidental burns.

but, the implementation does not include any checks to prevent transfers to the zero address.

Impact

This issue allows stream NFTs to be accidentally or intentionally burned, leading to loss of stream access.

function testZeroAddressTransferBug() public {
// Create a stream owned by user1
vm.prank(admin);
uint256 streamId = sablier.createTestStream(user1);
// Try to transfer to zero address
vm.prank(user1);
vm.expectEmit(true, true, true, true);
emit Transfer(user1, address(0), streamId);
address previousOwner = sablier.exposed_update(address(0), streamId, user1);
// Verify the transfer actually happened (this shows the bug)
assertEq(previousOwner, user1, "Previous owner should be user1");
vm.expectRevert(); // Should revert when trying to get owner of burned token
sablier.ownerOf(streamId);
}

Test Results:

[PASS] testZeroAddressTransferBug() (gas: 87305)
Logs:
BUG DETECTED: _update function allows transfer to zero address despite documentation
Severity: Medium
Impact: Stream NFTs can be accidentally burned
Location: SablierFlowBase._update()
Expected: Transfers to zero address should be prevented
Actual: _update() allows transfers to zero address

Tools Used

Foundry Framework
Manual Code Review
Custom Test Suite

Recommendations

Add an explicit check in the _update function to prevent transfers to the zero address:

function _update(
address to,
uint256 streamId,
address auth
)
internal
override
updateMetadata(streamId)
returns (address)
{
address from = _ownerOf(streamId);
// Add check to prevent transfers to zero address
if (to == address(0)) {
revert Errors.SablierFlowBase_InvalidRecipient(to);
}
if (from != address(0) && !_streams[streamId].isTransferable) {
revert Errors.SablierFlowBase_NotTransferable(streamId);
}
return super._update(to, streamId, auth);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.