Flow

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

Malicious user can honeypot other users to buy their Stream on an NFT marketplace.

Summary

Malicious user can create a transferable Stream with a trusted sender and a positif `RPS` since the protocol allows the stream creater to controle these parameter.

see the `_create()` function:

https://github.com/Cyfrin/2024-10-sablier/blob/8a2eac7a916080f2022527408b004578b21c51d0/src/SablierFlow.sol#L564C1-L621C6

Vulnerability Details

In the majority of NFT marketplaces such as Opensea and Blur, users can list their NFT by approving it to the marketplace contract.

Once the NFT finds a buyer, the marketplace contract transfers the NFT from the owner to the buyer who pays to receive it.

Here is a realistic scenario where a malicious user can honeypot other users to steal their funds:

note: that this scenario can be crafted in a more malicious way but for the sake of simplicity, we'll keep it this way.

1.Malicious user Creates a Stream with the following values:

  • balance = 1,000 USDC.

  • isTransferable = true.

  • ratePerSecond = 1 USDC.

  • sender = a company that is trusted and uses the protocol.

  • token = USDC.

and set his wallet address as the recipient.

2.Waits till the `snapshotDebtScaled` became 2,000 USDC and list his NFT on Opensea for the equivalent of 1,000 USDC.

3.Victim sees the sender is trusted and the stream is still streaming, and think that he canmake more than 500 USDC.
4.Attacker was monitoring the mempool and frontruns the user's transaction to withdraw all the debt.

5.The marketplace executes the trade, sends the 1,000 USDC equivalent to the attacker and sends the stream that contains only debt to the victim.

Impact

Victims think they are buying a worth Stream with a trusted sender and end up being scamed.

this leads to a loss of funds to the victim, and loss of trust in Senders and the Sablier protocol.

Tools Used

Manual Review.

Recommendations

don't let anyone controle the sender parameter.

modify `create()` and `createAndDeposit()` as follow:

function create(
address recipient,
UD21x18 ratePerSecond,
IERC20 token,
bool transferable
)
external
override
noDelegateCall
returns (uint256 streamId)
{
// Checks, Effects, and Interactions: create the stream.
streamId = _create(msg.sender, recipient, ratePerSecond, token, transferable);
}
function createAndDeposit(
address recipient,
UD21x18 ratePerSecond,
IERC20 token,
bool transferable,
uint128 amount
)
external
override
noDelegateCall
returns (uint256 streamId)
{
// Checks, Effects, and Interactions: create the stream.
streamId = _create(msg.sender, recipient, ratePerSecond, token, transferable);
// Checks, Effects, and Interactions: deposit on stream.
_deposit(streamId, amount);
}
Updates

Lead Judging Commences

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

Appeal created

benterkiii Submitter
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

Support

FAQs

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