Flow

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

Stream Status Validation Vulnerability in the Sablier Flow's _withdraw function.

Summary

A critical vulnerability has been identified in the Sablier Flow's _withdraw function where insufficient stream status validation allows unauthorized withdrawals and potential fund manipulation.

Vulnerability Details

The function _withdaw lacks explicit checks for stream existence, status, and timing constraints, potentially leading to unauthorized access and fund losses.
https://github.com/Cyfrin/2024-10-sablier/blob/main/src/SablierFlow.sol#L772-#L880.

function _withdraw(
uint256 streamId,
address to,
uint128 amount
) internal returns (uint128 withdrawnAmount, uint128 protocolFeeAmount) {
// Missing stream existence validation
// Missing stream status validation
// Missing stream timing validation
// Check: `msg.sender` is neither the stream's recipient nor an approved third party, the withdrawal address
// must be the recipient.
if (to != _ownerOf(streamId) && !_isCallerStreamRecipientOrApproved(streamId)) {
revert Errors.SablierFlow_WithdrawalAddressNotRecipient({ streamId: streamId, caller: msg.sender, to: to });
}
// ... rest of the function
}

The vulnerable state management

// Current vulnerable implementation
uint128 balance = _streams[streamId].balance;
uint128 withdrawableAmount;
if (balance < totalDebt) {
withdrawableAmount = balance; // No status validation before access
}

Potential Exploit

// Scenario: Expired Stream Withdrawal
Stream {
balance: 1000 tokens,
status: EXPIRED,
// Can still withdraw due to missing validation
}
Potential Loss: 1000 tokens

Impact

Direct fund loss, expired stream withdrawals, non-existent stream attacks and state manipulation.

Tools Used

Manual code review

Recommendations

Implement stream existence validation, add status checks and include timing validation.

// These critical checks are absent, implement them:
+ if (!_exists(streamId)) { ... }
+ if (stream.status != StreamStatus.ACTIVE) { ... }
+ if (block.timestamp < stream.startTime || block.timestamp > stream.endTime) { ... }
Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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