Polter Finance Exploit Explained: $12M Loss When Fork-and-Pray Fails
Simeon Cholakov
read

Introduction to Polter Finance

Polter Finance is a decentralized, non-custodial protocol designed for lending and borrowing digital assets. It uses liquidity pools where users can deposit assets to earn interest or borrow against their collateral. The protocol is based on the same smart contracts as the GEIST platform, which previously operated on the Fantom blockchain, but with modifications such as disabling flash loans to address known vulnerabilities.

The platform operates using a tokenized incentive model centered around $POLTER tokens, which are distributed to participants for providing liquidity, borrowing, or staking. Its mechanics include dynamic interest rate adjustments, vesting of earned rewards, and a liquidation system to maintain solvency and ensure borrower accountability.

How Polter Finance Operates

Lending Pools

Assets deposited into Polter Finance are pooled into a single reserve for each token type. Borrowers access liquidity from these reserves, and the interest paid by borrowers is distributed among depositors and stakers.

Collateralization and Borrowing

Borrowers must collateralize assets to access liquidity. The borrowing limit is determined by the Loan-to-Value (LTV) ratio specific to each asset. For instance, an asset with a 30% LTV allows borrowing up to 30% of the collateral's value. Borrowers' positions are monitored via a "Health Factor," which indicates the solvency of their loans.

Interest Rate Mechanism

Polter employs a dynamic interest rate model tied to the utilization rate of each asset pool:

  • Utilization Rate (U):
  • Interest Rate Curve: Each pool has a two-segment linear curve. Below the optimal utilization point, interest rates grow slowly, encouraging borrowing. Beyond this point, rates increase steeply to incentivize deposits and reduce borrowing.

Liquidation Process

Borrowers with a Health Factor below 1 become eligible for liquidation:

  • Health Factor Calculation:
  • Liquidation Mechanism: Liquidators repay a portion of the borrower's debt and claim a corresponding value of collateral, along with a penalty fee.

Tokenomics

$POLTER tokens are central to the protocol's incentive structure. Tokens are distributed based on participation metrics such as deposit size, borrowing utilization, and staking contributions. Rewards are vested for a 3-month period to encourage long-term engagement.

Polter Finance's Security

Oversight

Before delving into the details of the exploit, it's important to highlight a significant security oversight by the Polter Finance team. The project neglected to conduct an independent security audit of their platform, instead relying on the audit report of another project, Geist Finance, from which they had forked their codebase.

Reliance on Forked Code Audits

On their audit page, Polter Finance stated:

Screenshot from the Polter Finance website

While forking code from a reputable project can be a starting point, relying solely on the original project's audit is insufficient for several reasons:

  • Code Modifications: Even minor changes, such as the removal of the flash-loan function, can introduce unforeseen vulnerabilities.
  • Different Deployment Contexts: The operational environment, integrations, and dependencies may differ, affecting security.
  • Updates and Patches: The forked code may not include the latest security patches or updates implemented in the original project.
  • Responsibility for Security: Relying on another project's audit does not transfer the responsibility of ensuring security to your own project.

Attack Analysis

On November 16, 2024, Polter Finance, a decentralized lending protocol operating on the Fantom network, suffered a significant exploit resulting in the loss of approximately $12 million. The attacker leveraged a common vulnerability in decentralized finance: price oracle manipulation. Specifically, the exploit targeted Polter Finance's reliance on price data from SpookySwap's V2 and V3 liquidity pools for the BOO token. By executing a flash loan to manipulate these liquidity pools, the attacker artificially inflated the BOO token's price. This allowed them to deposit a minimal amount of BOO as collateral and borrow assets worth far more than the actual value of the collateral, effectively draining multiple lending pools.

Overview of the Exploit

