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

- Inverse Finance Exploiter #1: https://etherscan.io/address/0x117c0391b3483e32aa665b5ecb2cc539669ea7e9
- Inverse Finance Exploiter #2: https://etherscan.io/address/0x8b4c1083cd6aef062298e1fa900df9832c8351b3
- Exploiter’s Contract: https://etherscan.io/address/0xea0c959bbb7476ddd6cd4204bdee82b790aa1562
- Keep3rV2Oracle: https://etherscan.io/address/0x39b1df026010b5aea781f90542ee19e900f2db15

## 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.

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

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

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

function.

The `keep3rV2Oracle`

contract that refer to the SushiSwap’s $INV-$WETH pair (address `0x39b1df026010b5aea781f90542ee19e900f2db15`

) is one of updated feed.

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.

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.

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

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

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

Attacker lent 1,746.437878752304088448 $INV

Returned price from the `getUnderlyingPrice()`

function of $INV feed is 20926.791034009538953802 $USD.

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

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

Attacker borrowed the following assets:

- 3,999,669.029654761043260989 $DOLA

- 1,588
**.**263719446159096974 $ETH

- 94.03071805 $WBTC

- 39.368440899328442 $YFI

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

# 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**

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.

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

From now on, we will simplify the equation by ignoring the scaling constant `*e10/Q122`

from `price0Cumulative`

and `_observation.price0Cumulative`

.

The current block’s `price0Cumulative`

is retrieved from `IUniswapV2Pair(pair).price0CumulativeLast()`

.

The `_observation.price0Cumulative`

is replaced with a placeholder variable `{lastBlockCumulativePrice}`

to refer to the cumulative price from block number `14506358`

onward.

The variable `IUniswapV2Pair(pair).price0CumulativeLast()`

is replaced with `{lastBlockCumulativePrice} + (lastBlockLatestPrice*blockTime)`

. This is how the price cumulative of block number `14506359`

being calculated.

We remove the parentheses from `{lastBlockCumulativePrice} + (lastBlockLatestPrice*blockTime)`

to make the equation be more clearer.

In the left parentheses, `{lastBlockCumulativePrice}`

can be canceled each other out.

Then we remove the `lastBlockLatestPrice*blockTime`

from the parentheses.

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.

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