Full Report
Omni is an NFT money market on Ethereum. It allowed for borrowing and lending via NFTs. For instance, a user could borrow an ERC20 asset for the NFT put up as collateral. This makes the NFT more liquid, since it can be borrowed against. The function executeWithdrawERC721 will run once a user wants to remove their NFT collateral from the market. When it does this, it uses the onERC721Received if it's a contract that implements the interface. When allowing code to be executed as a callback, two things need to be done: use the checks-effects-interaction pattern and include reentrancy locks. If these are not done, then major havoc can ensue. The function executeWithdrawERC721 has a snippet of code that informs the market that the address no longer has deposited collateral in the contract. Prior to this variable being changed, we can escape the contract and borrow! When the code finishes, our collateral will be taken out, allowing us to steal funds from the contract. A similar vulnerability occurs by using the executeERC721LiquidationCall hook with the burn function. The attacker actually abused both of the vulnerabilities to perform the reentrancy bug twice. The rest of the post contains a great proof of concept with step by step details on how to exploit the bug. Overall, interesting vulnerability and interesting exploit!
Analysis Summary
# Incident Report: Omni Protocol Reentrancy Exploitation
## Executive Summary
On July 10, 2022, the Omni Protocol, an Ethereum-based NFT money market, was exploited for approximately $1.4 million. The attacker leveraged a cross-function reentrancy vulnerability triggered by the `onERC721Received` callback during NFT withdrawal and liquidation processes. This allowed the attacker to withdraw collateral while simultaneously borrowing funds, effectively "double-spending" their position and draining protocol liquidity.
## Incident Details
- **Discovery Date:** July 10, 2022
- **Incident Date:** July 10, 2022
- **Affected Organization:** Omni Protocol
- **Sector:** Decentralized Finance (DeFi) / NFT Finance
- **Geography:** Global / Ethereum Mainnet
## Timeline of Events
### Initial Access
- **Date/Time:** July 10, 2022
- **Vector:** Exploitation of Smart Contract Logic
- **Details:** The attacker utilized Doodles NFTs (via NFTX vaults) as collateral to interact with the Omni lending pools.
### Lateral Movement
- **Technique:** Cross-function Reentrancy
- **Details:** The attacker initiated a withdrawal via `executeWithdrawERC721`. Because the protocol called the ERC721 `safeTransferFrom` (which triggers a callback to the receiver) *before* updating the user's collateral status, the attacker reentered the contract. During this hijacked execution flow, the attacker borrowed assets against collateral that the protocol was already in the process of releasing.
### Data Exfiltration/Impact
- **Loss:** ~$1.4M USD in various ERC20 assets.
- **Mechanism:** By the time the original withdrawal transaction finalized, the collateral was returned to the attacker, but the borrowed debt remained in a state associated with a "zero-collateral" account, effectively leaving the protocol with bad debt and lost liquidity.
### Detection & Response
- **Detection:** Post-incident chain analysis and security researcher alerts.
- **Response Actions:** The protocol's smart contracts were eventually deprecated; the article notes that as of the reporting date, Omni smart contracts are no longer in active use.
## Attack Methodology
- **Initial Access:** Smart contract interaction via malicious actor-controlled contracts.
- **Persistence:** Not applicable (Atomic transaction-based exploit).
- **Privilege Escalation:** Exploiting logic flaws to gain "borrowing power" without maintaining required collateral.
- **Defense Evasion:** Use of Flash Loans or existing NFT liquidity (NFTX) to mask the scale of the attack during the initial phases of the transaction.
- **Lateral Movement:** Utilizing the `onERC721Received` hook to jump between the `Withdraw` and `Borrow` functions (Cross-function reentrancy).
- **Impact:** Draining of ERC20 liquidity pools (WETH and other assets).
## Impact Assessment
- **Financial:** Estimated loss of $1.4 million.
- **Data Breach:** None (Public blockchain transaction data only).
- **Operational:** Complete cessation of the protocol's lending operations.
- **Reputational:** Significant loss of trust; protocol moved to an inactive state.
## Indicators of Compromise
- **Network Indicators:** Transaction Hash: `0x05d65e0adddc5d9ccfe6cd65be4a7899ebcb6e5ec7a39787971bcc3d6ba73996`
- **File/Contract Indicators:** Omni Pool Contract: `0x50c7a557d408a5f5a7fdbe1091831728ae7eba45`
- **Behavioral Indicators:** High-frequency interactions involving `safeTransferFrom` followed immediately by `borrow` calls within the same transaction.
## Response Actions
- **Containment:** The vulnerability was inherent to the immutable smart contract; recovery involves migrating users or pausing the protocol if a pause hex exists.
- **Eradication:** Identification of the lack of "Checks-Effects-Interactions" pattern in `SupplyLogic.sol`.
- **Recovery:** Development of a Proof of Concept (PoC) by security researchers to understand and prevent similar flaws in future iterations.
## Lessons Learned
- **Pattern Adherence:** Failure to follow the "Checks-Effects-Interactions" pattern is fatal in DeFi. State changes (like setting collateral usage to false) should always happen before external calls.
- **Callback Risks:** The `safeTransferFrom` function in the ERC721 standard is notoriously "unsafe" regarding reentrancy because it handovers execution control to the receiver.
- **Lock Implementation:** Every state-changing function in a lending protocol should be protected by a global reentrancy guard (e.g., OpenZeppelin’s `ReentrancyGuard`).
## Recommendations
- **Audit Requirement:** Subject all "safe" transfer methods to rigorous reentrancy testing.
- **State Management:** Ensure all internal accounting and "UserConfig" updates are finalized before triggering NFT transfers.
- **Security Tooling:** Implement static analysis tools (like Slither) and formal verification to detect state-change violations before deployment.