In der vergangenen Woche (2026/06/08 - 2026/06/15) wurden 4 bemerkenswerte Vorfälle auf Ethereum und Solana festgestellt, die zu Gesamtverlusten von etwa 5,98 Millionen US-Dollar führten. Die folgende Tabelle zeigt die repräsentativen Ereignisse:
| Datum | Vorfall | Typ | Geschätzter Verlust |
|---|---|---|---|
| 2026/06/08 | Flooring Protocol | Integer-Überlauf | ~900.000 $ |
| 2026/06/09 | Top Token | Governance-Angriff | ~1,59 Mio. $ |
| 2026/06/10 | Raydium (auf Solana) | Fehlende Eingabevalidierung | ~1,34 Mio. $ |
| 2026/06/15 | Aztec | Fehlende Eingabevalidierung | ~2,15 Mio. $ |
- Aztec: Eine Validierungslücke zwischen dem Proof-Pfad des Rollups und dem L1-Settlement-Pfad ermöglichte es beiden, unterschiedliche Transaktionsmengen zu verarbeiten und dabei inkonsistente Zustände zu erreichen.
- Raydium: Eine fehlende Validierungsprüfung ermöglichte es dem Angreifer, die LP-Token-Einlösungsberechnung zu manipulieren und die gesamten Reserven aus vier Pools zu leeren.
Bester Sicherheitsprüfer für Web3
Design, Code und Geschäftslogik vor dem Launch validieren
Wöchentliches Highlight: Aztec
Bei diesem Vorfall verarbeiteten der ZK-Proof-Verifier und die L1-Settlement-Logik unterschiedliche Transaktionsmengen, weil ein einzelner Parameter unbegrenzt blieb. Diese Konsistenzlücke zwischen Proof und Settlement gilt für jedes Rollup-Design, bei dem diese beiden Pfade als separater Code ausgeführt werden.
Am 15. Juni 2026 wurde Aztec Connect, ein datenschutzorientiertes Rollup auf Ethereum, für etwa 2,15 Mio. $ ausgenutzt [1]. Die Grundursache war eine Diskrepanz zwischen der verifizierten Rollup-Transaktionsmenge und der L1-Settlement-Verarbeitungsgrenze, die es dem ZK-Proof-Pfad und der Settlement-Logik ermöglichte, unterschiedliche Transaktionslisten zu verarbeiten. Der Angreifer nutzte diese Lücke aus, um ungedeckte Einzahlungsguthaben im Rollup-Zustand gutzuschreiben und diese dann über normale Settlement-Flows abzuheben.
Hintergrund
Aztec Connect ist ein datenschutzorientiertes Rollup auf Ethereum, das private Transaktionen auf L2 ermöglicht. Da Benutzermittel auf L1 entstehen, müssen sie zunächst in den Rollup-Processor-Vertrag eingezahlt werden, bevor sie als Notes im L2-Merkle-Baum dargestellt werden können.
Der Einzahlungsprozess funktioniert in zwei Phasen:
Phase 1: Der Benutzer ruft depositPendingFunds() auf, wodurch userPendingDeposits[assetId][owner] über increasePendingDepositBalance() erhöht und die Token in den RollupProcessor übertragen werden. Dadurch entsteht eine ausstehende Einzahlung auf L1.
function depositPendingFunds(uint256 _assetId, uint256 _amount, address _owner, bytes32 _proofHash) external {
increasePendingDepositBalance(_assetId, _owner, _amount);
// ... Token in den Vertrag übertragen
}
Phase 2: Der Benutzer reicht einen Einzahlungsbeweis ein, der später in einem Rollup enthalten und dem L2-Zustand hinzugefügt wird. Wenn processRollup() ausgeführt wird, liest decodeProof() numTxs aus den codierten Calldata und gibt es zusammen mit den decodierten Proof-Daten zurück. Beide werden dann an processRollupProof() übergeben:
function processRollup(bytes calldata, bytes calldata _signatures) external {
(bytes memory proofData, uint256 numTxs, uint256 publicInputsHash) = decodeProof();
processRollupProof(proofData, _signatures, numTxs, publicInputsHash, rollupBeneficiary);
}
Innerhalb von processRollupProof() werden zwei Funktionen sequenziell aufgerufen. Zuerst verifiziert verifyProofAndUpdateState() den ZK-Proof gegen alle decodierten Transaktionen und aktualisiert den Rollup-Zustand. Dann verarbeitet processDepositsAndWithdrawals() das L1-Settlement, iteriert nur die ersten _numTxs-Slots und ruft decreasePendingDepositBalance() für jede Einzahlung auf (dieser Aufruf wird rückgängig gemacht, wenn der Benutzer in Phase 1 keine Mittel tatsächlich eingezahlt hat, wodurch das Rollup-Guthaben an eine echte L1-Übertragung gebunden wird):
function processRollupProof(bytes memory _proofData, bytes memory _signatures,
uint256 _numTxs, uint256 _publicInputsHash, address _rollupBeneficiary) internal {
verifyProofAndUpdateState(_proofData, _publicInputsHash); // Proof-Pfad: alle decodierten Transaktionen
processDepositsAndWithdrawals(_proofData, _numTxs, _signatures); // Settlement-Pfad: nur die ersten _numTxs
}
// innerhalb von processDepositsAndWithdrawals:
end := add(proofDataPtr, mul(_numTxs, TX_PUBLIC_INPUT_LENGTH))
while (proofDataPtr < end) {
// ... für jede Einzahlung:
decreasePendingDepositBalance(assetId, publicOwner, publicValue);
}
Dieses zweistufige Design erfordert, dass die L1-Settlement-Logik genau dieselbe Transaktionsmenge verarbeitet, die der ZK-Proof verifiziert hat. Wenn die beiden Pfade bei den zu verarbeitenden Transaktionen nicht übereinstimmen, können Einzahlungen im Rollup-Zustand gutgeschrieben werden, ohne ihre ausstehenden Salden auf L1 zu verbrauchen.
Schwachstellenanalyse
Im Rollup-Processor-Vertrag (0x7d65...2728) war numTxs nicht effektiv an die vom ZK-Proof durchgesetzte Transaktionsmenge gebunden. Der Proof-Pfad und der Settlement-Pfad konnten daher unterschiedliche Transaktionslisten verarbeiten.
Im Off-Chain-rollup_circuit wird num_txs als Zeuge geladen und nur bereichsbeschränkt. Der Schaltkreis verwendet es, um zu steuern, welche Slots als echte Transaktionen behandelt werden, überprüft jedoch nicht, ob num_txs der tatsächlichen Anzahl von Nicht-Padding-Proofs entspricht:
const auto num_txs = uint32_ct(witness_ct(&composer, rollup.num_txs));
field_ct(num_txs).create_range_constraint(MAX_TXS_BIT_LENGTH);
// ...
auto is_real = num_txs > uint32_ct(&composer, i); // steuert echte-Tx-Logik pro Slot
Der Prover kann num_txs auf einen beliebigen Wert innerhalb des zulässigen Bereichs setzen. Slots jenseits von num_txs werden weiterhin rekursiv verifiziert, aber ihre öffentlichen Eingaben werden auf null gesetzt, sodass sie nicht zum Rollup-Zustand beitragen:

Auf der Solidity-Seite liest decodeProof() numTxs aus Calldata-Metadaten, die nicht in die rekonstruierten proofData kopiert werden, die von verifyProofAndUpdateState() verifiziert werden. Die Grenze der Settlement-Schleife wird daher auch nicht durch den ZK-Proof abgedeckt:

Da keiner der beiden Seiten diesen Wert einschränkt, konnte ein Angreifer numTxs niedriger setzen als die tatsächliche Anzahl der decodierten Transaktionen. Die Settlement-Schleife würde dann Transaktionen überspringen, die der Proof bereits im Rollup-Zustand gutgeschrieben hatte. Eine nicht ausführbare Transaktion könnte den ersten decodierten Slot belegen (im Scanbereich des Settlements), während eine echte Einzahlung in einem späteren Slot liegen könnte (vom Schaltkreis bewiesen, aber außerhalb des Settlement-Scanbereichs). Der Proof würde die Einzahlung im Rollup-Zustand gutschreiben, aber die Settlement-Logik würde sie vollständig überspringen, einschließlich des Aufrufs von decreasePendingDepositBalance(). Dadurch blieb das ausstehende Einzahlungsguthaben auf L1 unverbraucht, während der Rollup-Zustand die Einzahlung bereits widerspiegelte.
Angriffsanalyse
Die folgende Analyse basiert auf der Transaktion 0x074ec9...9aeeb1.
Der Angreifer nutzte die Lücke zwischen dem Proof-Pfad und dem Settlement-Pfad in zwei Phasen aus.
Phase 1: Ungedeckte Guthaben erstellen
-
Schritt 1: Der Angreifer reichte mehrere Rollup-Batches ein, die jeweils zwei decodierte Transaktionen enthielten: eine nicht ausführbare (wertlose) Transaktion in Slot 1 und eine echte Einzahlung in Slot 2, wobei
numTxsauf 1 gesetzt wurde. Die L1-Settlement-Logik verarbeitete nur die wertlose Transaktion in Slot 1 und übersprang die echte Einzahlung in Slot 2 vollständig. -
Schritt 2: Der ZK-Proof verifizierte und schrieb jedoch alle decodierten Transaktionen gut, einschließlich der Einzahlung in Slot 2. Da die Settlement-Logik diese Einzahlung nie erreichte, wurde
decreasePendingDepositBalance()nicht aufgerufen, und das ausstehende L1-Einzahlungsguthaben blieb unverbraucht. Der Angreifer wiederholte dieses Muster für sieben verschiedene Assets und baute so ungedeckte Guthaben im Rollup-Zustand auf.
Phase 2: Mittel extrahieren
- Schritt 3: Sobald die sieben ungedeckten Guthaben eingerichtet waren, initiierte der Angreifer standardmäßige Abhebungen für jedes Asset. Diese Abhebungen erschienen der Settlement-Logik legitim, da die Guthaben im Rollup-Zustand existierten, sodass der L1-Vertrag die entsprechenden Mittel freigab — insgesamt etwa 2,15 Mio. $.

Fazit
Diese Schwachstelle war keine kryptografische Schwäche, sondern ein Zustandskonsistenzfehler zwischen zwei kritischen Codepfaden in der Rollup-Architektur. Die Grundursache: numTxs war auf keiner Seite an die bewiesene Transaktionsmenge gebunden. Der Schaltkreis beschränkte es nur bereichsmäßig, und der Solidity-Decoder las es aus unverifizieren Calldata-Metadaten. Ohne diese Bindung konnten der Proof-Pfad und der Settlement-Pfad unterschiedliche Transaktionslisten verarbeiten. Der Angreifer setzte numTxs niedriger als die tatsächliche Transaktionsanzahl, sodass die Settlement-Logik Einzahlungen übersprang, die der Proof bereits im Rollup-Zustand gutgeschrieben hatte. Die resultierenden ungedeckten Guthaben wurden dann über normale Settlement-Flows abgehoben.
Das Aztec-Connect-Rollup kündigte eine Abschaltung an, wobei die Transaktionsverarbeitung und Abhebungen bis zum 31. März 2024 enden sollten [2]. Der Rollup-Processor-Vertrag wurde jedoch am 10. April 2024 über einen Pull Request noch einmal aktualisiert [3], und die anfällige Logik ist in diesem Upgrade nach der Abschaltung vorhanden.
Die Lösung erfordert, dass numTxs an die vollständige Menge der vom ZK-Proof verifizierten Transaktionen gebunden wird, sodass beide Pfade immer dieselbe Menge verarbeiten. Jedes Rollup-Design, das Proof-Verifizierung von L1-Settlement trennt, muss sicherstellen, dass beide Pfade auf einer identischen, nachweislich begrenzten Transaktionsmenge arbeiten. Eine Diskrepanz bei auch nur einem Parameter kann ein ansonsten solides Proof-System zu einem Angriffsvektor für die Erstellung ungedeckter Guthaben machen.
Referenzen
- [1] BlockSec Phalcon Alert: Aztec-Vorfallsanalyse
- [2] Aztec Connect Abschaltungshinweis
- [3] RollupProcessorV3-Upgrade PR #67
Erste Schritte mit dem Phalcon Explorer
Transaktionen analysieren, um kluge Entscheidungen zu treffen
Jetzt kostenlos ausprobierenWeitere Vorfälle dieser Woche
Raydium
Am 10. Juni 2026 wurden vier Pools im Legacy-AMM-v3-Programm von Raydium auf Solana für etwa 1,34 Mio. $ ausgenutzt [1]. Der Abhebungs-Handler überprüfte nicht, ob ein vom Aufrufer bereitgestelltes Konto mit dem gespeicherten Gegenstück des Pools übereinstimmte, sodass der Angreifer ein kontrolliertes Konto einsetzte, um die Auszahlungsberechnung zu manipulieren. Dieselbe Technik leerte alle Reserven aus vier Pools innerhalb von Sekunden.
Hintergrund
Raydiums AMM ist ein Constant-Product-Market-Maker auf Solana. Jeder Pool hält zwei Token-Vaults und prägt einen LP-Token, der einen proportionalen Anteil an den Reserven darstellt. Wenn ein Liquiditätsanbieter abhebt, berechnet der Handler die Auszahlung proportional und überträgt den entsprechenden Anteil beider Vaults:
coin_out = total_coin * withdraw_amount / lp_supply
pc_out = total_pc * withdraw_amount / lp_supply
Auf Solana wird jeder Token-Typ durch ein Mint-Konto definiert, das den Gesamtvorrat, die Dezimalstellen und die Mint-Autorität speichert. Das Guthaben jedes Inhabers wird in einem separaten Token-Konto gespeichert, das an diesen Mint gebunden ist — ein Mint kann viele Token-Konten bei verschiedenen Inhabern haben. Dies unterscheidet sich von EVM, wo ein einziger ERC-20-Vertrag sowohl die Token-Definition als auch alle Guthaben intern verwaltet.
In der obigen Abhebungsformel wird lp_supply aus dem LP-Mint-Konto des Pools gelesen — demjenigen, das den gesamten LP-Vorrat verfolgt. Die Korrektheit der Berechnung hängt davon ab, dass dieser Wert der echte LP-Mint ist. Auf Solana übergibt der Aufrufer jedoch jedes Konto positionell in jede Anweisung, sodass der Handler validieren muss, dass jedes vom Aufrufer bereitgestellte Konto mit dem kanonischen Konto übereinstimmt, das im Pool-Zustand gespeichert ist.
Schwachstellenanalyse
Das ausgenutzte Programm (27haf8...8vQv) war nicht quelloffen, und seine ausführbaren Daten (ProgramData) wurden nach dem Angriff geschlossen, wodurch eine direkte Bytecode-Inspektion unmöglich wurde. Die folgende Analyse basiert auf dem aus dem letzten Upgrade-Puffer des Programms rekonstruierten Bytecode, der mit dem On-Chain-Transaktionsverhalten abgeglichen wurde.
Im Abhebungs-Handler war das vom Aufrufer übergebene LP-Mint-Konto nicht an amm.lp_mint des Pools gebunden. Der folgende aus dem On-Chain-Bytecode rekonstruierte Pseudo-Code zeigt das Konto-Layout. Der Handler überprüfte Bindungen für den Pool-Zustand, die PDA-Autorität, beide Vaults und Benutzerkonten — aber nicht für den LP-Mint an Slot 5:
let amm_info = next_account_info(it)?; // accounts[1] — Pool-Zustand (enthält amm.lp_mint)
// ...
let amm_lp_mint_info = next_account_info(it)?; // accounts[5] — vom Aufrufer bereitgestellter Mint
let amm = AmmInfo::load(amm_info)?;
// Bindungen für Autorität, Vaults, open_orders hier geprüft...
// >>> FEHLEND: Prüfung, ob accounts[5].key == amm.lp_mint <<<
let lp_mint = Mint::unpack(&amm_lp_mint_info.data.borrow())?;
let lp_mint_supply = lp_mint.supply; // liest aus dem unverifizierten Mint
let coin_amount = total_coin * withdraw_amount / lp_mint_supply;
let pc_amount = total_pc * withdraw_amount / lp_mint_supply;
Da das LP-Mint-Konto ungebunden war, konnte ein Angreifer ein Mint-Konto einsetzen, das er vollständig kontrollierte. Das Setzen seines Gesamtvorrats (supply) auf 1 und das Verbrennen von 1 Token ergab ein Auszahlungsverhältnis von 1 / 1 = 100% jeder Reserve.
Der anfällige Code war seit dem letzten Programm-Upgrade am 3. Januar 2023 unverändert aktiv, also etwa 1.254 Tage vor dem Exploit.
Angriffsanalyse
Die folgende Analyse basiert auf der Transaktion 1csN6v...3s7s.
- Schritt 1: Der Angreifer erstellte ein gefälschtes LP-Mint-Konto mit
decimals = 0und Gesamtvorratsupply = 0.

