Sitemap

Inverse Finance’s Incident Analysis —$INV Price Manipulation

7 min readApr 7, 2022
Press enter or click to view image in full size
Inverse Finance’s Incident Analysis — $INV Price Manipulation

On Apr 02, 2022, 11:04:09 AM UTC (block 14506359), the attacker borrowed assets from Inverse Finance using a collateral asset that had less actual value than the borrowed assets. The price of the collateral asset on Inverse Finance becoming more expensive at the time, making $INV collateral with more borrowable value than it should be. We will go over the technical details of this attack step by step in this article.

Related Address

Attack Steps

There are 2 main transactions used for the successful attack as follows:

1. Price manipulation, Update the price on Oracle, and Swap tokens at transaction: https://etherscan.io/tx/0x20a6dcff06a791a7f8be9f423053ce8caee3f9eecc31df32445fc98d4ccd8365

1.1 Verified that the price feed can be updated.

Press enter or click to view image in full size

1.2 Exploiter’s Contract allowed using $INV as collateral on the lending contract.

Press enter or click to view image in full size

1.3 Manipulated price by swapping 300 $WETH to 374.385477084842174221 $INV on the SushiSwap’s $WETH-$INV pair.

Press enter or click to view image in full size

1.4 Updated the price feed. The price feed of every pair was updated by calling the workForFree() function.

Press enter or click to view image in full size

The keep3rV2Oracle contract that refer to the SushiSwap’s $INV-$WETH pair (address 0x39b1df026010b5aea781f90542ee19e900f2db15) is one of updated feed.

Press enter or click to view image in full size

The price0CumulativeLast value after the update is 0x00000000000000000000000000000062d32f53f2f7afe532c0372fd0cacdbd4b, which the Oracle will calculate this value with e10 and Q112. Then the value from the calculation, 6476591327926140254201750, will be stored in the Oracle’s observation.

Press enter or click to view image in full size

It goes with the same as the price1CumulativeLast value, 0x000000000000000000000000000012d5e533107a3e9659278cb49bb1e692524d, which will be calculated into 316007731064365302759283159 and being stored into the Oracle’s observation.

At this update, Oracle has stored the manipulated cumulative price into the observation with timestamp 1648897434 at index number 114 of the observations.

1.5 Swapped 200 $WETH to 690,307.061277 $USDC on SushiSwap.

Press enter or click to view image in full size

1.6 Exchanged 690,307.061277 $USDC to 690,203.010884231600886834 $DOLA on Curve’s $DOLA + 3Crv pool.

Press enter or click to view image in full size

1.7 Swapped 690,203.010884231600886834 $DOLA to 1,372.052401667461914227 $INV on Sushiswap’s $INV-$DOLA pair.

Press enter or click to view image in full size

2. Lend and Borrow at: https://etherscan.io/tx/0x600373f67521324c8068cfd025f121a0843d57ec813411661b07edc5ff781842

Attacker lent 1,746.437878752304088448 $INV

Press enter or click to view image in full size

Returned price from the getUnderlyingPrice() function of $INV feed is 20926.791034009538953802 $USD.

Press enter or click to view image in full size

The collateral factor at the lent/borrowed time is 60% (0.6 e18) of the asset value.

Press enter or click to view image in full size

By the 1,746.437878752304088448 $INV lent, total value in $USD that the attacker can borrow is $21,928,404.32551701 (60% of 36,547,340.54252835)

The price of other related assets in the same transaction:

  • $WBTC is $46650.31
  • $ETH is $3488.82
  • $YFI is $23461.259
Press enter or click to view image in full size
Press enter or click to view image in full size
Press enter or click to view image in full size

Attacker borrowed the following assets:

  • 3,999,669.029654761043260989 $DOLA
Press enter or click to view image in full size
  • 1,588.263719446159096974 $ETH
Press enter or click to view image in full size
  • 94.03071805 $WBTC
Press enter or click to view image in full size
  • 39.368440899328442 $YFI
Press enter or click to view image in full size

Finally, the attacker transferred all borrowed assets to Inverse Finance Exploiter #2’s wallet.

Press enter or click to view image in full size
Press enter or click to view image in full size

