Eingehende Analyse: Die Balancer V2-Sicherheitslücke

Am 3. November 2025 wurden Balancer V2 und mehrere Forked-Projekte ausgenutzt, wodurch ein Schaden von über 125 Millionen Dollar entstand. Dieser Blog bietet eine eingehende technische Analyse des Vorfalls.

Eingehende Analyse: Die Balancer V2-Sicherheitslücke

Aktualisiert am 6. November 2025: Balancer hat seinen offiziellen vorläufigen Bericht [6] veröffentlicht, der die in unserer Analyse festgestellte Ursache bestätigt.

Am 3. November 2025 wurde Balancer V2's Composable Stable Pools zusammen mit mehreren Forked-Projekten über mehrere Chains hinweg von einem koordinierten Exploit betroffen, der zu einem Gesamtschaden von über 125 Millionen Dollar führte. BlockSec hat zum frühesten Zeitpunkt eine Warnung herausgegeben [1] und anschließend eine erste Analyse veröffentlicht [2].

Es handelte sich um einen äußerst raffinierten Angriff. Unsere Untersuchung ergab, dass die Ursache eine Preismanipulation war, die aus einem Präzisionsverlust bei der Berechnung der Invariante resultierte, was wiederum die BPT (Balancer Pool Token)-Preisberechnung verzerrte. Diese Manipulation der Invariante ermöglichte es dem Angreifer, durch einen einzigen Batch-Swap von einem bestimmten stabilen Pool zu profitieren. Obwohl einige Forscher aufschlussreiche Analysen vorgelegt haben, sind bestimmte Interpretationen irreführend, und die Ursache und der Angriffsprozess sind noch nicht vollständig geklärt. Ziel dieses Blogs ist es, eine umfassende und genaue technische Analyse des Vorfalls zu präsentieren.

Die wichtigsten Erkenntnisse (TL;DR)

Wurzelursache: Rundungsinkonsistenz und Präzisionsverlust

  • Bei der Hochskalierung wird unidirektional gerundet (Abrunden), während bei der Herunterskalierung bidirektional gerundet wird (Auf- und Abrunden).
  • Diese Inkonsistenz führt zu einem Präzisionsverlust, der, wenn er durch einen sorgfältig ausgearbeiteten Swap-Pfad ausgenutzt wird, gegen den Standardgrundsatz verstößt, dass die Rundung immer zugunsten des Protokolls erfolgen sollte.

Ausführung des Exploits

  • Der Angreifer hat die Parameter, einschließlich der Anzahl der Iterationen und der Eingabewerte, absichtlich so gestaltet, dass die Auswirkungen des Genauigkeitsverlusts maximiert werden.
  • Der Angreifer nutzte einen zweistufigen Ansatz, um sich der Entdeckung zu entziehen: Zunächst führte er den Kern des Exploits in einer einzigen Transaktion aus, ohne sofortigen Gewinn zu erzielen, und dann realisierte er den Gewinn, indem er die Vermögenswerte in einer separaten Transaktion abhob.

Auswirkungen auf den Betrieb und Verstärkung

  • Das Protokoll konnte aufgrund bestimmter Beschränkungen nicht angehalten werden [3]. Diese Unmöglichkeit, den Betrieb zu unterbrechen, verstärkte die Auswirkungen des Exploits und ermöglichte zahlreiche Folge- oder Nachahmungsangriffe.

In den folgenden Abschnitten werden wir zunächst wichtige Hintergrundinformationen zu Balancer V2 liefern, gefolgt von einer eingehenden Analyse der festgestellten Probleme und des damit verbundenen Angriffs.

0x1 Hintergrund

Der Composable Stable Pool von Balancer V2

Die betroffene Komponente bei diesem Angriff war der Composable Stable Pool [4] des Balancer V2-Protokolls. Diese Pools sind für Vermögenswerte konzipiert, von denen erwartet wird, dass sie nahezu eine 1:1-Parität beibehalten (oder zu einem bekannten Wechselkurs gehandelt werden), und ermöglichen große Swaps mit minimalen Preisauswirkungen, wodurch die Kapitaleffizienz zwischen gleichartigen oder korrelierten Vermögenswerten erheblich verbessert wird. Jeder Pool verfügt über ein eigenes Balancer Pool Token (BPT), das den Anteil des Liquiditätsanbieters am Pool zusammen mit den entsprechenden Basiswerten darstellt.

  • Dieser Pool verwendet Stable Math (basierend auf dem StableSwap-Modell von Curve), wobei die Invariante D den virtuellen Gesamtwert des Pools darstellt.
  • Der BPT-Preis kann wie folgt approximiert werden:

