Full Report
Saddle Finance is an automated market maker (AMM) on Ethereum. In particular, they specialize in stable swaps and aim to reduce the slippage of users. The bulk of the code is based on Curve but has been widely re-implemented. There are two types of pools: Standard and Meta. The standard pool is an AMM pool where the tokens provided for liquidity are swapped via the pool. In the meta tool, a pegged token and another token represent the liquidity in the standard pool. Meta allow liquidity in one pool to be used in additional pools. When trying to figure out the fees for a given Liquidity Provider (LP) token, there is a mispricing. The LP token's virtual price grows and grows as more fees are taken. However, the swap function does not account for this virtual price. The base virtual price calculation was simply NOT applied to the contract. Interestingly enough, forks of Saddle (Nerve and Synapse) were exploited for a similar issue. Patches were made to the forks and Saddle itself but they missed the patch on the swap calculation. At a high level, we need make a very large swap for the LP token with the missing price calculation. How this is precisely done: Obtain USDC funds via a flash loan. Swap USDC for sUSD. Swap the sUSD into the Saddle pool in order to get the LP token via the Meta pool. Swap the LP token for sUSD via the Meta pool. Doing this over and over again allows for us to obtain a lot more sUSD because of the LP token mispricing. Swap sUSD for USDC. In the real world attack, the attacker swaps the sUSD and LP several times to remove all of the liquidity from the Meta pool. The article demonstrates a proof of concept of the exploit as well, which is pretty cool to look at! Two takeaways for me. First, financial calculation mistakes are easy to make, hard to trace and costly- they are not obvious patterns to follow for auditors. Secondly, where there are bugs, there are likely more bugs! In this case, they patched the bulk of the functionality but missed a single location. Unfortunately, this was all the attacker needed.
Analysis Summary
# Incident Report: Saddle Finance Smart Contract Exploit
## Executive Summary
On April 30, 2022, Saddle Finance was targeted in a series of exploit transactions targeting its Meta pools, resulting in a loss of approximately $11 million in crypto assets. The attack exploited a mathematical error in the `MetaSwapUtils` library where the virtual price of LP tokens was not accounted for during swaps. While a whitehat actor successfully rescued 25% of the at-risk funds, the remainder was drained by malicious actors using flash-loan-assisted price manipulation.
## Incident Details
- **Discovery Date:** April 30, 2022
- **Incident Date:** April 30, 2022
- **Affected Organization:** Saddle Finance
- **Sector:** Decentralized Finance (DeFi)
- **Geography:** Global / Ethereum Blockchain
## Timeline of Events
### Initial Access
- **Date/Time:** April 30, 2022
- **Vector:** Exploitation of a logic error in the `swap` function of the Meta pool smart contracts.
- **Details:** The attacker utilized a flash loan to acquire the initial capital (USDC) necessary to manipulate the pool's liquidity and pricing.
### Lateral Movement
- **N/A:** As this was a decentralized protocol exploit, "movement" consisted of iterative swaps between sUSD and LP tokens to compound the pricing error.
### Data Exfiltration/Impact
- **Financial Loss:** Approximately $11 million USD equivalent in various stablecoins was removed from the liquidity pools.
- **Method:** Through repeated swapping of mispriced LP tokens for sUSD, the attacker effectively drained the pool's underlying collateral.
### Detection & Response
- **Detection:** The exploit was detected on-chain as large-scale liquidity drainage occurred.
- **Response actions:** A whitehat security researcher identified the ongoing attack and executed a "rescue" transaction, securing roughly $2.75 million (25% of total) before the attackers could claim it.
## Attack Methodology
- **Initial Access:** Smart contract vulnerability exploitation via flash loan.
- **Persistence:** Not applicable (transaction-based exploit).
- **Privilege Escalation:** Not applicable.
- **Defense Evasion:** Use of multiple transactions to obfuscate the total drain and bypass simple slippage checks.
- **Discovery:** Identifying an unpatched math error in `MetaSwapUtils.sol` that had been previously patched in similar forks (Nerve/Synapse) but remained in Saddle’s `swap` logic.
- **Impact:** Financial exhaustion of the sUSD-Saddle-LP Meta pool.
## Impact Assessment
- **Financial:** ~$11,000,000 lost ($8.25m to attackers, $2.75m rescued by whitehat).
- **Data Breach:** None (transactional transparency is inherent to the blockchain).
- **Operational:** Temporary cessation of specific pool operations and loss of liquidity provider confidence.
- **Reputational:** High; the bug was a "known-unknown" that had been patched in other functions but missed in one specific calculation.
## Indicators of Compromise
- **Network indicators:** Transactions interacting with `0x2b023d65485c4bb68d781960c2196588d03b871dc9eb1c054f596b7ca6f7da56`.
- **Behavioral indicators:** Rapid, high-volume swaps between USDC, sUSD, and LP tokens involving flash loans from protocols like Aave or Uniswap.
- **Smart Contract calls:** Calls to `MetaSwapUtils` missing the `baseVirtualPrice` calculation.
## Response Actions
- **Containment:** Manual intervention by whitehat community members to rescue remaining funds.
- **Eradication:** Deployment of patched smart contracts addressing the mispricing in the `swap` function.
- **Recovery:** Coordination with whitehats to return rescued funds to the protocol/users.
## Lessons Learned
- **Patch Management:** Incomplete patching is as dangerous as no patching. The team addressed the bug in deposit/withdraw functions but missed the swap function.
- **Code Inheritance:** When reimplementing code from another language (Vyper to Solidity), subtle mathematical omissions can lead to catastrophic failures.
- **Audit Complexity:** Financial logic errors are harder for traditional auditors to catch than standard reentrancy or access control bugs.
## Recommendations
- **Comprehensive Regression Testing:** Ensure that when a bug is found in one function, all related logic paths (swaps, deposits, withdrawals) are audited for the same error.
- **Formal Verification:** Use formal verification tools to ensure mathematical invariants (like the relationship between LP price and virtual price) hold true under all conditions.
- **Monitoring:** Implement real-time monitoring for abnormal swap ratios that deviate significantly from market oracles.