Root Cause Analysis

The core oracle that contains the price calculation logic of Inverse Protocol is the Keep3rV2Oracle contract.

The following code is the current() function of the Keep3rV2Oracle contract.

Keep3rV2Oracle.sol

Press enter or click to view image in full size

The current() function returns the price of the asset. It is mainly used for calculating the getUnderlyingPrice() function to determine the asset value.

The current() function takes the last stored cumulative price from the observation and the current cumulative price to calculate the price. The function has a flaw that allows the calculation with a small timeElapsed value.

The problem will arise when the current() function is called 1 block after the block that the price feed has been updated, which is the case that the attacker has leveraged in this attack.

The attacker manipulated the $INV price in block number 14506358 and the Oracle stored the manipulated cumulative price in the observations. At block number 14506359, the current() function used the recently updated cumulative price from the observations and the current block’s cumulative price.

To guarantee the latest cumulative price being set, the attacker has to manually force the price feed (SushiSwap) to update the cumulative price. So, the attacker called the sync() function first at the transaction 0x600373f67521324c8068cfd025f121a0843d57ec813411661b07edc5ff781842.

Press enter or click to view image in full size

Here’s the catch, the current block’s cumulative price was calculated from last price (the attacker force update the price to ensure this value) of the previous block multiplying with the current block time (15 seconds in this case). When the current() function is calculating _computeAmountOut(), the current block’s cumulative price is the latest cumulative price plus with the multiplying of the price and the passing time. When putting every parameter into the calculation of _computeAmountOut() function

Press enter or click to view image in full size

From now on, we will simplify the equation by ignoring the scaling constant *e10/Q122 from price0Cumulative and _observation.price0Cumulative.

Press enter or click to view image in full size

The current block’s price0Cumulative is retrieved from IUniswapV2Pair(pair).price0CumulativeLast().

Press enter or click to view image in full size

The _observation.price0Cumulative is replaced with a placeholder variable {lastBlockCumulativePrice} to refer to the cumulative price from block number 14506358 onward.

Press enter or click to view image in full size

The variable IUniswapV2Pair(pair).price0CumulativeLast() is replaced with {lastBlockCumulativePrice} + (lastBlockLatestPrice*blockTime). This is how the price cumulative of block number 14506359 being calculated.

Press enter or click to view image in full size

We remove the parentheses from {lastBlockCumulativePrice} + (lastBlockLatestPrice*blockTime) to make the equation be more clearer.

Press enter or click to view image in full size

In the left parentheses, {lastBlockCumulativePrice} can be canceled each other out.

Press enter or click to view image in full size

Then we remove the lastBlockLatestPrice*blockTime from the parentheses.

Press enter or click to view image in full size

The blockTime from price0Cumulative is the length time between block used in the calculation of cumulative price, which can be observed from the getReserve() function. The timestamp of block number 14506358 is 1648897434 and the timestamp number 14506359 is 1648897449. So, the value of blockTime is 15, which is also the same number of the parameter elapsed that we had considered having a flaw earlier. The value of blockTime and elapsed is the same, which will be canceled out in the calculation in the _computeAmountOut() function.

Press enter or click to view image in full size

Long story short, this oracle will return the price from the last block last reserve price directly (with a condition, of course). The price from the Oracle in this block (14506359) will be the price that being manipulated from the last block (14506358).

Conclusion

In summary, the attacker spent around $1,737,310 (500 $ETH) to manipulate the price of $INV on Inverse Finance and exchange all assets for $INV. All exchanged $INV is used as collateral to borrow $DOLA, $ETH, $WBTC, and $YFI. The total value of borrowed assets is around $14,843,389.

About Inspex

Inspex is formed by a team of cybersecurity experts highly experienced in various fields of cybersecurity. We provide blockchain and smart contract professional services at the highest quality to enhance the security of our clients and the overall blockchain ecosystem.

For any business inquiries, please contact us via Twitter, Telegram, contact@inspex.co

--

--

Inspex
Inspex

Written by Inspex

Cybersecurity professional service, specialized in blockchain and smart contract auditing https://twitter.com/InspexCo

No responses yet