Skip to content

Latest commit

 

History

History
57 lines (38 loc) · 3.23 KB

File metadata and controls

57 lines (38 loc) · 3.23 KB

Perfect Currant Cow

High

Infinite offchain distribution claim bug

Summary

As said here in Readme

DISTRIBUTION_OPERATOR_ROLE is required to call the distributeUsualToBuckets every 24 hours to calculate the new emissions and distribute them to the on-chain buckets and increase the off-chain buckets mint cap that can be claimed after successful approval of the off-chain distribution that is unchallenged and in the queue for more than USUAL_DISTRIBUTION_CHALLENGE_PERIOD.

The likelihood of a user being eligible for multiple distributions is high, given that distributions occur at 24-hour intervals. If this happens, a malicious user could potentially drain all funds allocated to the offchain distribution.

Root Cause

In DistributionModule.sol#L386, when user claims offchain distribution, its updates the claimedByOffChainClaimer[user] to track internally claimed amount. Each time user claims, the claimedByOffChainClaimer[user] is deducted from the claim amount, to avoid double claims.

https://github.com/sherlock-audit/2024-10-usual-labs-v1/blob/4fb4a64a479e0b9b8f93934220e891c29d54df33/pegasus/packages/solidity/src/distribution/DistributionModule.sol#L407-L420

There is a high likelihood that the user could be eligible for multiple distributions, where each distribution with different merkleRoot has different claimable amount for the user. The attack goes like;

  1. Operator calls distributeUsualToBuckets() at t=0, then queues it until the challenge period(i.e. 7 days) ends. Distribution goes unchallenged, and approved on day 7th.
  2. Bob as claimant claims 50 unit of usual tokens via claimOffChainDistribution(), and claimedByOffChainClaimer[bob] changes to 50
  3. Operator launch another distribution, which get approved also. And this time, the bob eligible for higher amount, say 150
  4. Bobs claims 150 unit, but got 150 - claimedByOffChainClaimer[bob](i.e. 100 unit) in his account and claimedByOffchainClaimer[bob] is set back 100. Note that the 2nd distribution still allow Bob to go for another 50 unit claim since proof was generated for 150 unit tokens amount.
  5. On 3rd claim, the claimedByOffChainClaimer[bob] is set back to 50, instead of 150, causing claimedByOffChainClaimer[bob] to be transitioning b/w 50 and 100 always, alternatively, allowing bob to claim either 100 unit or 50 unit every single time. This will drained the DistributionModule funds allocated to offchain distribution.

Internal pre-conditions

  1. Its required claimers to be eligible in more than one distribution with different claimable amount.

Attack Path

See RC.

Impact

Infinite minting of usual tokens upto offChainDistributionMintCap

Mitigation

Modify to below,

    function claimOffChainDistribution(address account, uint256 amount, bytes32[] calldata proof)
        external
        whenNotPaused
        nonReentrant
    {
        ...snip...
-       $.claimedByOffChainClaimer[account] = amount;
+       $.claimedByOffChainClaimer[account] += amount;

        emit OffChainDistributionClaimed(account, amountToSend);
        $.usual.mint(account, amountToSend);
    }