QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: medium
Valid

attacker can completely prevent users from withdrawing in `UpliftOnlyExample`

Summary

Due to the first in last out nature of the poolsFeeData, attacker can prevent users from withdrawing liquidity permanently by transferring to them millions of dust NFT positions over filling the feeData[] and causing OOG errors during withdrawals

Vulnerability Details

during withdrawals in UpliftOnlyExample::onAfterRemoveLiquidity() we start looping from the last deposit (localData.feeDataArrayLength - 1)

File: UpliftOnlyExample.sol
471: for (uint256 i = localData.feeDataArrayLength - 1; i >= 0; --i) {
493: if (feeDataArray[i].amount <= localData.amountLeft) {
494: uint256 depositAmount = feeDataArray[i].amount;
495: localData.feeAmount += (depositAmount * feePerLP);
501: delete feeDataArray[i];
502: feeDataArray.pop();
507: } else {
508: feeDataArray[i].amount -= localData.amountLeft;
509: localData.feeAmount += (feePerLP * localData.amountLeft);
510: break;
511: }
512: }

This opens up an attack on large valuable positions by malicious BOTs (especially in L2s where gas price is very cheap), here what would happen:

1- Attacker sees very large valuable deposit position
2- Attacker deposit dust value positions and transfer the NFT to the victim (taking advantage of afterUpdate hook in NFT transfer, pushing the position to the end of feeDataArray of victim)
3- Attack is repeated millions of times, causing any attempt to withdraw the actual first position of the user to cause OOG reverts
4- The victim can't transfer his valuable LP NFT position to other wallet as a solution, since transfer will try to re-order the array of the sender that will cause OOG reverts too

File: UpliftOnlyExample.sol
576: function afterUpdate(address _from, address _to, uint256 _tokenID) public {
590: FeeData[] storage feeDataArray = poolsFeeData[poolAddress][_from];
616: if (tokenIdIndex != feeDataArrayLength - 1) {
617: //Reordering the entire array could be expensive but it is the only way to keep true FILO
618: for (uint i = tokenIdIndex + 1; i < feeDataArrayLength; i++) {
619: delete feeDataArray[i - 1];
620: feeDataArray[i - 1] = feeDataArray[i];
621: }
622: }

5- the victim will try to remove those dust positions batch by batch to reach his first position, but the malicious bot of the attacker will track the victim poolsFeeData after each block (no need to front run, valid attack path on L2s), adding more position to the victim if needed

Attack is feasible since its on L2s and using dust positions to DOS (very small loss overall to attacker to keep the attack running)

This was possible due to:

  1. when transferring we don't check the array size of to if it has passed 100 or not

  2. the FILO nature when withdrawing, and re-ordering when transferring

  3. any one can enlarge the size of other people by transferring to them the NFT, and array will grow in afterUpdate hook

  4. no minimum LP deposits to prevent dusts deposits

Impact

Freeze of funds to the victim

Tools Used

Manual Review

Recommendations

  • implement minimum LP deposits

  • check for length of feeDataArray of the to during transfers and prevent it it will exceed

  • give a way to withdraw specific position index (if FILO implementation is not very important)

  • give the ability to guard against who can transfer his NFT position to me (set by user him self)

Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_afterUpdate_does_not_check_limit_NFT_per_user

Likelihood: Medium/High, anyone can receive an unlimited NFT number but will cost creation of LP tokens and sending them. Impact: Low/Medium, DoS the afterUpdate and addLiquidityProportional but will be mitigable on-chain because a lot of those NFT can be burn easily in onAfterRemoveLiquidity.

Appeal created

0xhuntoor Submitter
10 months ago
honour Auditor
10 months ago
0xhuntoor Submitter
10 months ago
n0kto Lead Judge
9 months ago
n0kto Lead Judge 9 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_afterUpdate_does_not_check_limit_NFT_per_user

Likelihood: Medium/High, anyone can receive an unlimited NFT number but will cost creation of LP tokens and sending them. Impact: Low/Medium, DoS the afterUpdate and addLiquidityProportional but will be mitigable on-chain because a lot of those NFT can be burn easily in onAfterRemoveLiquidity.

Support

FAQs

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