Full Report
Timeless Finance released a product called Bunni. The purpose of this is to make Uniswap v3 liquidity pools composable. This allows loans to be taken out via Uniswap liquidity and aims to have lower gas fees. When a user passes in funds and it mints tokens, the function _mintShares() is called. If the ERC20 token has zero supply, then the minted shares are simply the amount of liquidity added. Otherwise, it mints the amount proportional to the percentage of the total liquidity. In the second case, this is done with mulDiv(totalSupply,liquidityAdded,existingLiquidity). It should be noted that the division will round down. If totalSupply * addedLiquidity is less than existingLiquidity then this value could be 0. An attacker can take advantage of this scenario with the following steps: A user deposits funds into the new contract. For example, let's use 10K in liquidity. A MEV bot sees this and frontruns the transaction with two steps: Provide 1 wei of liquidity to the contract to make the supply 1. Provide 10,001 of liquidity to the Uniswap pool on behalf of Bunni. This is possible with the standard Uniswap functions. The users transaction executes. The math turns out as follows: 1 * $10K / 10,001. This will round down to 0. Withdraw all of the liquidity, including the profiting 10K. The attacker owns all of the original shares from the 1 wei deposit. Since the attackers owns all of the shares and zero shares were minted for the user depositing 10K, withdrawing the funds from the contract will get all of the funds! To fix this problem, there are two main ways. First, requiring a minimum deposit to start with could make this infeasible. The other solution would be having a check to ensure that it's not possible to create zero shares. The twitter thread links to code4rena and by Yannis Smaragdakis, which have similar findings. Overall, interesting find that exploits weird math.
Analysis Summary
# Vulnerability: Initial Deposit Share Inflation (Inflation Attack) in Bunni Protocol
## CVE Details
- **CVE ID:** N/A (DeFi protocols often lack formal CVE assignments; tracking via Code4rena/Security Research reports).
- **CVSS Score:** 8.1 (High)
- **CWE:** CWE-682: Incorrect Calculation; CWE-190: Integer Overflow or Wraparound (Rounding-down logic).
## Affected Systems
- **Products:** Timeless Finance - Bunni Protocol.
- **Versions:** Initial launch versions of `BunniHub` / `BunniToken` contracts (prior to Oct 2022 patch).
- **Configurations:** Uniswap v3 liquidity pools integrated via Bunni where the contract has a `totalSupply` of zero or very low liquidity.
## Vulnerability Description
The vulnerability is a classic "Inflation Attack" common in ERC-4626-like vault implementations. When a user deposits funds, the `_mintShares()` function calculates the number of shares to issue.
If the `totalSupply` of Bunni tokens is greater than zero, the contract uses the formula:
`shares = mulDiv(totalSupply, liquidityAdded, existingLiquidity)`
Because Solidity performs integer division, the result rounds down. If an attacker can manipulate the `existingLiquidity` to be significantly larger than the product of `totalSupply * liquidityAdded`, the resulting share count rounds down to **zero**. The user's deposit is processed, but they receive zero shares, while the ratio of assets per share is massively inflated for existing token holders (the attacker).
## Exploitation
- **Status:** PoC Available / Identified by security researchers (Riley Holterhus).
- **Complexity:** Medium (Requires MEV/Frontrunning capabilities).
- **Attack Vector:** Network (Public Mempool).
**Step-by-Step Scenario:**
1. **Victim** sends a transaction to deposit 10,000 units of liquidity into a new Bunni pool.
2. **Attacker** frontruns the transaction:
* Injects 1 wei of liquidity to become the first minter (setting `totalSupply` to 1).
* Directly transfers 10,001 units of liquidity to the Bunni contract (or the underlying Uniswap position) without minting shares, artificially inflating `existingLiquidity`.
3. **Victim's** transaction executes:
* Math: `1 (totalSupply) * 10,000 (added) / 10,001 (existing)` = `0.999...` which rounds down to **0**.
4. **Attacker** burns their 1 wei share, which now represents their original deposit + the victim's 10,000 units.
## Impact
- **Confidentiality:** None.
- **Integrity:** High (Calculations of share ownership are compromised).
- **Availability:** High (Loss of user funds; the first depositor can essentially "steal" subsequent initial deposits).
## Remediation
### Patches
- The Bunni team updated the contracts to include checks that prevent zero-share minting.
- Implementation of "Virtual Shares" or "Dead Shares" (burning the first 10^3 shares to the zero address) is the industry-standard fix for this pattern.
### Workarounds
- **Minimum Deposit Requirement:** Enforce a minimum liquidity amount for the initial mint to make the cost of the inflation attack prohibitively expensive.
- **Slippage Protection:** Ensure users set a `minSharesOut` parameter to revert the transaction if shares minted are 0.
## Detection
- **Indicators of Compromise:** Large direct transfers of assets to the vault contract that do not correspond to mint events; transactions where `liquidityAdded` result in 0 `shares` issued.
- **Detection Methods:** Monitoring the mempool for frontrunning attempts on `_mintShares()` or `deposit` functions in new DeFi vaults.
## References
- Riley Holterhus Advisory: hxxps://x[.]com/rileyholterhus/status/1581026040529555456
- Bunni Protocol: hxxps://bunni[.]pro
- Code4rena Similar Findings: hxxps://code4rena[.]com/reports/2022-04-pooltogether#h-01-the-first-depositor-can-steal-funds-of-others
- Yannis Smaragdakis on Inflation Attacks: hxxps://yanniss[.]github[.]io/yield-farming-losses[.]pdf