Déjà vu at LI.FI: Similar Exploit Hits Protocol Again Draining ~$9 million

Simão Amaro
12 min read

Introduction to LI.FI

LI.FI is a multi-chain liquidity aggregation protocol that supports any-to-any swaps by aggregating bridges and DEX aggregators across more than 20 networks. The protocol isn’t limited to just swaps; it also provides a JavaScript/TypeScript SDK for easy integration into front-end and back-end applications, allowing developers to build custom UX/UI around its bridge and swap functionalities. The protocol also offers a REST API for deeper and customized integrations. Additionally, LI.FI provides a widget for quick user onboarding without extensive integration.

Multi-chain Liquidity Aggregation and How does LI.FI work

In this section, you can find a brief introduction to Multi-chain Liquidity Aggregation and how it works in LI.FI. If you're already familiar with the topic, feel free to skip this part. Multi-chain liquidity aggregation refers to the process of combining and utilizing liquidity from multiple blockchain networks to facilitate seamless and efficient transactions. This involves integrating liquidity pools, decentralized exchanges (DEXs), and other financial instruments across various blockchains, allowing users to trade assets and access liquidity without being confined to a single network. The main goals of multi-chain liquidity aggregation are to enhance liquidity, improve trading efficiency, reduce costs, and provide users with a broader range of financial products and services.

Understanding LI.FI smart contracts requires familiarity with EIP-2535 (Multi-facet Proxy standard) since they are built using this standard. The contract logic resides within a single contract, which then utilizes DELEGATECALL to call facet contracts containing the core business logic.

LI.FI smart contracts structure

A basic example would be a user bridging assets from one chain to another using the Hop Protocol. The user interacts with the LiFiDiamond contract, which passes the Hop-specific call to the HopFacet. The HopFacet then forwards the required calls and parameters to Hop Protocol's contracts.

Bridging assets example

Attack Analysis

In a disconcerting déjà vu, LI.FI Protocol suffered a major exploit on July 16, 2024, nearly identical to a similar attack in 2022. This exploit resulted in the theft of nearly $9 million from user accounts. It involved 10 transactions on the Ethereum mainnet and 1 on Arbitrum.

The root cause of the exploit was the lack of validation for transaction calls within the recently deployed GasZipFacet contract just 5 days before the exploit, which was designed to facilitate gas refuel bridging for users.

This allowed arbitrary transactions to be processed. The attacker leveraged this vulnerability by crafting a malicious transaction that executed a transferFrom operation instead of a token swap, leading to the drainage of user balances.

This type of attack is a well-known vulnerability in the Web3 space and has been exploited in various instances, including the first attack on LI.FI in 2022. More information about the 2022 LI.FI exploit can be found here.

Vulnerability Exploitation

Let’s examine the third and largest of the 13 transactions, where the attacker stole around $4.6 million:

The attacker structured a Malicious Smart Contract which was used to call the fallback function of the LiFiDiamond contract. This initiated a broader transaction that led to a subsequent call from the LiFiDiamond contract to the GasZipFacet contract.

LiFiDiamond’s fallback() code snippet

The critical call involved invoking the depositToGasZipERC20 function, which accepts three parameters: _swapData, _destinationChains, and _recipient.

The depositToGasZipERC20 function is intended to swap ERC20 tokens into native tokens and deposit them into a gas zip router, utilizing the LibSwap.swap.

GasZipFacet depositToGasZipERC20() code snippet

The attacker provided the following input for the depositToGasZipERC20 function call:

Attacker’s depositToGasZipERC20() input

The callData parameter, which contains the encoded instructions for the external contract, is crucial to understanding the exploit. It encodes the function selector and the parameters required for the function call. In this case, the callData is used to encode a transferFrom function call instead of a token swap.

Let's break down the provided callData:

Attacker’s calldata break down

Function Selector: 0x23b872dd :

  • The first 4 bytes (0x23b872dd) represent the function selector for the transferFrom function in the ERC-20 token standard.
  • The transferFrom function has the following signature: transferFrom(address from, address to, uint256 value).

From Address: 0xa4fdabde3705fccf068bd1f164fcb30635b42b04

  • The address from which the tokens will be transferred. This is the sender's address.

To Address: 0x8b3cb6bf982798fba233bca56749e22eec42dcf3

  • The address to which the tokens will be transferred. This is the recipient's address.

Value (Amount): 0x00000000000000000000000000000000000000000000000000000012a7dc4e736

  • The amount of tokens to be transferred. This is represented in hexadecimal format and needs to be converted to a decimal value to understand the exact amount.

The key to understanding the exploit is the LibSwap.swap function, which the depositToGasZipERC20 function calls with the provided _swapData. Here's the relevant code of the swap function and how it was manipulated:

LibSwap swap() code snippet

In the code above, the swap function performs the following steps:

  1. Validation: It checks if the callTo address is a contract and if the fromAmount is greater than zero.
  2. Balance Checks: It ensures that the contract has sufficient balance of the sendingAssetId to perform the swap.
  3. Approval: If necessary, it approves the fromAmount of sendingAssetId to the approveTo address.
  4. Low-Level Call: It makes a low-level call to the callTo contract with the provided callData.
  5. Emit Event: If successful, it emits an AssetSwapped event with the details of the swap.

The attacker exploited the low-level call in the swap function. By crafting the _swapData, which included the malicious callData that encoded the transferFrom function. When the swap function executed the low-level call, it triggered the transferFrom function of the ERC20 token, transferring tokens from the victim's address to the attacker's address.

Tenderly’s transferFrom call

This process was repeated a total of 10 times in the same transaction, ultimately stealing around $4.6 million in a single transaction. The ability to execute multiple transferFrom calls within one broader transaction allowed the attacker to maximize their exploit in a very short time frame.

Tenderly’s fallback calls

Summed with the other 12 transactions, this resulted in a total of around $9 million being stolen.

The attacker then started to distribute the stolen funds between different wallets. However, the strong community work marking the addresses and investigating the attack has made very hard for the attacker to succeed.

How the attack could have been prevented

To prevent similar attacks is key to ensure robust validation of all input parameters to functions, particularly those involving financial transactions, is crucial. The GasZipFacet contract failed to validate the callData properly, which allowed the attacker to craft malicious transactions.

Additionally, the GasZipFacet contract was deployed without being audited. Conducting comprehensive security audits by third-party experts before deploying contracts to the mainnet can identify potential vulnerabilities. Also rigorous testing, including fuzz testing and simulation of potential attack vectors, could help uncover vulnerabilities that might be missed during standard testing procedures.

Consequences

The immediate consequence of this attack was the loss of approximately $9 million in user funds, impacting user trust and the protocol's reputation. Additionally, such exploits can lead to broader market instability, especially if users lose confidence in the security of DeFi protocols.

Implementing strict checks to verify that the callData conforms to expected structures and contains permissible operations can prevent such exploits.

The LI.FI Protocol Response

After the official acknowledgment, LI.FI announced that the only wallets affected were set to infinite approvals, and represented only a very small number of users. They also stated that they are engaging with appropriate law enforcement authorities and relevant third parties, including security teams from the industry, to trace funds.

Some hours after the announcement above, they announced that the protocol is fully operational again and that they continue to engage with law enforcement authorities and industry participants to trace and recover funds.

Addresses

Attacker Address:

Attacker Malicious Contract:

Exploited Contract:

Credits: LI.FI, deAlex, Decurity