# Lixir vault permit signature bypass drains holder balances

Lixir vault-token permit accepted any signature yielding nonzero ecrecover, letting attacker forge allowances and withdraw every holder's full vault balance.

- Payout: Real-world hack
- Severity: critical
- Program: LixirPermitDrain
- Credited: DeFiHackLabs
- Disclosed: 2026-06-25
- Write-up quality: 6/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-06/LixirPermitDrain_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260625-lixirpermitdrain
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity technique:source-review vuln:access-control vuln:crypto vuln:signature-replay

**Target:** ERC-4626-style vault token permit/withdraw functions
**Root cause:** The permit function only checked that ecrecover returned a nonzero address rather than verifying the recovered signer equals the owner, so any dummy (v,r,s) with v=28 passed.
**Steps:**
1. Enumerate vault holders for each Lixir vault (lv_WETH-USDC A/B, lv_LIX-WETH A/B, lv_USDC-USDT A/B).
2. For each holder call `permit(holder, attacker, shares, type(uint256).max, 28, R, S)` with a fixed dummy signature.
3. Call `withdrawETHFrom(holder, shares, 0, 0, attacker, type(uint256).max)` on ETH vaults or `withdrawFrom(...)` on ERC20 vaults.
4. Forward drained USDC/USDT/LIX to receiver and pay 0.015 ETH to block.coinbase.
**Exploitation:** Attacker deployed a helper contract that, in its constructor, looped over known holders of six Lixir vaults, forged a permit for each holder's full share balance using a constant dummy signature, then called withdrawETHFrom/withdrawFrom to pull the underlying assets to itself and forwarded proceeds to the attacker.
**Insight:** When auditing permit/EIP-2612 implementations, check the recovered signer is compared to owner and that v is constrained to 27/28; a bare `ecrecover(...) != address(0)` check lets any nonzero-recovery dummy signature authorize arbitrary spenders.
