Back to Blog

zkLend Exploit Post-Mortem: Details und Missverständnisse des $10M Flash Loan Angriffs beleuchtet

February 20, 2025
8 min read

Am 12. Februar 2025 wurde zkLend [1], ein Kreditprotokoll auf StarkNet, durch eine ausgeklügelte Manipulation seines Akkumulatormechanismus um rund 10 Millionen US-Dollar kompromittiert. Der Angreifer nutzte Flash-Kredite und Rundungsfehler, um die Werte der Sicherheiten künstlich aufzublähen und andere Vermögenswerte vom Protokoll zu leihen, um Gewinne zu erzielen.

Es fehlt jedoch eine detaillierte und genaue technische Analyse aus Sicherheitssicht. Trotz vorhandener Analysen anderer Sicherheitsexperten, die wertvolle Einblicke lieferten, bestehen weiterhin einige Missverständnisse – insbesondere hinsichtlich der Angriffsanalyse. Die spätere Veröffentlichung des offiziellen Post-Mortems von zkLend [2] bietet eine vereinfachte Beschreibung, es fehlt jedoch eine detaillierte technische Analyse. In diesem Blogbeitrag möchten wir eine umfassende Untersuchung liefern, um den Vorfall zu klären.

Wichtigste Erkenntnisse (TL;DR)

  • Die Hauptursache dieses Vorfalls liegt in der Kombination der folgenden drei Probleme:

    • Die leere Markinitialisierung ermöglicht die Einzahlung beliebiger Vermögenswerte.
    • Der spezifische Spendenmechanismus in zkLends Flash-Kredit ermöglicht die Manipulation des Akkumulators, einer globalen Variablen als Skalierungsfaktor zur dynamischen Anpassung der Sicherheitenwerte der Benutzer.
    • Präzisionsverlust tritt durch Abschneiden auf. Im Gegensatz zum klassischen Präzisionsverlust bei der Division beginnt der Nenner bei 1, wird aber auf einen sehr großen Wert aufgebläht, was zu einer Unterschätzung beim Verbrennen des Anteil-Tokens führt.
  • Der Angreifer profitierte nicht von wstETH, das von anderen Benutzern hinterlegt wurde. Stattdessen nutzte der Angreifer die Schwachstellen aus, um den Sicherheitenwert zu manipulieren und eine kleine Menge wstETH als Anfangskapital zu verwenden, um den Sicherheitenwert auf über 7.000 wstETH zu erhöhen, was die Aufnahme anderer Vermögenswerte aus dem Markt ermöglichte.

In den folgenden Abschnitten werden wir zunächst einige wichtige Hintergrundinformationen zu zkLend liefern. Anschließend werden wir eine eingehende Analyse der Probleme und des damit verbundenen Angriffs durchführen.

0x1 Hintergrund: Verständnis des Kernprotokolls von zkLend

zkLend ist ein Kreditprojekt auf StarkNet, das gängige Kreditprotokolle wie besicherte Kredite und Flash-Kredite unterstützt. Lassen Sie uns in die Implementierungsdetails dieser beiden Protokolle eintauchen.

0x1.1 Besicherte Kredite

Ein besicherter Kredit bezieht sich auf den Prozess, bei dem Benutzer bestimmte Vermögenswerte als Sicherheit an das Protokoll hinterlegen und im Gegenzug andere Vermögenswerte leihen. Der Wert der Sicherheit bestimmt die Kreditkapazität. Es ist wichtig zu beachten, dass Kreditprotokolle normalerweise nicht den Wert des Sicherheitenvermögens direkt speichern; stattdessen berechnen sie ihn mit der Formel:

collateral_balance = lending_accumulator * raw_balance

Insbesondere ist der lending_accumulator ein Skalierungsfaktor, der den Sicherheitenwert jedes Benutzers dynamisch anpasst, während raw_balance den tatsächlichen Anteil darstellt, den der Benutzer auf dem Markt hält. raw_balance wird aus dem collateral_balance unter Verwendung des lending_accumulator abgeleitet.

Was ist der Zweck dieses Designs? Es ermöglicht dem Protokoll, den Sicherheitenwert effizient zu verwalten und gleichzeitig die Benutzer zur Einzahlung von Vermögenswerten zu ermutigen. Durch die Zuweisung eines Teils der Einnahmen des Protokolls an die Sicherheitenanbieter steigt der lending_accumulator, wodurch der Wert aller Sicherheiten proportional und gleichzeitig verstärkt wird.

0x1.2 Flash-Kredite auf zkLend

Ein Flash-Kredit ist eine Art unbesicherter Kredit, bei dem Benutzer Vermögenswerte für einen sehr kurzen Zeitraum, normalerweise innerhalb einer einzigen Transaktion, vom Protokoll leihen können. Gelingt es dem Kreditnehmer nicht, den Kredit zurückzuzahlen oder die angegebenen Bedingungen zu erfüllen, wird die gesamte Transaktion rückgängig gemacht und der Kredit wird nicht ausgeführt.

In der Flash-Kredit-Implementierung von zkLend gibt es einen einzigartigen Spenden-Mechanismus. Wenn Benutzer Vermögenswerte zurückzahlen, geben sie nicht nur den erforderlichen Mindestbetrag zurück, sondern können auch zusätzliche Gelder als Spende beitragen. Das Protokoll verfolgt diese gespendeten Gelder und aktualisiert den lending_accumulator entsprechend. Dieser Prozess wird in der Funktion thesettle_extra_reserve_balance() implementiert. Die Formel zur Aktualisierung des lending_accumulator lautet wie folgt:

new_accumulator = (reserve_balance + totaldebt - amount_to_treasury) / ztoken_supply

  • reserve_balance: Der Gesamtbetrag des zugrunde liegenden Tokens (z. B. wstETH), der im Vertrag gehalten wird, einschließlich der von Benutzern gespendeten Token.
  • totaldebt: Die Gesamtschuld aller Kreditnehmer.
  • amount_to_treasury: Der Betrag der Protokolleinnahmen.
  • ztoken_supply: Die Gesamtmenge des Anteil-Tokens (z. B. zwstETH). Wenn Benutzer wstETH einzahlen, prägt der zkLend ztoken-Vertrag einen entsprechenden Betrag an zwstETH.

Nachdem wir das Kernprotokoll von zkLend verstanden haben, werden wir nun formal erläutern, wie der Angreifer seine Sicherheitenwerte manipulierte, indem er die Variablen lending_accumulator und raw_balance manipulierte.

0x2 Angriffsanalyse

Der Angreifer nutzte die folgenden Mechanismen und Schwachstellen im zkLend-Vertrag aus, um den Wert der Sicherheit zu manipulieren:

  • Manipulation von lending_accumulator
    • Leerer Markt: Vor dem Angriff war der zkLend-Markt für wstETH-Token leer, was die perfekte Bedingung für die Manipulation schuf. Darüber hinaus erlaubt der zkLend Market-Vertrag jedem, beliebige Mengen von Vermögenswerten in einen leeren Markt einzuzahlen. Der Angreifer zahlte eine geringe Menge an Vermögenswerten ein, um den Wert des lending_accumulator erheblich aufzublähen.
    • Spendenmechanismus: Die Funktion flash_loan() des zkLend Market-Vertrags verfügt über einen einzigartigen Spenden-Mechanismus. Insbesondere wenn ein Benutzer einen Flash-Kredit zurückzahlt, berechnet der Market-Vertrag die überschüssigen zurückgegebenen Mittel und erhöht die globale Variable lending_accumulator, wodurch die Sicherheitenwerte für alle Benutzer im Vertrag verstärkt werden.
  • Manipulation von raw_balance
    • Rundungsverhalten: Die Division im Prozess der Verbrennung von Anteil-Tokens verwendet Abschneiden (Truncation), was zu einer Unterschätzung der Änderung des raw_balance des Benutzers bei Entnahmen führt.

Durch die Manipulation beider Variablen konnte der Angreifer den Sicherheitenwert auf über 7.000 wstETH erhöhen und andere Vermögenswerte vom Markt leihen, um Gewinne zu erzielen.

0x2.1 Manipulation der Variable lending_accumulator

0x2.1.1 Leere Markinitialisierung

Durch die Untersuchung des Transaktionsprotokolls des Market-Vertrags vor dem Angriff können wir beobachten, dass der Angreifer zunächst 1 wei wstETH in den wstETH Market-Vertrag eingezahlt hat. Bei Durchsicht der internen Aufrufe dieser Transaktion wird deutlich, dass der wstETH Market-Vertrag 0 wstETH hielt und die Gesamtmenge an zwstETH ebenfalls 0 betrug.

Daher können wir bestätigen, dass es im zkLend wstETH-Markt keine vorherigen Einzahlungen oder Kredite gab. Sowohl der reserve_balance als auch die ztoken_supply befanden sich in ihren Anfangswerten von 0, und der Anfangswert des lending_accumulator war 1. Dieses leere Marktszenario schuf die Voraussetzungen für den nachfolgenden Angriff und ermöglichte es dem Angreifer, den lending_accumulator mit einer minimalen Menge an wstETH erheblich zu verstärken.

0x2.1.2 Manipulation von lending_accumulator über Flash-Kredit

Als Nächstes ruft der Angreifer in dieser Transaktion die Funktion flash_loan() auf, leiht 1 wei wstETH und zahlt 1000 wei wstETH zurück. Die überschüssigen 999 wei werden als Spende behandelt und im reserve_balance des Vertrags verbucht.

Gemäß der Formel zur Berechnung des lending_accumulator erhöht diese Transaktion den lending_accumulator von 1 auf 851,0.

0x2.1.3 Wiederholte Ausführung von flash_loan()

Der Angreifer führt insgesamt 10 flash_loan()-Aufrufe durch, leiht jedes Mal nur 1 wei wstETH, zahlt aber einen größeren Betrag zurück. Infolgedessen steigt der lending_accumulator auf einen astronomischen Wert von 4.069.297.906.051.644.020 (4,069 × 10^18), was zufällig der Dezimalpräzision von wstETH entspricht.

0x2.2 Manipulation der Variable raw_balance

Nachdem der lending_accumulator auf etwa 4,069 × 10^18 manipuliert wurde, rief der Angreifer die Funktion deposit() des Market-Vertrags mit 4.069297906051644020 wstETH auf. Basierend auf dem neuesten Wert des lending_accumulator wurde das raw_balance des Angreifervertrags zu 2.

0x2.2.1 Die erste Transaktion zur Manipulation von raw_balance

In dieser Transaktion rief der Angreifer die Funktion callflashloandraaan() des Angreifervertrags auf. Obwohl dieser Vertrag nicht Open Source ist, kann basierend auf der internen Aufrufsprotokollierung spekuliert werden, dass die Logik dieser Funktion eine Schleife enthält, die folgende Aktionen durchführt:

  • Einzahlung: Der Angreifer zahlt eine bestimmte Menge wstETH in den Marktvertrag ein.
  • Entnahme: Der Angreifer entnimmt den spezifischen Betrag an wstETH.

Analyse der Token-Übertragungsaufzeichnungen

Es kann beobachtet werden, dass der vom Angreifer eingezahlte Betrag an wstETH immer ein ganzzahliges Vielfaches des lending_accumulator ist, zum Beispiel das 2-fache des Wertes (z. B. 8.13859) des lending_accumulator.

Der entnommene wstETH-Betrag ist jedoch das 1,5-fache des Wertes (z. B. 6.10394) des lending_accumulator.

Durch Berechnungen können wir feststellen, dass der entnommene wstETH-Betrag den eingezahlten Betrag übersteigt. Warum passiert das?

Rundungsverhalten

Bei Überprüfung der Implementierung der Methoden deposit() und withdraw() sehen wir, dass diese beiden Methoden die Prägung und Verbrennung von zwstETH beinhalten. So funktioniert es:

`mint()` Funktion im Market-Vertrag

`burn()` Funktion im Market-Vertrag

Die Prozesse mint() und burn() beinhalten beide eine Skalierungslogik. Die Skalierungslogik beinhaltet Ganzzahldivision mit Abrundung (Abrundung auf die nächste ganze Zahl), was eine Schlüsselrolle beim Exploit spielt.

Wenn der Angreifer eine bestimmte Menge zwstETH verbrennt, wird die Skalierungslogik angewendet. Aufgrund des manipulierten Wertes des lending_accumulator, der außergewöhnlich hoch ist (etwa 4.069.297.906.051.644.020), führt diese Division dazu, dass das raw_balance des Angreifers nur um 1 Einheit sinkt, obwohl über 6 zwstETH verbrannt werden.

Die Änderungen am raw_balance des Angreifers sind in der folgenden Tabelle zusammengefasst:

Wir können beobachten, dass der Angreifer in dieser Transaktion wiederholt die Einzahlung - Entnahme-Logik ausführt und dabei den Präzisionsverlust während der Funktion withdraw() ausnutzt, was zu einer Unterschätzung der Differenz des raw_balance führt. Letztendlich stieg das raw_balance des Benutzers von 2 auf 3, was eine zusätzliche Einheit einbrachte.

0x2.2.2 Nachfolgender Angriffsprozess

Nachfolgende Angriffstransaktionen folgten dem gleichen Muster wie der erste Angriff: Der Angreifer durchlief wiederholt Einzahlungs - Entnahme-Transaktionen, um wstETH zu erwerben.

Das erworbene wstETH wird wieder in den Markt eingezahlt, wodurch das raw_balance weiter erhöht wird, was dazu führt, dass der Wert der Sicherheiten des Angreifers weiter steigt.

Beispielhafte Erläuterung

Wir verwenden die folgende Transaktion als Veranschaulichung.

  • Es wurden insgesamt 30 Einzahlungen getätigt, wobei jedes Mal 4,069 wstETH eingezahlt wurden.
  • Es wurden insgesamt 30 Entnahmen getätigt, wobei jedes Mal 6,104 wstETH entnommen wurden.
  • Nach diesem Zyklus konnte der Angreifer laut Berechnungen 61,39 wstETH entnehmen.

Darüber hinaus ist anzumerken, dass zwischen diesen Angriffstransaktionen mehrere increase()-Methoden aufgerufen wurden. Diese Methoden wurden verwendet, um eine bestimmte Menge wstETH vom Konto des Angreifers an den Angreifervertrag zu überweisen, der dann die Mittel für nachfolgende Einzahlungen in den Market-Vertrag bereitstellte.

Diese Aktionen steigern den Wert von raw_balance und ermöglichen es dem Angreifer, den Sicherheitenwert weiter zu erhöhen. Schließlich erreichte das raw_balance des Angreifers 1.724 mit einem Wert von 7.015,4 wstETH, was ausreichte, um andere Vermögenswerte vom Markt zu leihen.

0x3 Gewinnanalyse

0x3.1 Leihen anderer Finanzierungsarten

Nach der Manipulation des Sicherheitenwertes lieh der Angreifer andere Arten von Geldern vom Markt und fuhr mit den folgenden Transaktionen fort (Auszug):

0x3.2 Überbrücken der geliehenen Gelder auf Layer1

Bei der Überprüfung der Bridge-Transaktionen des Angreifervertrags kann beobachtet werden, dass der Angreifer einen Teil der geliehenen Gelder auf Layer 1 überbrückte.

0x4 Fazit

Zusammenfassend unterstreicht dieser Angriff auf das zkLend-Protokoll mehrere wichtige Implikationen für das Design und die Sicherheit von dezentralen Kreditprotokollen:

  • Markinitialisierung und Bedingungen für die Einzahlung von Vermögenswerten: Der anfänglich leere Markt ermöglichte es dem Angreifer, eine geringe Menge wstETH einzuzahlen und den lending_accumulator zu manipulieren, wodurch er einen Hebel für den Exploit erhielt. Die Sicherstellung einer ausreichenden Liquiditätsbasis oder die Begrenzung von Vermögensspenden in frühen Marktphasen könnte helfen, ähnliche Angriffe zu verhindern.
  • Wichtigkeit korrekter Akkumulatormechanismen: Der Angreifer nutzte den Spendenmechanismus in der Funktion flash_loan() aus, um den lending_accumulator zu manipulieren und die Sicherheitenwerte aller Benutzer aufzublähen. Protokolle mit akkumulatorbasierten Mechanismen sollten vor einer einfachen Manipulation von Skalierungsfaktoren geschützt sein.
  • Rundungsverhalten und Präzisionsverlust: Ein Rundungsproblem während der Verbrennung von zwstETH-Tokens führte zu Präzisionsverlust und Unterschätzung des raw_balance, was es dem Angreifer ermöglichte, das raw_balance zu manipulieren. Protokolle sollten eine höhere Präzision oder Validierungsprüfungen verwenden, um solche Exploits zu verhindern.

Dieser Vorfall unterstreicht erneut die Bedeutung von rechtzeitigen Benachrichtigungen bezüglich der Initialisierungs- und Betriebsstati sowie der proaktiven Gefahrenabwehr zur Minderung potenzieller Verluste.

Referenz

[1] https://zklend.com/

[2] zkLends Sicherheitsvorfall Post-Mortem: https://drive.google.com/file/d/10i1dh_J89tPPw7KRcmFIVM6iNrJZAyfi/view

Sign up for the latest updates
~$15.9M Lost: Trusted Volumes & More | BlockSec Weekly
Security Insights

~$15.9M Lost: Trusted Volumes & More | BlockSec Weekly

This BlockSec bi-weekly security report covers 11 notable attack incidents identified between April 27 and May 10, 2026, across Sui, Ethereum, BNB Chain, Base, Blast, and Berachain, with total estimated losses of approximately $15.9M. Three incidents are analyzed in detail: the highlighted $1.14M Aftermath Finance exploit on Sui, where a signed/unsigned semantic mismatch in the builder-fee validation allowed an attacker to inject a negative fee that was converted into positive collateral during settlement; the $5.87M Trusted Volumes RFQ authorization mismatch on Ethereum; and the $5.7M Wasabi Protocol infrastructure-to-contract-control compromise across multiple EVM chains.

Newsletter - April 2026
Security Insights

Newsletter - April 2026

In April 2026, the DeFi ecosystem experienced three major security incidents. KelpDAO lost ~$290M due to an insecure 1-of-1 DVN bridge configuration exploited via RPC infrastructure compromise, Drift Protocol suffered ~$285M from a multisig governance takeover leveraging Solana's durable nonce mechanism, and Rhea Finance incurred ~$18.4M following a business logic flaw in its margin-trading module that allowed circular swap path manipulatio

~$7.04M Lost: GiddyDefi, Volo Vault & More | BlockSec Weekly
Security Insights

~$7.04M Lost: GiddyDefi, Volo Vault & More | BlockSec Weekly

This BlockSec weekly security report covers eight attack incidents detected between April 20 and April 26, 2026, across Ethereum, Avalanche, Sui, Base, HyperLiquid, and MegaETH, with total estimated losses of approximately $7.04M. The highlighted incident is the $1.3M GiddyDefi exploit, where the attacker did not break any cryptography or use a flash loan but simply replayed an existing on-chain EIP-712 signature with the unsigned `aggregator` and `fromToken` fields swapped out for a malicious contract, demonstrating how partial signature coverage turns any historical signature into a generic permit. Other incidents include a $3.5M Volo Vault operator key compromise on Sui, a $1.5M Purrlend privileged-role takeover, a $413K SingularityFinance oracle misconfiguration, a $142.7K Scallop cross-pool index injection, a $72.35K Kipseli Router decimal mismatch, a $50.7K REVLoans (Juicebox) accounting pollution, and a $64K Custom Rebalancer arbitrary-call exploit.