Back to Blog

~$18M verloren: jaredFromSubway, Aztec & mehr | BlockSec Weekly

Code Auditing
June 25, 2026
11 min read
Key Insights

In der vergangenen Woche (2026/06/15 - 2026/06/21) haben wir 3 bemerkenswerte Sicherheitsvorfälle mit einem Gesamtschaden von ca. 18,3 Mio. USD beobachtet.

Datum Vorfall Typ Geschätzter Schaden
2026/06/18 Aztec Unsachgemäße Bindung öffentlicher Eingaben ~2,2 Mio. USD
2026/06/20 LABUBU Token Fehlkonfiguration ~1,1 Mio. USD
2026/06/20 jaredFromSubway Unsachgemäßes Genehmigungsmanagement ~15 Mio. USD
  • Aztec: Ausgewählt, weil das Protokoll innerhalb von drei Tagen ein zweites Mal ausgenutzt wurde, diesmal über seinen Escape-Hatch-Circuit, was wiederkehrende ZK-Proof-Binding-Probleme verdeutlicht.
  • jaredFromSubway: Ausgewählt, weil der Vertrag des MEV-Bots nicht vertrauenswürdigen Token-Verträgen Genehmigungen erteilt hat, ohne den Verbrauch zu überprüfen oder verbleibende Allowances zu widerrufen, was dem Angreifer ermöglichte, ca. 15 Mio. USD anzusammeln und abzuschöpfen.

Best Security Auditor for Web3

Validate design, code, and business logic before launch

Wöchentliches Highlight: jaredFromSubway

Im Gegensatz zu traditionellen Approval-Exploits – bei denen Angreifer Schwachstellen in vertrauenswürdigen DeFi-Verträgen ausnutzen, um Vermögenswerte abzuschöpfen, die Nutzer diesen Verträgen genehmigt haben – zielt dieser Angriff auf die umgekehrte Richtung: Der MEV-Bot hat proaktiv Genehmigungen für seine eigenen Vermögenswerte an nicht vertrauenswürdige Drittanbieter-Verträge als Teil seiner Arbitrage-Operationen erteilt. Der Angreifer konstruierte eine gefälschte Handelsumgebung (im Wesentlichen ein Honeypot), in der gefälschte Swap-Pools echte Swap- und Sync-Events ausgaben, während die gefälschten Token die erteilten Allowances niemals verbrauchten, diese nach außen gerichteten Genehmigungen ansammelten und sie anschließend abschöpften, mit gemeldeten Gesamtverlusten von ca. 15 Mio. USD.

Am 20. Juni 2026 verlor jaredFromSubway, ein MEV-Bot-Betreiber auf Ethereum, ca. 15 Mio. USD [1]. Basierend auf der On-Chain-Analyse war die Grundursache ein unsachgemäßes Genehmigungsmanagement im Bot-Vertrag: Genehmigungen wurden an nicht vertrauenswürdige Wrapper-Verträge erteilt, die diese niemals verbrauchten, und der Angreifer sammelte diese ungenutzten Allowances an, bis er die echten Guthaben des Bots in einer einzigen Transaktion abschöpfte.

Hintergrund

jaredFromSubway ist ein bekannter MEV-Bot-Betreiber auf Ethereum, spezialisiert auf Sandwich-Angriffe und On-Chain-Arbitrage. Der betroffene Vertrag (0x1f2f...f387) ist einer seiner Betriebswallets und hält große Betriebskapitalguthaben in WETH, USDC und USDT.

MEV-Bots dieser Art müssen dynamisch mit beliebigen neuen Token und Pools interagieren, die On-Chain erscheinen. Sie überwachen den Mempool, simulieren Transaktionen und genehmigen Token-Interaktionen automatisch, um Arbitragemöglichkeiten zu nutzen. Dieses Betriebsmodell beruht auf der Annahme, dass Token sich wie erwartet verhalten: Wenn ein Swap ausgeführt wird, verbraucht der Token-Vertrag die erteilte Allowance durch den Aufruf von transferFrom.

Schwachstellenanalyse

Die Grundursache ist das unsachgemäße Genehmigungsmanagement des MEV-Bots bei der Interaktion mit nicht vertrauenswürdigen Verträgen.

Der Bot führt verschiedene Arbitragepfade über Uniswap-Pools und -Router aus. Bei den meisten Interaktionen überträgt der Bot Token direkt über transfer an Pools, wobei der Bot selbst msg.sender ist und keine Genehmigung erforderlich ist. Interaktionen mit Wrapper-artigen Token-Verträgen folgen jedoch einem Pull-Modell: Der Bot ruft wrapper.wrapTo() auf, und innerhalb dieses Aufrufs ruft der Wrapper-Vertrag realToken.transferFrom(bot, wrapper, amount) auf, um die echten Token des Bots zu ziehen. Da msg.sender bei transferFrom der Wrapper-Vertrag ist – nicht der Bot – ist eine vorherige approve-Genehmigung erforderlich:

  1. <real_token>.approve(tokenContract, amount) — Allowance auf dem echten Token an den Wrapper-Vertrag erteilen
  2. tokenContract.wrapTo() → mehrstufige swap()-Aufrufe über Pools → tokenContract.unwrap() — echten Token wrappen, durch Pools routen und zurück zum echten Token entpacken

Der Bot nahm an, dass wrapTo() die Allowance über transferFrom verbrauchen würde, wie es ein korrekt implementierter Wrapper-Vertrag täte. Der Bot überprüfte jedoch niemals, ob die Allowance nach der Operation tatsächlich verbraucht wurde, noch widerrief er verbleibende Allowances. Wenn wrapTo() transferFrom nicht aufruft, überlebt die vollständige Allowance die Operation und wird zu einer dauerhaften Angriffsfläche – jeder Vertrag, der eine solche Allowance hält, kann später transferFrom aufrufen, um die echten Vermögenswerte des Bots zu verschieben.

Angriffsanalyse

Basierend auf der On-Chain-Rekonstruktion konstruierte der Angreifer eine gefälschte Handelsumgebung mit drei Komponenten, um die oben beschriebene Schwachstelle auszunutzen:

  1. Gefälschte Wrapper-Token: Jeder gefälschte Token verwendete den Namen des echten Tokens, fügte dem Symbol jedoch ein f als Präfix hinzu (z. B. Name USD Coin mit Symbol fUSDC für USDC). Er implementierte wrapTo() und unwrap(), um einen legitimen Wrapper nachzuahmen, sowie eine auf den Angreifer beschränkte withdraw()-Funktion, die unverbrauchte Allowances über transferFrom abschöpfte.

  2. Gefälschte Swap-Pools: Der Angreifer deployte ca. 44 Uniswap-V2-artige Pools über eine selbst deployte Factory. Diese Pools paarten gefälschte Token miteinander, um überzeugende Swap-Routen zu bilden. Wenn swap() aufgerufen wurde, gaben die Pools echte Sync- und Swap-Events aus, die von legitimen Trades nicht zu unterscheiden waren.

  3. Vom Angreifer konstruierte Gewinne: Während unwrap() sendete der gefälschte Token eine kleine Menge echter Token über transfer zurück an den Bot. Der Bot erhielt echten Gewinn, der jedoch absichtlich vom Angreifer konstruiert und nicht durch Marktarbitrage erzielt worden war.

Der Angreifer kontrollierte diese Komponenten über einen blockweisen getStatus()-Schalter in einem externen Vertrag. getStatus() gab 1 zurück, wenn es im selben Block wie eine Aktivierungstransaktion aufgerufen wurde (die _getStatus = block.number setzte), und andernfalls 0. Wenn getStatus() == 0, rief wrapTo() transferFrom normal auf und die Allowance wurde verbraucht. Wenn getStatus() == 1, übersprang wrapTo() transferFrom – die Allowance wurde nicht verbraucht –, während unwrap() weiterhin vom Angreifer konstruierte Token an den Bot zurücksendete. Der Angreifer nutzte wahrscheinlich Builder-Bestechungen, um die Aktivierungstransaktion im selben Block wie die Transaktion des Bots zu platzieren, wenn er Allowances ansammeln wollte.

Der Angriff verlief in drei Phasen:

Phase 1: Angriffsinfrastruktur deployen

  • Schritt 1: Der Angreifer richtete die Infrastruktur über die Blöcke 25354424 bis 25354519 ein. Dies umfasste das Deployen eines gefälschten Token-Factory-Vertrags (0x81f2...0091), das Erstellen von ca. 44 gefälschten Uniswap-V2-Pools über eine selbst deployte Factory, die Finanzierung der Pools mit initialen Token-Guthaben, damit swap()-Aufrufe erfolgreich wären, sowie das Senden von 0,01 ETH an den Harvest-Vertrag (0xb84d...df52) für Gas und Builder-Bestechung.

  • Schritt 2: Der Angreifer produzierte gefälschte Wrapper-Token in großer Stückzahl über CREATE2, wobei jeder einen echten Token imitierte (unter Verwendung des echten Namens, aber mit dem Symbol-Präfix f) und eine auf den Angreifer beschränkte withdraw()-Funktion enthielt. CREATE2 lieferte deterministische Adressen, über die der Harvest-Vertrag iterieren konnte.

Phase 2: Vertrauen aufbauen und Genehmigungen ansammeln

  • Schritt 3 (anfängliches Vertrauen): In den frühesten Transaktionen (z. B. 0x542d...362b in Block 25354425) hatten die gefälschten Token keinen getStatus()-Schalter – wrapTo() rief transferFrom direkt auf und verbrauchte die Allowance. Der Bot genehmigte, wrappte, swappte, entpackte und erzielte normal Gewinn. Dies etablierte die gefälschten Token als profitable Handelsmöglichkeiten.

  • Schritt 4 (fortgesetztes Vertrauen): In nachfolgenden Transaktionen (z. B. 0x085e...37e51) war der getStatus()-Schalter deployt, gab aber 0 zurück (anderer Block als Aktivierung). wrapTo() rief weiterhin transferFrom auf und verbrauchte die Allowance. Der Bot erzielte weiterhin Gewinn und interagierte weiterhin.

  • Schritt 5 (Ansammlung): Ab 0x8560...1915 in Block 25360519 platzierte der Angreifer über Builder-Bestechung eine Aktivierungstransaktion im selben Block wie die Transaktion des Bots, sodass getStatus() 1 zurückgab. In diesem Modus übersprang wrapTo() transferFrom – die Allowance wurde nicht verbraucht –, aber unwrap() sendete weiterhin eine kleine Menge echter Token an den Bot zurück. Der Bot sah eine profitable Operation und ließ die Genehmigung bestehen. Über ca. 600 Blöcke (~13 Transaktionen) wiederholte der Bot dieses Muster für WETH, USDC und USDT und sammelte unverbrauchte Allowances für alle drei echten Vermögenswerte an.

Phase 3: Abschöpfung

  • Schritt 6: Der Angreifer rief withdraw() für alle gefälschten Token in der Harvest-Transaktion 0x2be870...cf3e65 auf und nutzte die ungenutzten Allowances, um transferFrom aufzurufen und die echten Guthaben des Bots an den Angreifer zu übertragen. Eine Builder-Bestechung von 0,01 ETH wurde eingeschlossen, um die Block-Aufnahme sicherzustellen. Die Abschöpfung extrahierte 1.474,58 WETH + 2.870.573 USDC + 2.035.760 USDT (~7,5 Mio. USD) allein aus dem betroffenen Vertrag.

Die identifizierte Angriffstransaktion führt zu Verlusten von ca. 7,5 Mio. USD, und der Gesamtverlust beträgt basierend auf der Aussage von jaredFromSubway ca. 15 Mio. USD [1].

Fazit

Die Grundursache dieses Vorfalls war das unsachgemäße Genehmigungsmanagement des MEV-Bots bei der Interaktion mit nicht vertrauenswürdigen Verträgen. Im Gegensatz zu traditionellen Approval-Exploits, bei denen Angreifer Schwachstellen in vertrauenswürdigen DeFi-Verträgen ausnutzen, um vom Nutzer genehmigte Vermögenswerte abzuschöpfen, funktioniert dieser Angriff in die umgekehrte Richtung: Der Bot genehmigte proaktiv seine eigenen Vermögenswerte an nicht vertrauenswürdige Drittanbieter-Verträge als Teil seiner Arbitrage-Operationen. Der Angreifer sammelte diese nach außen gerichteten, ungenutzten Genehmigungen an und schöpfte sie in einer einzigen Transaktion ab.

Um ähnliche Risiken in der Zukunft zu reduzieren, sollten Bot-Verträge, die mit nicht vertrauenswürdigen Token-Verträgen interagieren, nach jeder Operation überprüfen, ob Genehmigungen verbraucht wurden, und verbleibende Allowances widerrufen. Unverbrauchte Allowances nach einem scheinbar erfolgreichen Trade sind ein starkes Signal für bösartiges Token-Verhalten.

Get Started with Phalcon Explorer

Dive into Transactions to Act Wisely

Try now for free

Weitere Vorfälle dieser Woche

Aztec

Am 18. Juni 2026 ermöglichte eine fehlende Gleichheitsbeschränkung im Escape-Hatch-ZK-Circuit von Aztec einem Angreifer, ca. 2,2 Mio. USD (1.158 ETH, 150.000 DAI und ca. 0,47 renBTC) aus dem Legacy-RollupProcessor-Vertrag auf Ethereum abzuheben [2], [3]. Dies war der zweite Aztec-Exploit innerhalb von drei Tagen (der erste, der in unserem vorherigen Bericht behandelt wurde, zielte auf den upgegradeten RollupProcessorV3) durch einen verwandten ZK-Circuit-Public-Input-Binding-Fehler.

Hintergrund

Der Legacy-RollupProcessor von Aztec enthält eine escapeHatch-Funktion: einen Sicherheitsmechanismus, der es jedem ermöglicht, einen Einzeltransaktions-Proof einzureichen, wenn der Rollup-Betreiber die Verarbeitung einstellt. Im Gegensatz zu processRollup (das einen autorisierten Anbieter erfordert) öffnet sich der Escape Hatch in periodischen Fenstern basierend auf der Blocknummer und ist von jedem aufrufbar:

function escapeHatch(
    bytes calldata proofData,
    bytes calldata signatures,
    bytes calldata viewingKeys
) external override whenNotPaused {
    (bool isOpen, ) = getEscapeHatchStatus();
    require(isOpen, 'Rollup Processor: ESCAPE_BLOCK_RANGE_INCORRECT');
    processRollupProof(proofData, signatures, viewingKeys);
}

Der Escape Hatch verwendet einen dedizierten ZK-Circuit (escape_hatch_circuit), der eine Join-Split-Transaktion verarbeitet: Eingabe-Notes aus dem Merkle-Baum verbrauchen und Ausgabe-Notes erstellen. Der Circuit muss überprüfen, dass die Eingabe-Notes im aktuellen Datenbaum existieren (unter Verwendung von old_data_root für Merkle-Mitgliedschaft), und dann denselben Root als öffentliche Eingabe für den L1-Vertrag zur Validierung gegen den On-Chain-Zustand offenlegen.

Schwachstellenanalyse

Die Schwachstelle liegt im Escape-Hatch-Circuit (escape_hatch_circuit.cpp). Der old_data_root-Wert wird in zwei unabhängige Witnesses umgewandelt, ohne eine Gleichheitsbeschränkung, die sie verbindet.

Der erste Witness (Zeile 33) wird in die Join-Split-Circuit-Komponente übergeben, wo er für Merkle-Mitgliedschaftsnachweise verwendet wird, um zu überprüfen, dass Eingabe-Notes im Datenbaum existieren:

join_split_inputs inputs = {
    // ...
    witness_ct(&composer, tx.js_tx.old_data_root),        // Zeile 33: erster Witness
    // ...
};
auto outputs = join_split_circuit_component(composer, inputs);

Der zweite Witness (Zeile 50) wird unabhängig erstellt und als öffentliche Eingabe offengelegt (Zeile 88), die der Solidity-Vertrag extrahiert und gegen den On-Chain-Daten-Root prüft:

auto old_data_root = field_ct(witness_ct(&composer, tx.js_tx.old_data_root));  // Zeile 50: zweiter Witness
// ...
composer.set_public_input(old_data_root.witness_index);    // Zeile 88: als öffentliche Eingabe offengelegt

In einem ZK-Circuit erstellt jeder witness_ct-Aufruf eine unabhängige Variable. Ohne ein explizites assert_equal zwischen den Zeilen 33 und 50 kann der Prüfer diesen beiden Witnesses unterschiedliche Werte zuweisen. Auf der Solidity-Seite prüft validateMerkleRoots require(oldDataRoot == dataRoot) nur anhand der öffentlichen Eingabe aus Zeile 50, ohne Einblick in den in Zeile 33 verwendeten Wert.

Dasselbe Unbinding-Muster existiert auch für input_owner und output_owner: Diese Werte werden in den Zeilen 38–39 bezeugt (in join_split_circuit_component für die Eigentümerschaftsüberprüfung übergeben) und erneut in den Zeilen 111–112 (als unabhängige öffentliche Witnesses offengelegt). Wir haben jedoch keinen praktischen Exploit-Pfad für diese Lücke identifiziert.

Angriffsanalyse

Der Escape-Hatch-Circuit wurde aus der aztec-connect-Codebasis entfernt [4], aber der deployte Verifier-Vertrag enthält weiterhin den EscapeHatchVk-Verifikationsschlüssel, sodass mit dem anfälligen Circuit generierte Proofs weiterhin die On-Chain-Verifikation bestehen können. Zum Zeitpunkt des Angriffs war der Vertrag seit 142 Tagen inaktiv und das Escape-Hatch-Fenster war offen. Die Adresse des Angreifers war erst 14 Stunden vor dem Exploit über Union Chain erstellt worden [2]. Der Angriff besteht aus drei Kernschritten:

  • Schritt 1: Der Angreifer konstruierte einen gefälschten Merkle-Baum mit selbst gehaltenen Notes beliebigen Werts. Diese Notes existierten nicht im echten On-Chain-Datenbaum (dem Merkle-Baum, der alle gültigen Notes speichert).

  • Schritt 2: Der Angreifer generierte einen Escape-Hatch-Proof unter Ausnutzung der oben beschriebenen ungebundenen Witnesses. Der Witness aus Zeile 33 (für Merkle-Mitgliedschaft in der Join-Split-Komponente verwendet) wurde auf den gefälschten Merkle-Root gesetzt (die Mitgliedschaftsprüfung bestand, weil die fabrizierten Notes im gefälschten Baum existierten, und die Eigentümerschaftsprüfungen bestanden, weil der Angreifer die Signing-Keys besaß). Der Witness aus Zeile 50 (als die von Solidity geprüfte öffentliche Eingabe offengelegt) wurde auf den echten On-Chain-Daten-Root gesetzt (die Solidity-require(oldDataRoot == dataRoot)-Prüfung bestand, weil dieser Wert mit dem gespeicherten Root des Vertrags übereinstimmte).

  • Schritt 3: Da sowohl die Circuit- als auch die Solidity-Prüfungen erfüllt waren, wurde der Proof erfolgreich verifiziert. Der Vertrag verarbeitete die Escape-Hatch-Transaktion als legitim und gab die Mittel frei.

Der Angreifer wiederholte diesen Vorgang über drei Transaktionen (0x9e1d6a...6b03ca, 0xab306c...59c2b5, 0x5c196c...4705c3), zielte auf unterschiedliche Vermögenswerte und extrahierte jeweils 1.158 ETH, 150.000 DAI und ca. 0,47 renBTC, insgesamt ca. 2,2 Mio. USD.

Fazit

Die Grundursache dieses Vorfalls war eine fehlende Gleichheitsbeschränkung zwischen zwei Witnesses für old_data_root im Escape-Hatch-Circuit. Ein Witness wurde für die private Note-Mitgliedschaftsüberprüfung innerhalb der Join-Split-Komponente verwendet, der andere wurde als die von Solidity geprüfte öffentliche Eingabe offengelegt. Ohne eine Beschränkung, die sie bindet, bewies der Angreifer die Eigentümerschaft fabrizierter Notes gegen einen gefälschten Merkle-Baum, während der L1-Vertrag einen gültigen On-Chain-Root sah. Bemerkenswert ist, dass das Entfernen des anfälligen Circuits aus dem Quellcode den bereits deployte Verifier-Vertrag nicht neutralisierte – die escapeHatch-Funktion im Legacy-RollupProcessor bleibt aufrufbar, wann immer ihr blocknummernbasiertes Fenster offen ist.

Um ähnliche Risiken in der Zukunft zu reduzieren: Wenn derselbe logische Wert an mehreren Stellen in einem ZK-Circuit vorkommt, müssen alle Instanzen explizit als gleich beschränkt werden – unabhängige witness_ct-Aufrufe für denselben Wert sind eine Binding-Lücke. Circuit-Audits sollten systematisch überprüfen, dass jede öffentliche Eingabe an den Circuit-internen Wert gebunden ist, den sie repräsentiert.

Get Started with Phalcon Security

Detect every threat, alert what matters, and block attacks.

Try now for free

Referenzen

Über BlockSec

BlockSec ist ein Full-Stack-Anbieter für Blockchain-Sicherheit und Krypto-Compliance. Wir entwickeln Produkte und Dienstleistungen, die Kunden dabei helfen, Code-Audits durchzuführen (einschließlich Smart Contracts, Blockchain und Wallets), Angriffe in Echtzeit abzufangen, Vorfälle zu analysieren, illegale Gelder zu verfolgen und AML/CFT-Verpflichtungen zu erfüllen – über den gesamten Lebenszyklus von Protokollen und Plattformen.

BlockSec hat mehrere Blockchain-Sicherheitspapiere auf renommierten Konferenzen veröffentlicht, mehrere Zero-Day-Angriffe auf DeFi-Anwendungen gemeldet, mehrere Hacks blockiert und dabei mehr als 20 Millionen Dollar gerettet sowie Milliarden von Kryptowährungen gesichert.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit