Sablier

Sablier
DeFiFoundry
53,440 USDC
View results
Submission Details
Severity: low
Invalid

Centralization risks can disrupt streaming services for all parties involved.

Summary

Canceling or renouncing streams are functions triggered only by the sender and depend exclusively on its availability. Although the design and operation of these functions may initially appear to concern only one party (the sender), under a certain condition (described in the Sablier documentation), both the sender and the receiver may be unable to access their payments, whether payouts or refunds. This also can result in the funds being locked or the stream process being disrupted.

Vulnerability Details

As outlined in the access control documentation, the actions of canceling, canceling multiple streams, and renouncing a stream are exclusively within the control of the sender making them completely dependant on the sender's availability. This presents a centralization risk that can potentially 1) disrupt streaming services and/or 2) lock/loss assets, particularly if the sender encounters specific scenarios (described below) that come off from a case that is mentioned as a benefit in the documentation provided by Sablier: (https://docs.sablier.com/concepts/protocol/streaming)

“Lastly, streaming is more secure than lump-sum payments, because it makes it possible to correct errors. Suppose you accidentally started a steam worth 10 ETH to the wrong address. For example, if you accidentally initiated a stream worth 10 ETH to an incorrect address, you can cancel the stream and reclaim the unstreamed Ether (e.g., recovering 9.99 of the 10 ETH). Conversely, recovering a lump-sum payment sent to the wrong address is not possible, unless the recipient is willing to return it”.

Drawing from this particular case (recognized as a benefit in the protocol documentation, thereby acknowledging its potential occurrence), the following vulnerable scenarios may arise:

Scenario 1)

When the user invokes the cancel and cancelMultiple functions, the process of canceling a stream involves stopping the transfer of assets before the scheduled end time and refunding any remaining funds to the sender. Both functions invoke _cancel(uint256 streamId) internal(), which after setting the refunded amount it proceeds to retrieve the ERC20 asset from storage refunding the sender:

// Retrieve the ERC-20 asset from storage.
IERC20 asset = _streams[streamId].asset;
// Interaction: refund the sender.
asset.safeTransfer({ to: sender, value: senderAmount });

Given the protocol's flexibility of enabling the utilization of different standard ERC20 tokens within the stream, it's known that specific tokens, like USDC, integrate blacklisting mechanisms. In such instances, if the sender is blacklisted, the aforementioned cancel function will reverse when attempting to transfer the tokens (a similar case can happen with the receiver facing the blacklist when attempting withdrawals).

Upon initial examination, the utilization of blacklisted tokens involves a decision with the risk of being locked, which lies beyond the protocol's scope. However, it exposes a centralization flaw coming from the main scenario described above (the benefit mentioned in the protocol documentation). The following case is then feasible:

  1. Alice is required to deposit 3,000 USDC/USDT (tokens with the blacklist mechanism) into Sablier for Bob before Jan 1, with the end time set to Feb 1. However, as illustrated in the benefit case, Alice mistakenly designates a different/incorrect recipient address (such as Eve).

  2. Beginning Jan 1, Bob's allocation of the USDC/USDT deposit should increment every second. However, since the stream control is solely reliant on Alice, there is no option for Bob (or Eve in this situation) within the protocol to cancel the stream, even if he/she notices the incorrect address.

  3. Subsequent to the stream creation, Alice is placed on the token blacklist. If Alice notices the incorrect address of the receiever and attempts to cancel the stream and receive a refund, the transaction will fail due to her inclusion on the blacklist.

  4. Despite Eve recognizes the mistake, (even is she is willing to) she cannot cancel the stream of course.

  5. Bob and Alice are unable to access their respective funds, since Alice is blacklisted and Bob never received it.

Scenario 2)

As documentation states renouncing a stream implies that the sender forfeits the ability to cancel it. The conditions to perform a renounce are to have an active, cancelable stream with the sender set to the StreamManagement contract:

// Check: the stream is not cold.
Lockup.Status status = _statusOf(streamId);
if (status == Lockup.Status.DEPLETED) {
revert Errors.SablierV2Lockup_StreamDepleted(streamId);
} else if (status == Lockup.Status.CANCELED) {
revert Errors.SablierV2Lockup_StreamCanceled(streamId);
} else if (status == Lockup.Status.SETTLED) {
revert Errors.SablierV2Lockup_StreamSettled(streamId);
}
// Check: `msg.sender` is the stream's sender.
if (!_isCallerStreamSender(streamId)) {
revert Errors.SablierV2Lockup_Unauthorized(streamId, msg.sender);
}

Given that the sender possesses complete control to renounce a stream while it is active, the following scenario can occur:

  1. Alice initiates a deposit of 3,000 DAI to Bob in Sablier before Jan 1, with the end time set to Feb 1. However, as detailed in the benefit case, Alice inadvertently designates a different/incorrect recipient address (such as Eve).

  2. Beginning Jan 1, Bob's allocation of the DAI deposit should increment every second. However, since the stream control is solely reliant on Alice, there is no option for Bob (or Eve in this scenario) within the protocol to terminate the stream.

  3. Before recognizing its error, Alice renounces the stream making it impossible to cancel, as the sender gave up control of it.

  4. Consequently, when Alice notices the incorrect address, the funds are lost (transferred to the incorrect address), and Bob never receives the intended amount.

Scenario 3)

Lastly, (again as stated in the documentation) it is possible to inadvertently initiate a stream with incorrect payment data. For instance:

  1. Alice is required to initiate a deposit of 3,000 DAI to Bob in Sablier before Jan 1, with the end time set to Feb 1. However, as outlined in the documentation scenario, Alice mistakenly inputs 2,000 DAI instead.

  2. From Jan 1 onwards, Bob's allocation of the DAI deposit increases every second, but with the incorrect amount. Nevertheless, as stream control rests solely with Alice, Bob lacks any means within the protocol to terminate the stream.

  3. If Alice notices her mistake promptly, she cancels the stream and establishes a new one with the remaining funds.

  4. In the event that Alice fails to cancel the stream for any reason, Bob receives the incorrect amount. Depending on the circumstances, setting a new stream after the elapsed time may or may not be feasible, but the time constraints established in the stream creation for this payment do not comply anymore.

Impact

As detailed in the previous scenarios, while the impact is significant, the likelihood is minimized to the edge case outlined in the Sablier documentation, making it low. However, it is very important to highlight that the centralization of the cancel, cancel multiple, and renounce functions, which rely entirely on the sender's availability, can disrupt the streaming process. This can result in loss of funds, funds being locked, or compliance issues in various situations.

Tools Used

Manual review.

Recommendations

Although the primary actions of the stream (cancel and renounce) depend solely on the sender, there are instances where involving the receiver does not compromise the protocol’s functionality and can mitigate specific risks (as previously mentioned). For example, if the sender sets params.cancelable = true during stream creation:

  • Introduce a new option/parameter allowing the receiver to cancel the stream, (which can also be enabled/disabled by the sender at stream creation).

This feature can address scenarios where the sender inputs the correct address but incorrect data, enabling the receiver to cancel the erroneous stream in case the sender is not available/reachable. This offloads the centralization burden from the sender in cases where its availability is compromised or limited. Additionally, it can potentially address situations where an incorrect address is set.

Consequently, in the case of renounce, while the sender can still renounce control of the stream, it is also beneficial for the receiver to have the ability to cancel the stream if required (as described in Scenario 2).

In general, offloading the centralization of cancel and renounce functions from the sender, or at least providing this option, will increase the protocol's flexibility and reduce scenarios where funds are lost or stuck.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Info/Gas/Invalid as per Docs

https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity

mrjorystewartbaxter Submitter
about 1 year ago
mrjorystewartbaxter Submitter
about 1 year ago
0xnevi Judge
about 1 year ago
inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Info/Gas/Invalid as per Docs

https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity

Support

FAQs

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