Aus der obigen Formel geht hervor, dass der BPT-Preis billiger erscheint, wenn D auf dem Papier kleiner gemacht werden kann (auch ohne tatsächlichen Verlust von Mitteln).

batchSwap() und onSwap()

Balancer V2 bietet die Funktion batchSwap(), die Multi-Hop-Swaps innerhalb des Vault [5] ermöglicht. Es gibt zwei Swap-Typen, die durch einen an diese Funktion übergebenen Parameter bestimmt werden:

  • GIVEN_IN ("Given In"): Der Aufrufer gibt den genauen Betrag des Eingabe-Tokens an, und der Pool berechnet den entsprechenden Ausgabebetrag.
  • GIVEN_OUT ("Given Out"): Der Aufrufer gibt den gewünschten Ausgabebetrag an, und der Pool berechnet den erforderlichen Eingabebetrag.

Normalerweise besteht ein batchSwap() aus mehreren Token-to-Token-Swaps, die über die Funktion onSwap() ausgeführt werden. Im Folgenden wird der Ausführungspfad skizziert, wenn einer SwapRequest ein GIVEN_OUT-Swap-Typ zugewiesen wird (beachten Sie, dass ComposableStablePool von BaseGeneralPool erbt):

Im Folgenden wird die Berechnung von amount_in für den Swap-Typ GIVEN_OUT gezeigt, die die Invariante D beinhaltet.

Skalierung und Rundung

Um die Berechnungen über verschiedene Token-Salden hinweg zu normalisieren, führt Balancer die folgenden zwei Operationen durch:

  • Upscaling: Skalierung von Salden und Beträgen auf eine einheitliche interne Genauigkeit, bevor Berechnungen durchgeführt werden.
  • Verkleinern: Konvertiert die Ergebnisse zurück in ihre ursprüngliche Genauigkeit und wendet dabei eine gerichtete Rundung an (z. B. werden Eingabebeträge in der Regel aufgerundet, um sicherzustellen, dass der Pool nicht zu wenig Ladung enthält, während Ausgabebeträge oft abgerundet werden).
Offensichtlich sind Hochskalierung und Herunterskalierung theoretisch gepaarte Operationen - Multiplikation bzw. Division. Bei der Durchführung dieser beiden Operationen besteht jedoch eine Inkonsistenz. Insbesondere die Herunterskalierung hat zwei Varianten oder Richtungen: divUp und divDown. Im Gegensatz dazu gibt es bei der Hochskalierung nur eine Richtung, nämlich mulDown.

Der Grund für diese Inkonsistenz ist unklar. Laut dem Kommentar in der Funktion _upscale() halten die Entwickler die Auswirkungen des Rundens in einer einzigen Richtung für minimal.

// Die Aufwärtsrundung würde nicht notwendigerweise immer in dieselbe Richtung gehen: Bei einem Tausch würde zum Beispiel der Saldo von // Token in sollte aufgerundet und der von Token out abgerundet werden. Dies ist die einzige Stelle, an der wir in // alle Beträge in die gleiche Richtung gerundet werden, da die Auswirkungen dieser Rundung voraussichtlich minimal sind (und es keinen // Rundungsfehler, es sei denn, _scalingFactor() wird außer Kraft gesetzt).

0x2 Schwachstellenanalyse

Das zugrundeliegende Problem ergibt sich aus der Abrundungsoperation, die während der Hochskalierung in der Funktion BaseGeneralPool._swapGivenOut() durchgeführt wird. Insbesondere rundet _swapGivenOut() swapRequest.amount durch die Funktion _upscale() fälschlicherweise ab. Der resultierende gerundete Wert wird anschließend als amountOut verwendet, wenn amountIn über _onSwapGivenOut() berechnet wird. Dieses Verhalten widerspricht der Standardpraxis, dass die Rundung in einer Weise angewendet werden sollte, die dem Protokoll zugute kommt.

Für einen bestimmten Pool (wstETH/rETH/cbETH) unterschätzt der berechnete amountIn daher den tatsächlich erforderlichen Einsatz. Dies ermöglicht es einem Nutzer, eine geringere Menge eines Basiswerts (z. B. wstETH) gegen einen anderen (z. B. cbETH) einzutauschen, wodurch die invariante D aufgrund der geringeren effektiven Liquidität sinkt. Folglich wird der Preis des entsprechenden BPT (wstETH/rETH/cbETH) deflationiert, da BPT-Preis = D / Gesamtangebot.

0x3 Angriffsanalyse

