Skip to content
On this page

Puppet V2: When Even a New String Can't Hide the Old Vulnerability

The developers of the "Puppet" challenge series seemingly learned a valuable lesson from their initial foray into decentralized finance. With Puppet V2, they've upgraded their lending pool, opting for a more sophisticated price oracle: the venerable Uniswap V2 exchange, bolstered by its recommended utility libraries. "Surely," one might think, "this robust setup is enough to prevent any cunning attacks?"

Think again. In the intricate world of DeFi, even seemingly standard and well-understood components can harbor an Achilles' heel if misused. Puppet V2 serves as a stark, brilliant reminder that even with "modernized" tools, understanding the nuances of how they're deployed is paramount.

The Setup: A Lending Pool with a Twist

Our scene is a lending pool designed to allow users to borrow DVT tokens by depositing WETH as collateral. The catch? The collateral required is three times the current market value of the borrowed DVT, as determined by an oracle. This oracle, the _getOracleQuote function within our PuppetV2Pool.sol, is the crux of the challenge.

Let's break down the starting conditions:

  • You, the attacker (player), begin with a respectable 20 ETH and a sizable 10,000 DVT tokens.
  • The Uniswap V2 liquidity pool holds 10 WETH and 100 DVT, setting an initial price of 1 DVT = 0.1 WETH.
  • The Puppet V2 lending pool itself is a tempting target, safeguarding a cool 1,000,000 DVT tokens.

Your mission, should you choose to accept it, is to "save" all those funds by depositing them into a designated recovery account. In other words, drain the pool.

The Illusion of Security: Uniswap V2 as a Spot Price Oracle

The core of Puppet V2's "enhanced" security lies in its reliance on UniswapV2Library.quote. This library function is designed to fetch the current, instantaneous spot price of a token pair by querying the reserves held within the Uniswap V2 pair contract. On the surface, it seems logical: "If I want to know the price, I ask the market."

However, this is precisely where the vulnerability lies. Uniswap V2's quote function provides a spot price. This means it reflects the price at that exact moment, based on the current reserve balances. Crucially, it does not incorporate any form of time-weighted average price (TWAP), which would smooth out sudden fluctuations.

This characteristic makes it highly susceptible to manipulation by large, single-transaction trades, especially if the liquidity in the oracle pool (the Uniswap pair) is not deep enough to absorb such trades without significant price impact.

Pulling the Strings: The Attack Unveiled

The exploit leverages this oracle flaw in a classic "price manipulation" attack pattern:

  1. Preparation: The attacker wraps their initial 20 ETH into WETH and transfers their 10,000 DVT to the attacking contract.

  2. Oracle Manipulation: This is the critical step. The attacker uses their substantial 10,000 DVT tokens and sells them all for WETH on the Uniswap V2 exchange.

    • Remember the Uniswap pool started with 10 WETH and 100 DVT. Dumping 10,000 DVT into it will drastically shift the ratio.
    • The UniswapV2Router.swapExactTokensForTokens function is used for this. This large sale floods the market with DVT, causing its price relative to WETH to plummet to near zero within the Uniswap pool.
  3. Exploiting the Lending Pool: Now, with the DVT price severely depressed in the Uniswap oracle, the PuppetV2Pool is tricked. When calculateDepositOfWETHRequired calls _getOracleQuote, it receives the manipulated, ultra-low price for DVT.

    • Since the collateral required is 3x the DVT value, a negligible amount of WETH is now needed to borrow a huge quantity of DVT.
    • The attacker can then approve the PuppetV2Pool to spend their (now much larger) WETH balance and call pool.borrow(amountToBorrow) to withdraw the entire 1,000,000 DVT from the lending pool with minimal WETH collateral.
  4. Fund Recovery: Finally, the borrowed 1,000,000 DVT (plus the initial 10,000 DVT the attacker had) is transferred to the recovery account, completing the challenge.

The beauty of this attack lies in its simplicity and the fact that it all occurs within a single, atomic transaction. The price manipulation is temporary, reverting once the transaction is complete (if the attacker doesn't then reverse their own trade), but by then, the damage to the lending pool is already done.

The Lesson Re-Learned: Don't Trust Spot Prices

Puppet V2 perfectly illustrates why relying on a Uniswap V2 spot price as an oracle for high-value operations is a critical design flaw. While Uniswap V2 is robust as a liquidity pool, its instant price feedback mechanism is its weakness when used blindly as a price oracle.

Key Takeaway for Developers: For robust price oracles, especially when securing significant funds, always favor Time-Weighted Average Prices (TWAPs) or other more resilient oracle solutions that are less susceptible to sudden, large-scale manipulations. The "Puppet" might have new strings, but if you're not careful, an attacker will still be pulling them.


Challenge Link: https://www.damnvulnerabledefi.xyz/challenges/puppet-v2/

Built with AiAda