Open code423n4 opened 1 year ago
JustDravee marked the issue as duplicate of #122
Due to the high quality of the report and the details given (particularly, the real impact on the user's funds), I'll be selecting this for the report. While High Severity can be defended here, I believe this to be more of a Griefing attack (Medium Severity, no profit motive for an attacker, but damage done to the users or the protocol) The user's assets in the protocol aren't at direct risk, even if they need to pay more Gas (on Optimism). On Immunefi, "Theft of gas" or "Unbounded gas consumption" are considered Medium Severity issues, and some projects even put "Loss of gas costs or funds will not be considered ‘loss of funds’" in their OOS section (like Olympus). Hence the Medium Severity.
JustDravee marked the issue as selected for report
JustDravee changed the severity to 2 (Med Risk)
Lines of code
https://github.com/code-423n4/2023-03-polynomial/blob/aeecafc8aaceab1ebeb94117459946032ccdff1e/src/KangarooVault.sol#L185-L334 https://github.com/code-423n4/2023-03-polynomial/blob/aeecafc8aaceab1ebeb94117459946032ccdff1e/src/LiquidityPool.sol#L181-L335
Vulnerability details
Impact
Direct loss of money for users who have deposited funds and wish to withdraw them, as they would be required to pay extremely high gas fees due to the queuing mechanism exploited by the attacker.
Proof of Concept
The affected contracts, KangarooVault and LiquidityPool, provide the functionality for instant deposits and withdrawals of tokens. However, in certain scenarios, deposits and withdrawals can be queued.
In both cases, the deposit transfers the
sUSD
from your address immediately. In the instant deposit scenario,mints you Vault or Liquidity token - depending on the contract. On the other hand, queuing it adds you to the deposit queue, by creating a "QueuedDeposit" object and sets its id to the current value ofnextQueuedDepositId
(mentioning that, because its going to be important later)In KangarooVault, the deposits are queued ONLY If there are currently active positions.
On the other hand, in LiquidityPool instant deposits have a fee whereas the regular deposits don't have a fee. After discussing with the protocol team, queue deposits its primarily for the regular traders, because the price won't fluctate that much and instant is mostly for other protocols to build on top.
Finally, the deposits are processed in a nearly identical way (LP, Kangaroo). Below, I've only extracted the vulnerable code which contains a for loop that iterates through a specified number of deposits, denoted by the variable
count
. The main purpose of this loop is to process each deposit in the queue and update the overall state of the contract.At the beginning of each iteration, the function accesses the deposit at the current
queuedDepositHead
position in thedepositQueue
. The deposit'srequestedTime
is then evaluated to ensure that it is not equal to 0 and that the current block timestamp exceeds the deposit'srequestedTime
plus theminDepositDelay
. If either of these conditions is true, the function terminates early, halting further deposit processing.Upon passing the aforementioned check, it mints the tokens for the user and updates the contract accounting balance. Finally, the
queuedDepositHead
is incremented, advancing to the next deposit in the queue.We can conclude that:
queuedDepositHead
= the next in line deposit to be executednextQueuedDepositId
= currently, the last deposit id in the queueAs a result, this means all the deposits are executed in sequential order and if there are currently 10 deposits and yours is the 11th, if you want to process your own, you have to process the 10 before that or wait for somebody else to process them.
The queuing mechanism can be exploited by a malicious actor, who can queue a large number of deposits or withdrawals for a very low cost. This essentially locks all the deposited funds in the contract and forces the users to pay extremely high gas fees to process their transactions. This vulnerability can be exploited in a similar way for both contracts.
This can be mitigated to extent by the
minDepositAmount
variable, but that will just make the attack vector a bit more expensive for the attacker and the vulnerability would still be there.To apply that to real world, assume the following scenario:
Since there's no way to force Alice to process her deposits, Bob can either wait for somebody else to process them or process them himself. However, whoever does that will have to pay enormous amount of gas fees.
Here's a PoC using Foundry, making use of LiquidityPoolTest test suite. However, it would work pretty much in the same way for KangarooVault, where the only prerequisite would be that there have to be currently active positions.
Running the test using the following command
forge t --match-test testDepositFee -vv --gas-report
yiels the following result.The same scenario can happen when processing withdrawals. If we expand the example to extreme values (100,000,000 deposits), this would mean that the approximate gas to be paid for users that want to withdraw their funds equal approximately 2448749420000, which converted to today's ETH:USD ratio is around 4070335.77 USD.
Tools Used
Manual review, foundry.
Recommended Mitigation Steps
My recommendations for this vulnerability are the following: