Payy exploit: forged ZK proof, fix public 73 days
Published Oct 13, 2026
- Project
- Payy Network
- Chain
- Ethereum
- Date
- Sep 24, 2026
- Loss
- $1.92M
- Class
- ZK verifier bug
- Basis
- Project post-mortem or audit firm analysis
Payy's rollup contract paid out USDC when shown a valid proof that funds had been burned on the rollup. On September 24, 2026 an attacker submitted three such proofs for burns that never happened and withdrew 1,918,793 USDC.
The proofs were forged through a bug in the Barretenberg verifier. A sub-protocol checked a running sum but did not force that sum to start at zero, which, in the words of the upstream fix, left "one degree of freedom" to a malicious prover. The rollup contract also did not require the burn hash to be non-zero. The exact way the proof was built has not been published.
The fix landed in the Aztec repository on May 14, 2026 (commit 58bf73a, "small subgroup ipa bug") and shipped in Barretenberg v5.0.0 on July 13, 73 days before the attack. Payy's verifier, deployed on May 15, was built from an October 2025 version. Payy has ruled out key leaks, social engineering and infrastructure compromise.
How to protect your protocol
- Track security fixes in every dependency that guards money. A proving system is a dependency like any other. Subscribe to its releases, read the changelog for anything touching soundness, and have a path to redeploy the verifier quickly.
- Know the exact version you deployed. Pin it, record it next to the deployed address, and check it against upstream advisories on a schedule, not after an incident.
- Validate public inputs on-chain. A proof only guarantees what its public inputs say. Reject zero or empty values that should never appear (here, a zero burn hash), so a flaw in the proof system alone is not enough.
- Cap withdrawals per window. A rollup bridge that can be emptied in a few transactions turns any verifier bug into a total loss.
Sources
- Certora analysis
- Payy post-mortem
- Rollup contract:
0x7d8837b547f4fea0053571cb149e845fc58e9b2d - Withdrawals:
0xf43abdac5422087f645d77923eb1c825178bff3eb86d17d40fa18d89701e1814,0xb82f517eceb472f3b31dc7048ad730f867a251e4e9718ccdbf3593ee8bde596a,0xda88fb9273c703a4d2647c744362f58b6db4e104d7a203a75d4b67e3d54858c8 - Upstream fix: AztecProtocol/aztec-packages commit
58bf73a289f7da78c8920c9c73604a8e5d105025(May 14, 2026), released in Barretenberg v5.0.0 (July 13, 2026).
How this was prepared: compiled from the sources above with the help of automated tools, then reviewed by the Foothold security team before publication.
Give Atlant a task
Could the same thing happen in your code?
Get it checked →