Aztec Connect: Exploit über ZK-Rollup-Schwachstelle bringt Angreifer rund 2,19 Mio. US-Dollar

Am 14. Juni 2026 wurde der bereits abgekündigte RollupProcessor-Vertrag von Aztec Connect (0xff1f2b4adb9df6fc8eafecdcbf96a2b351680455) ausgenutzt. Der Angreifer zog in einer einzigen atomaren Transaktion Vermögenswerte im Umfang von rund 2,19 Mio. US-Dollar aus dem L1-Pool ab. Der Angriff basierte auf einer gezielt erzeugten Grenzlücke zwischen "numRealTxs" und "decoded_slots". Aztec Connect war seit März 2024 deprecated, der unveränderliche (immutable) Vertrag blieb aber angreifbar, weil noch Restbestände von Nutzern darin lagen. Kern der Schwachstelle Die Ursache liegt in einer strukturellen Divergenz: RollupProcessorV3 traversiert bei der L1-Abwicklung nur einen Bereich von Settlement-Zyklen, der kleiner ist als der Bereich, der über den ZK-Hash der öffentlichen Inputs gebunden wird. Angreifer konnten dadurch Inhalte von 31 der 32 Public-Input-Slots per ZK-Proof in den L2-State-Root committen, ohne dass diese Slots auf L1-Ebene eine Settlement-Validierung durchliefen. Decoder.sol spielt dabei eine zentrale Rolle: "numRealTxs" wird aus calldata (Offset 4516) ausgelesen, ohne onchain-seitige Einschränkungen. "decoded_slots" wird für das Datenlayout des SHA256-Precompiles auf das nächste Vielfache von "numTxsPerRollup" aufgerundet. Genau dieses Aufrunden erzeugt eine "Gap-Region" zwischen "numRealTxs" und "decoded_slots", die sich frei befüllen lässt. In RollupProcessorV3.sol deckt der Settlement-Zyklus nur die ersten "numRealTxs" Slots ab. Warum die Sicherheitsannahmen versagten Im Normalbetrieb gilt: Jeder Public-Input-Slot wird entweder auf L1 validiert (z. B. durch Reduktion von pendingDepositBalance bei Einzahlungen) oder im ZK-Circuit so eingeschränkt, dass publicValue == 0 sein muss. Im vorliegenden Fall traf beides für die Gap-Slots nicht zu: - Das SHA256-Precompile commitet alle 32 Slots (getestet mit 8192 Bytes Input = 32 × 256 Bytes), inklusive der Gap-Slots, und diese Inhalte wurden per ZK-Proof gebunden. - Der L1-Settlement-Loop verarbeitete nur den ersten Slot; die Gap-Slots [2..32] blieben ohne L1-Validierung. - Die ZK-Circuit-Constraint für publicValue in den Gap-Slots (eigentlich 0) wurde umgangen oder war nicht wirksam. Das Resultat ist ein Dual-Path-Problem: Dieselbe calldata wird in zwei Pfaden mit unterschiedlichen Obergrenzen interpretiert. ZK erkennt 32 Slots, L1 nur 1. Diese Abweichung ermöglichte faktisch "Minting aus dem Nichts" auf L2, das anschließend als legitime Auszahlungen auf L1 realisiert wurde. Ablauf des Angriffs Die Transaktion 0x074ec931…aee1 enthielt 14 processRollup()-Aufrufe in einem Zwei-Phasen-Muster: zuerst 7 "Mint"-Rollups, danach 7 "Withdrawal"-Rollups, alles atomar in einer Transaktion. Phase 1: Minting (Rollup #13277–13283, 7×) 1) Die EOA des Angreifers 0x0f18d8b44a740272f0be4d08338d2b165b7edd17 rief den Master-Control-Entry-Contract 0x06f585f74e0da633ae813a0f23fb9900b61d0fcd auf und triggerte den Selector 0x6f3ce701. 2) Der Master-Contract rief nacheinander drei Relay-Contracts auf, die mehrere bösartige Rollup-calldatas hardcodiert hatten. Wesentliche Parameter: - numRealTxs = 1, rollupSize = 1024, numInnerRollups = 32 - Slot 1 (auf L1 sichtbar): proofId = 0 (noop), publicValue = 0 - Slots 2–32 (31 Gap-Slots, auf L1 unsichtbar): proofId = 1 (deposit), publicValue = N, publicOwner = L2-Adresse des Angreifers - inklusive passendem ZK-Proof (der Circuit schränkte publicValue in den Gap-Slots nicht auf 0 ein) 3) Relay Contract A rief RollupProcessor.processRollup() für Rollup #13277–#13281 fünfmal auf. Der Verifier akzeptierte die ZK-Proofs; das SHA256-Commitment bezog alle 32 Slots ein. Der L1-Settlement-Loop stoppte nach 1 × TX_PUBLIC_INPUT_LENGTH, verarbeitete also nur noops. Die Fake-Deposits in den Slots [2..32] wurden in den Merkle-Root committed, das L2-Guthaben des Angreifers stieg um 5 × 31N. 4) Relay Contract B wiederholte das Muster für Rollup #13282–#13283 zweimal, was weitere 2 × 31N hinzufügte. Insgesamt lagen damit 7 × 31N ungedeckte Deposits auf L2 vor, während der L1-Vault unverändert blieb. Phase 2: Withdrawal (Rollup #13284–13290, 7×) Anschließend wandelte der Angreifer das künstlich aufgeblähte L2-Guthaben über sieben Withdrawal-Rollups in echte L1-Assets um: - Rollup #13284 (DAI): withdraw() → RollupProcessor transferierte 270,513.054 DAI an 0x0f18…edd17 - Rollup #13285 (wstETH): 167.890 wstETH → Angreifer - Rollup #13286 (yvDAI): 4,873.857 yvDAI → Angreifer - Rollup #13287 (yvWETH, Relay Contract C übernimmt): 16.570 yvWETH → Angreifer - Rollup #13288 (LUSD): 9,273.734 LUSD → Angreifer - Rollup #13289 (yvLUSD): 359.047 yvLUSD → Angreifer - Rollup #13290 (ETH): RollupProcessor transferierte 908.987 ETH per internem CALL an den Angreifer Die atomare Transaktion wurde vollständig ausgeführt (gasUsed = 4,513,539); ein teilweises Zurückrollen auf Vertragsebene war nicht möglich. Der Nettoschaden lag bei rund 2,19 Mio. US-Dollar und stammte aus dem legitimen Nutzer-Asset-Pool des RollupProcessor. Fund-Tracking Onchain-Forensik (Stand: 15. Juni 2026, etwa einen Tag nach dem Vorfall) zeigt: - Sämtliche Assets wurden in einer Transaktion aus dem RollupProcessor über den Zwischenvertrag 0x06f585…d0fcD direkt an die EOA 0x0F18D8b44a740272f0be4d08338d2b165b7EdD17 transferiert. - Der Zwischenvertrag hielt keine Restbestände. - Die gestohlenen Mittel sind weiterhin zu 100% unverändert in der EOA; Hinweise auf Verschleierung oder Geldwäscheaktivitäten lagen zu diesem Zeitpunkt nicht vor. Fazit und Empfehlungen Der Vorfall zeigt, dass die Obergrenze des Settlement-Loops im ZK-Rollup-Vertrag strikt mit dem Umfang der ZK-Public-Input-Commitments übereinstimmen muss. Entsteht eine Lücke zwischen der Loop-Grenze "numRealTxs" auf L1 und den "decoded_slots" des SHA256-Commitments, können Sicherheitsannahmen, die auf Circuit-Constraints für Gap-Slots beruhen, ausgehebelt werden. L1 muss jeden Public-Input-Slot, der durch den ZK-Proof gebunden wird, eigenständig verifizieren und diese Verantwortung nicht an die Circuit-Ebene auslagern. Das SlowMist Security Team empfiehlt vor dem Deployment eines Rollup-Systems ein umfassendes externes Security-Audit, mit Fokus auf logische Konsistenz an der L1/L2-Grenze, vertrauenswürdige Boundaries beim calldata-Decoding sowie onchain-basierte Sekundärverifikation der ZK-Public-Inputs. Für deprecated Contracts, die noch Legacy-Assets halten, wird eine geordnete Asset-Migration oder die Eliminierung der Bestände empfohlen, um fortlaufende Exponierung zu vermeiden. Der Bericht wurde vom Threat-Intelligence-Team von SlowMist erstellt und basiert auf MistEye Threat Intelligence System, MistTrack Tracking Platform sowie SlowMist-Agent-AI-gestützter Analyse. Rückfragen und Feedback sind willkommen.