This issue arises when a campaign creator chooses to deploy the campaign first and fund it later. If a malicious recipient funds the Merkle Lockup contract immediately after deployment and triggers the claim()
function, the grace period starts prematurely by setting the _firstClaimTime
to the current block.timestamp
. Consequently, the creator risks missing the window to call the clawback
function. In the scenario where the creator funds the campaign after the grace period ends and then realizes there's a malicious value in the merkle tree, they won't be able to call the clawback
function, leaving the funds exposed to risk if a malicious Merkle tree is detected.
After the creator deploys a campaign, a grace period begins after the first claim. This is done by setting _firstClaimTime
to uint40(block.timestamp)
Link to code.
During the grace period, which lasts for 7 days, the creator can withdraw the funds by calling clawback()
in case of a malicious Merkle tree:
In case of a malicious Merkle tree, clawback can be called to withdraw funds from the deployed MerkleLockup contracts until the grace period ends
Also according to the protocols documentations creators are able to deploy the campaign and fund it later:
Additionally, you don't have to immediately fund the Airstream contract. You can just create the contract and at a later date fund it with the airdropped tokens.
The problem arises when a creator deploys a campaign and plans to fund it later. A malicious recipient can exploit this by funding the Merkle lockup contract right after deployment and calling claim()
, which will prematurely start the grace period by setting _firstClaimTime
to the current block.timestamp
. This premature initiation of the grace period disrupts the intended sequence.
If the creator funds the campaign after the grace period ends and realizes there's a malicious value in the Merkle tree, they won't be able to call the clawback
function, leaving the funds exposed to risk if a malicious Merkle tree is detected.
Alice (the creator) deploys a campaign but plans to fund it later.
Bob (a malicious recipient) funds the campaign right after deployment and calls claim()
with his actual parameters (index, recipient, amount, and merkleProof).
Bob's claim will start the grace period.
A few days later (let's say 8 days), Alice funds the campaign but then realizes there's a malicious value in the Merkle tree, so she attempts to withdraw the funds by calling clawback()
. However, her transaction reverts because the grace period has ended.
To test the scenario please make a file named Ninja.t.sol
in this path: /v2-periphery/test/integration/merkle-lockup/
and paste the following test code in it:
Run the test:
The premature initiation of the grace period allows malicious users to exploit the system, leaving creators unable to withdraw funds during the intended window. Consequently, if a malicious Merkle tree is detected after the grace period, the creator loses the ability to claw back funds, leaving them exposed to potential losses.
VSCode
Foundry
One possible solution is to define a funding function to fund the campaign and a modifier to check whether the campaign is funded by the admin or not. In this case, if anyone other than the admin directly funds the campaign, they would simply lose their money because isFunded
would not be true.
SablierV2MerkleLockup.sol
SablierV2MerkleLT.sol
https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity
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.