Der Angreifer führte einen zweistufigen Angriff aus, wahrscheinlich um das Entdeckungsrisiko zu minimieren:

  • In der ersten Phase wurde der Kern des Angriffs in einer einzigen Transaktion durchgeführt, die keinen unmittelbaren Gewinn brachte.
  • In der zweiten Phase realisierte der Angreifer Gewinne, indem er in einer separaten Transaktion Vermögenswerte abhob.

Die erste Phase kann in zwei weitere Phasen unterteilt werden: Parameterberechnung und Batch-Swap. Im Folgenden werden diese Phasen anhand eines Beispiels [Angriffstransaktion (TX) auf Arbitrum] (https://app.blocksec.com/explorer/tx/arbitrum/0x7da32ebc615d0f29a24cacf9d18254bea3a2c730084c690ee40238b1d8b55773) veranschaulicht.

Die Parameterberechnungsphase

In dieser Phase kombinierte der Angreifer Off-Chain-Berechnungen mit On-Chain-Simulationen, um die Parameter jedes Hops in der nächsten Phase (Batch-Swap) auf der Grundlage des aktuellen Zustands des Composable Stable Pools (einschließlich Skalierungsfaktoren, Verstärkungskoeffizient, BPT-Rate, Swap-Gebühren und anderer Parameter) genau abzustimmen. Interessanterweise setzte der Angreifer auch einen Zusatzvertrag ein, um diese Berechnungen zu unterstützen, was möglicherweise dazu diente, das Risiko des Front-Running zu verringern.

Zu Beginn sammelt der Angreifer grundlegende Informationen über den Zielpool, darunter die Skalierungsfaktoren der einzelnen Token, den Amplifikationsparameter, den BPT-Satz und den Prozentsatz der Swap-Gebühr. Anschließend wird ein Schlüsselwert namens trickAmt berechnet, der die manipulierte Menge des Ziel-Tokens angibt, mit der ein Präzisionsverlust herbeigeführt wird.

Der Skalierungsfaktor des Ziel-Tokens wird als sF bezeichnet und die Berechnung lautet wie folgt:

Um die in Schritt 2 der nächsten Phase (Batch Swap) verwendeten Parameter zu bestimmen, führte der Angreifer nachfolgende Simulationsaufrufe an die Funktion 0x524c9e20 des Hilfsvertrags mit den folgenden Aufrufdaten durch:

uint256[] balances; // Salden der Pool-Token (ohne BPT) uint256[] scalingFactors; // Skalierungsfaktoren für jeden Pool-Token uint tokenIn; // Index des Eingabe-Tokens für die Simulation dieses Hops uint tokenOut; // Index des Ausgangstokens für die Simulation dieses Hops uint256 amountOut; // Gewünschter Betrag des Ausgangs-Tokens uint256 amp; // Verstärkungsparameter des Pools uint256 fee; // Prozentsatz der Pool-Swap-Gebühr

Und die Rückgabedaten lauten:

uint256[] balances; // Pool-Token-Salden (ohne BPT) nach dem Swap

Der anfängliche Kontostand und die Anzahl der Iterationsschleifen wurden außerhalb der Kette berechnet und als Parameter an den Vertrag des Angreifers übergeben (angegeben als 100.000.000.000 bzw. 25). Bei jeder Iteration werden drei Swaps durchgeführt:

  • Swap 1: Schieben Sie den Betrag des Ziel-Tokens auf trickAmt + 1, unter der Annahme, dass die Swap-Richtung 0 → 1 ist.
  • Swap 2: Weiteres Auslagern des Ziel-Tokens mit trickAmt, was ein Abrunden im _upscale()-Aufruf auslöst.
  • Swap 3: Ausführen einer Swap-Back-Operation (1 → 0), bei der der zu tauschende Betrag aus dem aktuellen Token-Saldo im Pool abgeleitet wird, indem die beiden signifikantesten Dezimalziffern abgeschnitten werden, d. h. auf das nächste Vielfache von $10^{d-2}$ abgerundet wird, wobeid die Anzahl der Dezimalziffern ist. Zum Beispiel: 324.816 -> 320.000.
    • Beachten Sie, dass dieser Schritt gelegentlich aufgrund der in der StableMath-Berechnung verwendeten Newton-Raphson-Methode fehlschlagen kann. Um dies abzumildern, implementiert der Angreifer zwei Wiederholungsversuche, die jeweils einen 9/10-Rückgriff auf den ursprünglichen Wert verwenden. Der Hilfskontrakt des Angreifers ist von der StableMath-Bibliothek von Balancer V2 abgeleitet, was durch die Einbeziehung der benutzerdefinierten Fehlermeldungen im "BAL"-Stil deutlich wird.

Die Batch-Swap-Phase

Die batchSwap()-Operation kann in drei Schritte aufgeteilt werden:

  • Schritt 1: Der Angreifer tauscht BPT (wstETH/rETH/cbETH) gegen Basiswerte, um den Saldo eines Tokens (cbETH) genau an den Rand einer Rundungsgrenze (Betrag = 9) zu bringen. Damit werden die Voraussetzungen für den Präzisionsverlust im nächsten Schritt geschaffen.

  • Schritt 2: Der Angreifer tauscht dann zwischen einem anderen Basiswert (wstETH) und cbETH unter Verwendung eines manipulierten Betrags (= 8). Aufgrund der Abrundung bei der Skalierung von Token-Beträgen wird das berechnete Δx etwas kleiner (8,918 zu 8), was zu einem unterschätzten Δy und damit zu einer kleineren Invariante (D aus dem StableSwap-Modell von Curve) führt. Da der BPT-Preis = D / totalSupply ist, wird der BPT-Preis künstlich deflationiert.

  • Schritt 3: Der Angreifer tauscht die zugrundeliegenden Vermögenswerte wieder in BPT um, stellt das Gleichgewicht wieder her und profitiert gleichzeitig von dem deflationären BPT-Preis.

0x4 Angriffe und Verluste

In der nachstehenden Tabelle sind die Angriffe und die damit verbundenen Verluste zusammengefasst. Die Gesamtverluste belaufen sich auf über 125 Mio. $.

0x5 Schlussfolgerung

Bei diesem Vorfall handelte es sich um eine Reihe von Angriffstransaktionen, die auf das Balancer-V2-Protokoll und seine Forked-Projekte abzielten und zu erheblichen finanziellen Verlusten führten. Nach dem ersten Angriff wurden zahlreiche Folge- und Nachahmungstransaktionen über mehrere Ketten hinweg beobachtet. Aus diesem Vorfall lassen sich mehrere wichtige Lehren für das Design und die Sicherheit von DeFi-Protokollen ziehen:

  • Rundungsverhalten und Präzisionsverlust: Die unidirektionale Rundung (Abrunden) bei der Hochskalierung unterscheidet sich von der bidirektionalen Rundung (Auf- und Abrunden) bei der Herunterskalierung. Um ähnliche Schwachstellen zu vermeiden, sollten die Protokolle Arithmetik mit höherer Genauigkeit verwenden und robuste Validierungsprüfungen durchführen. Es ist wichtig, den Standardgrundsatz einzuhalten, dass die Rundung immer zugunsten des Protokolls erfolgen sollte.

  • Entwicklung der Ausnutzung: Der Angreifer führte einen ausgeklügelten zweistufigen Exploit aus, der darauf ausgelegt war, die Entdeckung zu umgehen. In der ersten Phase führte der Angreifer den Kern des Exploits innerhalb einer einzigen Transaktion aus, ohne sofortigen Gewinn zu erzielen. In der zweiten Phase realisierte der Angreifer Gewinne, indem er in einer separaten Transaktion Vermögenswerte abhob. Dieser Vorfall verdeutlicht einmal mehr das anhaltende Wettrüsten zwischen Sicherheitsforschern und Angreifern.

  • Operative Sensibilisierung und Reaktion auf Bedrohungen: Dieser Vorfall unterstreicht, wie wichtig rechtzeitige Warnungen über die Initialisierung und den Betriebsstatus sowie proaktive Mechanismen zur Erkennung und Verhinderung von Bedrohungen sind, um potenzielle Verluste durch laufende oder nachgeahmte Angriffe zu verringern.

Unter Aufrechterhaltung der Betriebs- und Geschäftskontinuität können Branchenteilnehmer BlockSec Phalcon als letzte Verteidigungslinie nutzen, um ihre Vermögenswerte zu schützen. Das BlockSec-Expertenteam steht bereit, um eine umfassende Sicherheitsbewertung für Ihr Projekt durchzuführen.

Referenz

[1] https://x.com/Phalcon_xyz/status/1985262010347696312

[2] https://x.com/Phalcon_xyz/status/1985302779263643915

[3] https://x.com/Balancer/status/1985390307245244573

[4] https://docs-v2.balancer.fi/concepts/pools/composable-stable.html

[5] https://docs-v2.balancer.fi/reference/swaps/batch-swaps.html

[6] https://x.com/balancer/status/1986104426667401241

Sign up for the latest updates