Flow

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

Paused stream handling lead to unauthorized operations or token streaming under incorrect conditions in `SablierFlowBase.sol`

Summary

A vulnerability in paused stream handling within the SablierFlowBase contract allows actions on streams that should be voided or paused. This flaw could lead to unauthorized operations or token streaming under incorrect conditions.

Vulnerability Details

In SablierFlowBase, streams marked as “paused” may not account for isVoided status, leading to possible unauthorized access to stream functions. Specifically, the notPaused modifier checks only ratePerSecond and skips isVoided, leaving voided streams at risk.

modifier notPaused(uint256 streamId) {
if (_streams[streamId].ratePerSecond.unwrap() == 0) {
revert Errors.SablierFlow_StreamPaused(streamId);
}
_;
}

This code solely evaluates ratePerSecond to determine if a stream is paused, disregarding isVoided.

Proof of Concept (PoC)

  1. Deploy the contract with two streams: one active, one voided.

  2. Call a function requiring notPaused, targeting the voided stream.

  3. Observe that notPaused bypasses isVoided, executing on a voided stream.

Below is a sample test demonstrating the vulnerability:

const { expect } = require("chai");
describe("Paused Stream Vulnerability", function () {
it("Should bypass notPaused check on voided stream", async function () {
const [owner, user] = await ethers.getSigners();
const StreamContract = await ethers.getContractFactory("SablierFlowBase");
const stream = await StreamContract.deploy(/*initialAdmin, initialNFTDescriptor*/);
await stream.createStream({ isVoided: true, ratePerSecond: 0 }); // Voided stream with paused rate
await expect(stream.functionRequiringNotPaused(streamId)).to.not.be.revertedWith(
"SablierFlow_StreamPaused"
);
});
});

The test shows successful execution of functions on a voided stream, demonstrating the vulnerability.

Impact

This vulnerability enables unauthorized actions on voided streams, potentially leading to state inconsistencies and incorrect fund flows.

Tools Used

Manual review.

Recommendations

Update the notPaused modifier as follows:

modifier notPaused(uint256 streamId) {
if (_streams[streamId].ratePerSecond.unwrap() == 0 || _streams[streamId].isVoided) {
revert Errors.SablierFlow_StreamPaused(streamId);
}
_;
}
Updates

Lead Judging Commences

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