The vulnerability arises when a malicious recipient creates a second stream with the same sender-recipient pair and a higher rate per second (rps) shortly after the original stream. If a blockchain reorganization (reorg) occurs, causing the original stream’s block to be dropped, the second stream inherits the original stream’s identifier. This enables the recipient to receive deposits intended for the original stream at the higher rps they set, resulting in faster, unintended access to funds.
The second part involves changing the stream token, following the same attack path as above, which allows them to steal a more valuable token from depositor
Looking at the deposit
function:
The check: _verifyStreamSenderRecipient
is meant to protect the deposit function from reorg attacks.
The sender and recipient of a stream can be freely set by the creator. This means that a malicious recipient could create a new stream immediately after the one created by the original sender, setting the same sender and recipient but with a different rate per second (rps). If a reorganization (reorg) occurs, the recipient could then receive the deposit at a rate they defined.
For example,
token to be distributed is usdc
At block 10, Alice creates a stream with an rps (rate per second) of 0.1 (meaning 0.1 usdc are streamed per second) and sets Bob as the recipient. Bob is then assigned the streamId, say, 5.
At block 11, Bob creates another stream, sets Alice as the sender, an rps of 1_000_000 (1_000_000 tokens streamed per second), and himself as the recipient. He receives the streamId of 6.
At block 12, Alice (or any other funder) calls the deposit function with arguments: streamId=5, amount=259_200, sender=Alice, and recipient=Bob.
A chain reorganization occurs, and block 10 is dropped.
Consequently, the streamId of 6 (created by Bob) now becomes 5. The deposit function checks _verifyStreamSenderRecipient, which passes since Bob set the sender and recipient of streamId 6 to match those of the original streamId 5.
As a result, the 259_200 usdc deposit, which was intended to be streamed to Bob over a month, becomes available to him all at once.
Say original sender creates streamId n,
Note that recipient only needs to create streamId n+1 with the same sender and recipient as streamId n, but different rps
If a reorg happens at a block <= block at which n was created, deposits made to streamId n will be streamed to recipient at his defined rps
Following the same attack path as above, recipient can also change the token to a more valuable one than what was specified than the original sender
It is very likely that a depositor would have deposited different erc20 tokens at different points in time, and it is not uncommon for the depositor to max approve, or at least, approve SablierFlow to spend a larger amount than what he actually wants to deposit.
Now consider the following scenario:
Alice creates a stream to stream usdc to Bob(StreamId=5)
Bob creates another stream with same sender and recipient, and changes the token to ETH(StreamId=6)
User who has max approved USDC and ETH to SablierFlow in the past deposits 10(USDC) to streamId 5
Reorg happens
User's deposit goes to Stream created by Bob, which specified ETH as the token
10 ETH gets transferred from User to SablierFlow.
Attacker can change both the RPS and token to steal the more valuable token from the depositor, and have it streamed to him at once
Note also that anyone, not just the recipient can perform this attack.
In the event of a reorg, this allows a malicious recipient to
manipulate the stream rate to receive deposited funds more quickly than intended
change the stream token to a more valuable one to steal from depositor
Combining these, a depositor who intends to stream 100 USDC over a month can be manipulated by the recipient to steal 100 ETH from him in an instant.
Manual Review
Don't allow the creator of a stream to specify the sender.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.