The core issue stemmed from a flaw in Polter Finance's price oracle system, particularly within the ChainlinkUniV2Adapter contract used by the AaveOracle. The oracle failed to validate significant price fluctuations and lacked safeguards against manipulation via flash loans. The attacker exploited this by:

  1. Flash Loan Manipulation: Initiating flash loans to temporarily drain BOO tokens from SpookySwap V2 and V3 liquidity pools, causing a drastic imbalance in the token reserves.
  2. Collateral Deposit: Depositing a small amount of BOO into Polter Finance's lending pool as collateral.
  3. Exploiting Inflated Price: Leveraging the artificially inflated BOO price to borrow assets worth far more than the actual value of the collateral.
  4. Profiting and Exiting: Repaying the flash loans and retaining the borrowed assets, effectively draining Polter Finance's liquidity.

Detailed Breakdown of the Attack

Step 1: Flash Loan Initiation

The attacker initiated the exploit by taking out substantial flash loans from SpookySwap's V2 and V3 liquidity pools:

  • SpookySwap V2 Flash Loan: 269,042 BOO tokens
  • SpookySwap V3 Flash Loan: 1,154,788 BOO tokens
Flash loans are uncollateralized loans that must be repaid within the same transaction. They are commonly used for arbitrage opportunities but can be exploited to manipulate on-chain data if not properly safeguarded.
Flash loan from SpookyV2 and SpookyV3 LPs

By borrowing these large amounts of BOO tokens, the attacker temporarily removed significant liquidity from the pools. This action drastically reduced the BOO reserves in the pools, causing an imbalance in the token pairings.

Automated Market Maker (AMM) Model:

The price of tokens in AMM-based decentralized exchanges like SpookySwap is determined by the constant product formula:

  • x: Reserve of token A (e.g., BOO)
  • y: Reserve of token B (e.g., wFTM)
  • k: Constant product of the reserves

When the reserve $x$ (BOO tokens) decreases significantly due to the flash loan, the price of BOO relative to token B increases sharply to maintain the constant $k$. This results in an artificially inflated price of BOO within the liquidity pools.

Step 2: Collateral Deposit with Inflated Value

Using the manipulated spot price, the attacker deposited just 1 BOO token into Polter Finance's lending pool as collateral.

Due to the logic flaw in the oracle, the AaveOracle used the flawed price feed that evaluated the 1 BOO token at an inflated value of approximately $1.37 trillion instead of its actual market value.

This allowed the attacker to have an excessively overvalued collateral, enabling them to borrow vast amounts of assets.

Depositing 1 BOO Token
Inflated BOO Token value

Step 3: Exploiting the Oracle Vulnerability

The ChainlinkUniV2Adapter contract was used to fetch the current price of the BOO token. However, it lacked safeguards to check for drastic price fluctuations resulting from the flash loan.

The _fetchPrice function within this adapter retrieved the current price of BOO directly from the liquidity pools without any mechanisms to detect or prevent sudden price manipulations.

_fetchPrice() Function

The getRoundData() function, used for retrieving historical price data, also failed to validate significant price changes.

getRoundData() function

It relied on _getPriceAndTimestamp(), which simply returned the current manipulated price without assessing its legitimacy or comparing it to previous values. Moreover, getRoundData() used hardcoded values for answeredInRound, bypassing dynamic price verification mechanisms.

_getPriceAndTimestamp() function

As a result, the manipulated price was not validated before being returned. Additionally, the hardcoded answeredInRound = 2 value in the function did not account for whether the price data was accurate for the current round, further allowing the flawed price to pass unchecked.

latestRoundData() function

The latestRoundData() function, intended to return the most recent price information, was instead providing falsely inflated prices due to liquidity shifts caused by the attacker's flash loan. It lacked mechanisms to verify that the retrieved price (answer) was accurate and unaffected by manipulation.

The getRoundData() and latestRoundData() functions were designed to maintain consistent and accurate pricing for BOO tokens but failed to account for the significant price deviations triggered by the flash loan. The attacker exploited this vulnerability by offering minimal collateral while benefiting from an artificially inflated price feed.

previousChainlink0Response mechanism

The prevChainlink0Response mechanism was intended to flag price changes exceeding a defined threshold by comparing the current price with the previous one in the _chainlinkPriceChangeAboveMax function, which is called within _fetchPrice:

code block

