Root Cause
Two separate deprecated Aztec rollup contracts — both immutable with admin keys revoked — were drained in four days due to a proof-verification mismatch: the set of transactions asserted inside a verified ZK proof was not constrained to match the transactions actually executed during settlement.
// Simplified pseudocode — RollupProcessorV3 vulnerable pattern
function processRollup(bytes calldata proof, uint256 _numTxs, bytes calldata encodedData) external {
// ✓ Verifies proof's public inputs including numRealTxs
verifier.verify(proof, publicInputs);
// ✗ _numTxs comes from calldata, NOT re-bound to the verified proof's numRealTxs
_processDepositsAndWithdrawals(encodedData, _numTxs); // mismatch
}
// June 17 — Private Rollup Bridge escapeHatch variant
function escapeHatch(
bytes calldata proof,
bytes calldata encodedData,
bytes calldata viewingKeys
) external {
// ✓ ZK proof verified — but only checks that SOME valid exit is claimed
require(verifier.verify(proof), "bad proof");
// ✗ No assertion that proof's public withdrawal equals actual payout
uint256 payout = abi.decode(encodedData, (uint256)); // attacker-controlled
payable(msg.sender).transfer(payout);
}
Because both contracts were immutable stage-2 rollups (deprecated 2022), no administrative patch was possible — the only fix would have required a funded migration away from the contract before the exploits occurred. Aztec Labs confirmed it held no admin keys over either contract.
Attack Steps
| # | Date | Action |
|---|---|---|
| 1 | Jun 14, 2026 | Attacker targets RollupProcessorV3 (Aztec Connect); crafts rollup proof where numRealTxs (proved) < _numTxs (executed), extracting more assets than the proof authorises |
| 2 | Jun 14, 2026 | Contract releases 1,158 ETH + 150,000 DAI + 0.46 renBTC ≈ $2.19M to attacker |
| 3 | Jun 17, 2026 | Fresh EOA calls escapeHatch(bytes,bytes,bytes) on deprecated Private Rollup Bridge RollupProcessor |
| 4 | Jun 17, 2026 | Submits ZK proof whose sole public output claims 1,158 ETH payout to EOA; contract pays out without rebinding proof to state |
| 5 | Jun 17+ | Stolen funds routed onward; Aztec Labs confirms ≥2 separate attackers |
Impact
- June 14, 2026: ~$2.19M drained (Aztec Connect
RollupProcessorV3) — 1,158 ETH, 150,000 DAI, 0.46 renBTC - June 17, 2026: ~$2.16M drained (Private Rollup Bridge
RollupProcessor) — 1,158 ETH viaescapeHatch() - Combined: ~$4.35M across two incidents in 72 hours
- Active Aztec Network products and the AZTEC ERC-20 token were unaffected — exploited contracts were deprecated infrastructure
Lessons for Auditors
- Deprecated ≠ drained. Immutable contracts holding user reserves remain live attack surfaces indefinitely. Any sunset process must include a funded escape hatch or forced migration before admin keys are revoked.
- Proof vs. execution inputs must be identical. A verified ZK proof certifies its specific public inputs — not the calldata that drives settlement. If any settlement parameter (amounts, transaction count, recipients) is re-read from calldata after proof verification, an attacker can substitute a different value. Verify the proof, then re-derive all execution parameters from the verified public inputs only.
escapeHatchfunctions are critical paths. Emergency withdrawal mechanisms that bypass normal settlement logic carry equivalent or greater risk. They must be audited with the same rigour as primary execution paths and should have their own proof-binding constraints.- Immutability is an audit finding when funds remain. Flag any contract with
owner = address(0)or revoked admin that still holds material user balances. Recommend a migration before shutdown.