- Schritt 2: Der Angreifer initialisierte ein Token-Konto, das an den gefälschten LP-Mint gebunden war, und prägte dann genau 1 Token hinein (als Mint-Autorität), wodurch der Gesamtvorrat des Mints auf 1 fixiert wurde.

- Schritt 3: Der Angreifer rief die Abhebungsfunktion auf, übergab den gefälschten LP-Mint im erwarteten Konto-Slot und das Token-Konto aus Schritt 2 (mit 1 gefälschtem LP-Token) als LP-Quelle. Mit
withdraw_amount = 1undlp_supply = 1berechnete der Handlertotal_coin * 1 / 1undtotal_pc * 1 / 1, was 100 % beider Reserven entsprach (893.700USDCund 66.837RAYfür den RAY/USDC-Pool).

- Schritt 4: Der Handler verbrannte den 1 Token des Angreifers und übertrug die gesamten Reserven aus beiden Pool-Vaults, was den RAY/
USDC-Pool vollständig leerte.

Der Angreifer wiederholte dasselbe Muster gegen drei weitere Pools innerhalb von etwa 15 Sekunden. Die abgehobenen Beträge über alle vier Pools hinweg waren:
| Pool | Abgehoben (ca.) |
|---|---|
| RAY/USDC | ~66.837 RAY + ~893.700 USDC |
| RAY/wSOL | ~74.720 RAY + ~5.603 wSOL |
| RAY/SRM | ~8.622 RAY + ~10.692 SRM |
| RAY/Sollet ETH | ~5.038 RAY + ~16 Sollet ETH |
Fazit
Die Grundursache ist eine einzige fehlende Konto-Validierungsprüfung: Der Abhebungs-Handler verwendete den supply eines vom Aufrufer bereitgestellten Mint-Kontos als LP-Supply-Divisor, ohne es an amm.lp_mint des Pools zu binden. Auf Solana muss jedes vom Aufrufer bereitgestellte Konto an sein kanonisches Gegenstück gebunden werden, das im Pool-Zustand gespeichert ist. Eine korrekte Implementierung sollte jeden LP-Mint ablehnen, dessen Schlüssel nicht mit dem gespeicherten Eintrag des Pools übereinstimmt, und die Einlösung aus einem pool-internen LP-Zähler berechnen statt aus dem supply des extern bereitgestellten Mints. Der ausgenutzte Vertrag war eine ältere Bereitstellung (zuletzt im Januar 2023 aktualisiert), die am selben Tag wie der Angriff geschlossen wurde. Laut dem Raydium-Team wird die vollständige Entschädigung durch die Raydium-Treasury abgewickelt [1].
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, Blockchains und Wallets), Angriffe in Echtzeit abzufangen, Vorfälle zu analysieren, illegale Gelder zurückzuverfolgen und AML/CFT-Verpflichtungen zu erfüllen — über den gesamten Lebenszyklus von Protokollen und Plattformen hinweg.
BlockSec hat mehrere Blockchain-Sicherheitsarbeiten auf renommierten Konferenzen veröffentlicht, mehrere Zero-Day-Angriffe auf DeFi-Anwendungen gemeldet, mehrere Hacks blockiert und dabei mehr als 20 Millionen US-Dollar gerettet sowie Milliarden von Kryptowährungen gesichert.
-
Offizielle Website: https://blocksec.com/
-
Offizieller Twitter-Account: https://twitter.com/BlockSecTeam