However, due to the hardcoded roundId in getRoundData(), the mechanism couldn't fetch valid previous prices, rendering the comparison ineffective and allowing the inflated price to bypass the oracle's checks.

  • Hardcoded roundId: The getRoundData() function always returns a roundId of 2, preventing access to historical price data by decrementing roundId.
getRoundData() function
  • Invalid Previous Round Data: Since roundId is always 2, attempting to fetch previous round data using _currentRoundId - 1 (which becomes 1) fails because the adapter does not support fetching data for roundId other than 2.
  • Undetected Price Spike: Consequently, the _chainlinkPriceChangeAboveMax function in the PriceFeedV2 contract could not detect the abnormal price spike caused by the flash loan manipulation.
code block

This failure allowed the artificially inflated price of 1 BOO token to bypass the oracle's checks, leading to erroneous collateral value calculations.

The attacker continued to borrow wFTM tokens against the inflated collateral. By maintaining the manipulated price, they could continue the borrowing cycle, draining the liquidity pools without restriction. The oracle contract was unable to detect these repeated borrowings due to its malfunctioning price validation logic.

Borrow 9,134,844 wFTM

The attacker borrowed 9,134,844 wFTM by using the inflated price of the BOO token as collateral, ultimately draining approximately $12 million from the Polter Finance lending pools.

Step 4: Repaying Flash Loans and Profiting

After successfully borrowing assets from Polter Finance, the attacker proceeded to repay the flash loans. By returning the borrowed BOO tokens to SpookySwap V2 and V3, they restored the liquidity pools' reserves, which normalized the BOO token's price back to its actual market value. With the flash loans settled, the attacker retained the borrowed wFTM tokens and any other assets obtained during the exploit.

How the Attack Could Have Been Prevented

The exploit against Polter Finance stemmed from significant security oversights, primarily the absence of an independent security audit. Instead of thoroughly auditing their platform, Polter Finance relied on the audit report of Geist Finance, the project from which they forked their codebase. This reliance is insufficient because even minor modifications, like disabling the flash-loan function, can introduce new vulnerabilities. An independent audit could have identified the weaknesses in their oracle system that were exploited.

To prevent the attack, Polter Finance should have implemented robust validation mechanisms within their price oracle. The ChainlinkUniV2Adapter contract lacked safeguards against drastic price fluctuations caused by flash loans. Incorporating time-weighted average prices (TWAP) or using reliable price feeds from decentralized oracle networks like Chainlink would have mitigated the impact of temporary price manipulations. Additionally, functions responsible for fetching price data should have included checks to compare new prices against historical data and reject anomalous values.

By conducting an independent security audit and enhancing their oracle's validation processes, Polter Finance could have detected and addressed these vulnerabilities. Such measures would have significantly reduced the risk of price manipulation attacks, safeguarding the protocol and its users' assets.

Consequences

The immediate consequence of the Polter Finance exploit was the loss of approximately $12 million from its lending pools. This substantial financial loss affected both the platform and its users, who suffered significant asset depletion. As this severe breach exposed critical vulnerabilities in Polter Finance's security infrastructure, it could negatively impact user trust and tarnish the protocol's reputation. Such an incident may lead users to reassess the security and reliability of Polter Finance.

The Polter Finance Protocol Response

Following the exploit, Polter Finance took immediate action to mitigate the damage and communicate with their community. Their response included several key steps:

  1. Platform Suspension: The platform was promptly paused to prevent further exploitation and protect any remaining user funds.
Screenshot from Polter Finance Twitter

2. Reaching Out to the Exploiter: The team made an on-chain appeal to the individual responsible for the exploit, aiming to negotiate the return of the funds or reach an amicable solution.

Screenshot from Polter Finance Twitter

3. Collaboration with Security Experts: Polter Finance actively engaged with cybersecurity organizations to investigate the exploit and seek a resolution.

Screenshot from Polter Finance Twitter

4. Legal Action: Polter Finance filed an official police report concerning the exploit and shared this development with their community.

Screenshot from Polter Finance Twitter

Addresses

Addresses