🥶Minting and Burning Liquidity Pool Tokens
Code4rena High Bug Bounty Payout
Vulnerability Details
Impact:
The Pool contract permits users to burn liquidity provider (LP) tokens without concurrently withdrawing the corresponding tokens. This ability creates an opportunity for malicious actors to manipulate the pool's rate adversely, rendering it impossible for users to receive any LP tokens when depositing tokens into the pool. Such manipulation primarily hinges on the calculation of liquidity tokens at Utils:calcLiquidityUnits
, where a minimal total supply (e.g., 1) leads to all computed units rounding down to zero.
Proof of Concept:
Below is an example script where a user attempts to deposit 1M tokens into a pool with a total supply of just 1:
This script demonstrates that a pool with a total supply of 1 yields zero LP tokens for a deposit of 1M tokens, indicating a significant flaw in the system.
How the Vulnerability Works:
Given that any user can initiate a Pool via the PoolFactory, a malicious actor can create a pool, burn their LP tokens, and set the total supply to 1. Consequently, this actor becomes the sole owner of the pool's LP tokens henceforth.
Recommended Mitigation Steps:
Consider eliminating the burn functionality or limit its access to only privileged users to prevent arbitrary burning of LP tokens.
Implement safeguards within the smart contract to prevent the total supply from dropping to levels that would cause the rounding issue, like establishing a minimum total supply limit.
Review and revise the liquidity calculation logic to ensure that it handles edge cases gracefully, without leaving room for exploitation. Ensure thorough testing and auditing of the revised logic to confirm its robustness and security.
Vulnerability Details
Impact:
A malicious actor can monitor the SetPricePerShare
event, exploiting it if the price data becomes stale due to a lack of updates, thus manipulating the mint()
function to use an outdated pricePerShare
that differs from the current market price. This user can then wait for the price to be updated, subsequently using the burn()
function with the newly updated pricePerShare
. This process allows the actor to make a risk-free profit at the expense of the contract's holdings.
Proof of Concept:
Price Updating Mechanism: The
pricePerShare
variable inWrappedIbbtcEth
is updated by the externally invokedupdatePricePerShare
function. However, the variable is utilized inmint
,burn
, andtransfer
functions without any check for its freshness, making it susceptible to being outdated or stalled. This stale data issue can occur if theupdatePricePerShare
function fails to run due to various reasons, including bugs, system-level dependencies, or network outages. (See: balanceToShares function and updatePricePerShare function)
Recommended Mitigation Steps:
Introduce a Threshold Variable: Implement a threshold variable in the
WrappedIbbtcEth
contract that defines the maximum allowable time since the lastpricePerShare
update.Develop Two Transfer Variants: Create two variants of
transferFrom
andtransfer
functions. Both should check if the current time minus the time of the last price update is less than the defined threshold:If the condition is met, both function variants execute the transfer.
If the condition is not met, the first variant should revert, while the second one triggers a price update (even though it might be more gas-intensive).
Limit Exposure: With the threshold in place, if a scheduled price update fails (due to network issues or other reasons), the associated risk is confined to the market volatility during the threshold time, effectively capping the exposure.
Last updated