On October 26, 2020, USDC and USDT vaults of the Harvest Finance were exploited, causing a financial loss of about USD $33.8 million. The first exploit transaction to the USDC vault from this attack provides a strong motivation to this research. The exploit transaction contains a sequence of function calls that interact with the following smart contracts:
Attack Contract: The attack contract is a contract deployed by the attacker to execute the complex logic of the attack. It also serves as the callee of the flash loan. Namely, the flash loan provider will execute a certain function within the attack contract with
flash loan. It enables the hacker to execute complicated logic within a single transaction.
Harvest Vault Contract: Harvest vault contract is the main user interface contract of Harvest Finance Protocol. It is the victim contract of this attack. Users can deposit USDC into this vault contract and receive fUSDC tokens. Users can later use fUSDC tokens to retrieve their deposit back. The conversion rate of fUSDC and USDC is determined by current USDC balance of the vault contract, an external call investedUnderlyingBalance() to Harvest Strategy Contract, and the total supply of fUSDC tokens.
Harvest Strategy Contract: Harvest strategy contract is a contract that implements the investment strategy of Harvest Finance. It has a read-only function investedUnderlyingBalance() that calculates and returns the estimate of invested assets. However, the return value could be manipulated as this function uses the balances of Curve Y Pool contract to calculate the estimate.
Curve.Fi Y Pool Contract: Curve.Fi is an exchange protocol for stable coins. Curve.Fi Y pool contract maintains a pool of DAI, USDC, USDT, and TUSD. Users can exchange one kind of stable coins to another in this pool. The exchange rate is determined by the current balances of DAI, USDC, USDT, and TUSD in the pool. However, hackers can manipulate the balances of the pool by exchanging large amounts of stable coins back and forth.
The exploit transaction under consideration is complex, involving multiple steps and smart contract interactions. It starts with a flash loan of 50M USDC and 18.31M USDT from Uniswap, and proceeds to execute the same attack vector 3 times. The figure above summarizes the first attack vector, which interacts with Harvest vault contract and Curve.Fi Y pool contract, triggering a sequence of subsequent function calls. The transaction consumes abnormally 9,895,111 gas, just within the gas limit of 12,065,986 at the time.
The attack vector begins with an exchange of 17.22M USDT to 17.22M USDC via Curve.Fi Y pool contract. This exchange depletes the USDC balance and inflates the USDT balance in the Curve Y pool, leading to an underestimation of invested assets in the Harvest strategy contract, now valued at 51.10e12.
Then the attack contract deposits 49.98 USDC into Harvest vault contract, which increases its USDC balance from 72.83M to 122.51M. Due to the underestimated value of invested assets, the attack contract receives an inflated 51.46M fUSDC back. This inflates the total fUSDC supply from 127.58M to 179.04M. The attack contract then reverses its initial exchange, converting 17.24M USDC back to 17.23M USDT, thereby restoring the original asset balances in the Curve Y pool.In the final step, the attack contract redeems all its fUSDC tokens for 50.30M USDC, reducing the Harvest vault's USDC balance from 122.81M to 72.51M and restoring the total fUSDC supply to its
original value of 127.58M.
Abnormal Behaviors: We identify four distinct dimensions of abnormal behavior:
a high frequency of user interactions with the Harvest vault contract,
an exceptionally large volume of token flow,
an abrupt fluctuations in the total supply of fUSDC tokens,
remarkably high gas consumption.
To better understand the abnormality of the exploit transaction, we collect and analyze all transaction history of the Harvest vault contract from its deployment to the point of the exploit, as illustrated in figures below:
As shown in the above figures, the last data point in each sub-figure, representing the exploit transaction, consistently emerges as an
outlier. Specifically, we have observed that the exploit transaction is the first transaction in the contract's history to:
(1) invoke the withdraw() function from a contract rather than from a user address,
(2) call both deposit() and withdraw() functions three times within a single transaction,
(3) consume more gas than any previous transaction,
(4) extract more USDC from the protocol than any other transaction,
(5) elevate the total supply of fUSDC tokens to an all-time high.
Research Questions: Inspired by these observations and their implications for enhancing smart contract security, we are motivated to explore the following research questions:
(Effectiveness of Invariant Guards)
RQ1: Given the fact that exploit transactions often exhibit abnormal behaviors, what kinds of invariant guards are most effective at stopping exploit transactions?
(Bypassing Invariant Guards)
RQ2: If an exploit transaction or benign transaction violates invariant guards, how difficult is it for an attacker or a regular user to bypass them?
(Combinatorial Efficacy)
RQ3: As multiple dimensions of abnormality may be associated with an exploit transaction, how effective is the combination of different invariant guards in preventing exploits?
(Gas Overheads)
RQ4: Invariant guards require additional gas at runtime. What are the gas overheads of different types of invariant guards?