👓Real World Examples
Last updated
Last updated
Understanding the Examples: In this section, we dive into practical instances of read-only reentrancy vulnerabilities discovered in actual bug bounty reports, Code Arena analyses, and more. By examining these real-life scenarios, you'll attain a richer, more tangible comprehension of read-only reentrancy attacks and their potential repercussions. This understanding will serve to bolster your smart contract auditing capabilities in a substantive and effective manner.
Background Summary
Curve, an automated market maker (AMM), allows users to trade assets via liquidity pools. These pools are backed by liquidity providers (LPs) who collect trading fees. Similar to other DeFi projects, Curve issues a LP token, which represents ownership in the liquidity pool. When these LP tokens are redeemed, holders receive a proportionate share of the underlying assets.
A distinctive feature of Curve is its ability to create AMMs for assets with similar values (e.g., USDC/DAI/USDT or ETH/stETH) through its stable swap pools. This innovation minimizes slippage, allowing for efficient trades between these "pegged" assets.
Smart contracts that interact with Curve as LPs often hold these LP tokens. The value of these LP tokens is determined by dividing the total underlying assets by the total LP token supply. In the case of Curve's stable swap, the underlying assets are pegged to each other, allowing the use of the stable swap invariant's D value to estimate the total value of the underlying tokens.
This ratio between D and the total supply is implemented in the get_virtual_price function, serving as a reliable oracle for LPs. The function is used to estimate fee growth and LP token value. An important aspect is that this ratio, under the fee mechanism, is designed to only grow over time.
However, the read-only reentrancy vulnerability allows for the temporary creation of inconsistencies between D and the total LP token supply, breaking this growth assumption. This inconsistency has led to the manipulation of price feeds of various protocols such as MakerDAO, Enzyme, Abracadabra, TribeDAO, and Opyn.
The Concept of Read-Only Reentrancy
Traditionally, reentrancy exploits occur when a state-modifying function is called repeatedly before the first invocation of the function has finished. This can lead to an inconsistent state, which is then used to carry out malicious modifications to the contract's storage. Most contracts implement reentrancy guards to shield their state from such potentially harmful actions.
However, read-only reentrancy represents a different scenario. It occurs when a 'view' function, which typically doesn't modify the contract's state, is reentered. Since these functions don't alter the contract's state, they are often left unprotected. The potential danger arises when an inconsistent state is reported due to this reentrancy, causing incorrect values to be returned.
Protocols that depend on these return values could be misled into reading the inaccurate state and consequently performing unintended actions. This makes read-only reentrancy a unique vulnerability that, despite not directly modifying the contract's state, can indirectly cause harmful consequences.
Analyzing the Vulnerability
The exploit begins within the Curve stable swap pools' remove_liquidity function, despite its preparation to combat reentrancy attacks. This function incorporates native Vyper reentrancy guards designed to secure every state-changing operation.
Initially, the function retrieves the underlying balances and the LP token contract's total supply.
The balances here represent the current balances retained after deducting the administrative fees.
Subsequently, the tokens are eradicated, resulting in a decrease in supply.
The standard burning function, 'burnFrom', in the LP token contract diminishes the user's balance and the total supply relative to the amount of LP tokens the user has incinerated. Finally, the underlying tokens are repatriated to the user.
The burned LP tokens' share per underlying is calculated, compared against slippage, and sent out. The function then interacts with non-Curve contracts, threatening control over the execution process. If an underlying token is ETH, native ETH is sent out, triggering the recipient's fallback function if the recipient is a contract.
During the fallback's execution, not all tokens have been dispatched (balances aren't fully updated), but the LP token's total supply has already diminished. An attacker can hijack the execution flow during this state of inconsistency between pool balances and total supply.
This vulnerability makes the get_virtual_price function susceptible to attacks.
This function lacks a reentrancy guard and can be invoked at any time. When activated, it first calculates D based on the current balances held by the contract and the stable swap invariant. It then queries the LP token's total supply and calculates the ratio of D and total supply to provide the LP to pegged asset exchange rate. Since balances and total supply are inconsistent, D and the total supply will also be inconsistent.
Certain protocols that integrated with the get_virtual_price function blindly trusted its return value. Typically, this value was utilized to estimate a lower boundary for the LP’s value by multiplying it with the lowest exchange rate of the underlying tokens.
With a clear understanding of the manipulation, we can now devise an attacking contract. The overall procedure of the attack is:
Deposit substantial amounts of liquidity.
Remove liquidity.
Execute malicious activities during the callback.
Profit.
The following smart contract implementation outlines the process:
The consequences varied depending on each project's setup and the liquidity secured in the pool contract. For instance, the ETH/stETH pool's LP token price was realistically manipulatable by a factor of two, while others were arbitrarily manipulatable. Yet, even the factor of two was enough to place over a hundred million dollars at risk.
In the case of EraLend, the infiltrator exploited this vulnerability through a callback during the LP token burning process.
Within the context of SyncSwap's LP tokens, it is possible to initiate a burn, then proceed with a callback prior to the activation of the 'update_reserves' function. This sequence of events causes the oracle to utilize an inaccurate reserve value for price computation, leading to an inflated oracle price.
Exploiter address: 0xf1D076c9Be4533086f967e14EE6aFf204D5ECE7a
Example attack txs: 0x7ac4da1e…, 0x99efebac…
The breach leveraged funds secured through a flash loan to deposit into Curve’s wstETH/ETH pool, subsequently lodging the LP tokens into dForce’s wstETHCRV-gauge vault.
When the 'remove_liquidity' function was invoked, the assailant's contract capitalized on the reentrancy vulnerability, allowing them to manipulate the virtual price. dForce relies on this altered virtual price as the oracle for the valuation of wstETHCRV-gauge tokens
Attack tx OP: 0x6c197621…
Attack tx ARBI: 0x5db5c240…