Sablier

Sablier
DeFiFoundry
53,440 USDC
View results
Submission Details
Severity: medium
Valid

`SablierV2MerkleLockupFactory` is vulnerable to reorg attacks

Summary

SablierV2MerkleLockup is used to create system specific airstreams, which represents an "airdrop", but using sablier lockup streams to unlock the tokens in time. Currently it is possible to for users to create linear and tranche streams.
When a user create such airstream, he interacts with SablierV2MerkleLockupFactory and have to provide important parameters for the corresponding SablierV2MerkleLockup to be created. For example for SablierV2MerkleLL user has to provide durations, but the most imoprtant params are baseParams

struct ConstructorParams {
IERC20 asset;
bool cancelable;
uint40 expiration;
address initialAdmin;
string ipfsCID;
bytes32 merkleRoot;
string name;
bool transferable;
}

Depending on the merkleRoot , users can provide proofs to initiate corresponding token distributions.
Currently there is no other way for the creator to fund the SablierV2MerkleLockup, but to directly tranffer the corresponding ERC20 amounts to the contract. This is mandatory, because when user try to claim a stream, SablierV2MerkleLockup would be the msg.sender for SablierV2Lockup::_create function call, which transfer funds for the corresponding stream from msg.sender

Vulnerability Details

In the README it is written that the contracts should be supported by any EVM compatible chain, which means Ethereum Mainnet and Polygon. Those and especially polygon are chains, where reorgs are happening frequenty and this may be big problem here, because if an expoiter sees that a user has created a SablierV2MerkleLockup and in the follownig block he tries to fund it, but a reorganization is happening for exactly those blocks, he can take advantage and front-run createMerkleLT function with his merkleRoot and admin address. By doing so, he will steal victim's funds, because the address of the deployed contract would be the same one, before the reorg. The one, which victim used to send his ERC20 tokens.

This is a problem, because SablierV2MerkleLockupFactory deploys contracts using create and so the address of the deployed contracts is only dependant on SablierV2MerkleLockupFactory nonce

Recommendation

  • Deploy the contracts via create2 with salt that includes msg.sender address.

Or

  • Make user transfer ERC20 asset funds in the same transaction when SablierV2MerkleLockup is created:

+ function createMerkleLT(..., uint256 fundAmount)
external
returns (ISablierV2MerkleLT merkleLT)
{
...
merkleLT = new SablierV2MerkleLT(baseParams, lockupTranched, tranchesWithPercentages);
+baseParams.asset.safeTransferFrom(msg.sender, merkleLT, fundAmount)

Impact

Funds theft

Tools Used

Manual Review

Recommendations

  • Deploy the contracts via create2 with salt that includes msg.sender address.

Or

  • Make user transfer ERC20 asset funds in the same transaction when SablierV2MerkleLockup is created:

+ function createMerkleLT(..., uint256 fundAmount)
external
returns (ISablierV2MerkleLT merkleLT)
{
...
merkleLT = new SablierV2MerkleLT(baseParams, lockupTranched, tranchesWithPercentages);
+baseParams.asset.safeTransferFrom(msg.sender, merkleLT, fundAmount)
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

CREATE is vulnerable to ChainReorgs

Support

FAQs

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