#3: KyberSwap-Vorfall: Meisterhafte Ausnutzung von Rundungsfehlern mit äußerst subtilen Berechnungen

KyberSwap-Vorfall: Meisterhafte Ausnutzung von Rundungsfehlern mit äußerst subtilen Berechnungen

#3: KyberSwap-Vorfall: Meisterhafte Ausnutzung von Rundungsfehlern mit äußerst subtilen Berechnungen

Am 23. November 2023 ereignete sich eine Reihe von Angriffen auf KyberSwap. Diese Angriffe führten zu einem Gesamtschaden von über 48 Millionen US-Dollar. Das grundlegende Problem ergab sich aus einer falschen Rundungsrichtung während des Reinvestitionsprozesses von KyberSwap. Dies führte anschließend zu fehlerhaften Tick-Berechnungen und schließlich zu einer doppelten Verbuchung der Liquidität.

Wir haben einen ausführlichen Bericht veröffentlicht: "Yet Another Tragedy of Precision Loss: An In-Depth Analysis of the KyberSwap Incident", der auf die Einzelheiten des Vorfalls eingeht. Für ein tieferes Verständnis empfehlen wir, die vollständige Analyse zu konsultieren. Im Folgenden geben wir eine prägnante Einführung in diesen Vorfall und heben ihn als einen der zehn größten Sicherheitsvorfälle des Jahres 2023 hervor.

Hintergrund

KyberSwap ist eine dezentrale automatisierte Market Maker (CLAMM)-Plattform. Um die Marktnachfrage nach konzentrierter Liquidität zu bedienen, wurde KyberSwap Elastic auf Basis von Uniswap V3 mit mehreren Verbesserungen, einschließlich der Reinvestitionskurve zur automatischen Aufstockung der Erträge aus der Liquiditätsbereitstellung, eingeführt.

1. Tick und Quadratwurzel des Preises

Ein Tick in Uniswap V3-ähnlichen CLAMMs wird verwendet, um den Preis diskret zu markieren, sodass LPs Liquidität innerhalb eines festen Bereichs anstelle des gesamten Bereichs bereitstellen können (daher der Begriff "konzentriert").

Liquidität kann in einem Bereich zwischen zwei beliebigen Ticks (die nicht nebeneinander liegen müssen) platziert werden, d.h. einem Paar von Tick-Indizes (ein unterer Tick und ein oberer Tick). Konkret ist der Preis jedes Ticks (bei einem ganzzahligen Index i) wie folgt definiert:

In der Praxis wird die Quadratwurzel des Preises (bezeichnet als sqrtP oder sqrtPrice) verwendet:

Es ist auch möglich, den aktuellen Tick basierend auf der aktuellen Quadratwurzel des Preises zu berechnen:

Offensichtlich gilt: Während für einen gegebenen Tick nur eine einzige Quadratwurzel des Preises berechnet wird, können mehrere Quadratwurzeln des Preises auf denselben Tick verweisen. Eine detailliertere Erklärung finden Sie in den Dokumenten von Uniswap V3 und KyberSwap.

2. Reinvestitionskurve

Uniswap V3-basierte CLAMMs leiden unter der Auslastung von Pool-Gebühren und erheblichen Gasgebühren, die für die Reinvestition erforderlich sind. Daher hat KyberSwap eine Reinvestitionskurve übernommen, um das Problem zu lösen.

Der Schlüssel zur Reinvestitionskurve liegt darin, dass die bei jedem Swap erhobenen Gebühren als zusätzliche Liquidität im Pool angesammelt werden, und zwar als "Reinvestitionsliquidität" innerhalb eines unendlichen Bereichs. Die Reinvestitionstoken werden an die LPs ausgegeben und die angesammelte Reinvestitionsliquidität wird entsprechend an die LPs zugewiesen. Außerdem nimmt die Reinvestitionsliquidität an Swaps und Preisberechnungen teil.

Der entsprechende Code für die oben genannten Berechnungen ist in der Funktion computeSwapStep im folgenden Code-Snippet des entsprechenden Pools zu sehen.

Es ist zu beachten, dass aufgrund der Reinvestitionsliquidität die liquidity in dieser Funktion eine Summe aus zwei Komponenten ist: baseL für die Basisliquidität und reinvestL für die angesammelte Liquidität für die Reinvestition.

3. Swap in KyberSwap

Die Implementierung der swap-Funktion des besprochenen KyberSwap-Pools kann abstrahiert als das folgende Diagramm dargestellt werden:

Die entscheidende Logik für die Tick-Berechnung liegt in der Schleife während des Swaps, wie im blauen Rechteck hervorgehoben. Insbesondere umfasst die Hauptlogik die Funktion computeSwapStep und die Funktion _updateLiquidityAndCrossTick. Erstere berechnet Schlüsselzustände wie Eingangs- und Ausgangsbeträge für den gegebenen Swap und nextSqrtP, während letztere Fälle behandelt, in denen ein Cross-Tick auftritt.

Traditionell spricht man bei steigendem Preis davon, dass sich der Tick nach rechts/oben verschiebt, andernfalls sagt man, der Tick bewegt sich nach links/unten.

Um die später zu erörternde Schwachstelle besser zu verstehen, ist es wichtig, die relevante Code-Logik der Funktion computeSwapStep zu untersuchen, wie in der folgenden Abbildung dargestellt:

Insbesondere berechnet die Funktion calcReachAmount die für den Zielpreis targetSqrtP benötigten Eingabetoken (Zeilen 50-57). Wenn der usedAmount größer ist als der specifiedAmount, wird der Tick nicht überschritten und nextSqrtP wird aus deltaL (d. h. der Delta-Liquidität, Zeilen 59-62) berechnet. deltaL wird mit der Funktion estimateIncrementalLiquidity ermittelt und der endgültige Preis nextSqrtP mit der Funktion calcFinalPrice (Zeilen 70-79) berechnet. Wenn weniger Eingabe benötigt wird, wird nextSqrtP auf den Preis des nächsten Ticks gesetzt, aber dieser Fall wird bei dem Angriff nicht genutzt.

Die oben genannten Schritte machen deutlich, dass, wenn der Tick nicht überschritten wird, die von computeSwapStep zurückgegebene nextSqrtP nicht größer sein sollte als die sqrtP des nächsten Ticks. Aufgrund der Abhängigkeit des Preises von der Liquidität (Basisliquidität und Delta-Liquidität) und dem Präzisionsverlust ist es dem Angreifer jedoch möglich, die nextSqrtP so zu manipulieren, dass sie größer ist, während der Tick nicht überschritten wird.

Schwachstellenanalyse

Die Ursache liegt in der fehlerhaften Tick-Berechnung, die durch die falsche Rundungsrichtung bei der Delta-Liquiditätsberechnung (d. h. der Funktion estimateIncrementalLiquidity) des SwapMath-Vertrags (der von der Funktion computeSwapStep aufgerufen wird) verursacht wird. Dies wiederum beeinflusst die spätere Tick-Berechnung fehlerhaft.

Interessanterweise stellen wir bei der Untersuchung des Kommentars in Zeile 188 (hervorgehoben durch das blaue Rechteck) fest, dass deltaL aufgerundet werden soll, um nextSqrtP abzurunden. deltaL wird jedoch aufgrund der Verwendung der Funktion mulDivFloor in Zeile 189 fälschlicherweise abgerundet. Folglich wird nextSqrtP ungenau aufgerundet.

Angriffsanalyse

Die Angreifer initiierten mehrere Angriffstransaktionen, wobei jede Transaktion mehrere Pools leerte. Der Einfachheit halber basiert die folgende Diskussion auf dem ersten Angriff innerhalb der Angriffstransaktion.

Die Kernangriffsl logik besteht aus den folgenden sechs Schritten:

  1. Leihen von 2.000 WETH per Flash Loan von AAVE.

  2. Tauschen von 6,850 WETH gegen 6,371 frxETH im Opferpool 0xfd7b. Dieser Schritt dient dazu, den aktuellen Tick und die currentSqrtP an einen Ort zu verschieben, an dem derzeit keine Liquidität vorhanden ist.

  • Die currentSqrtP scheint vom Angreifer zufällig gewählt zu werden, und der Swap stoppt genau bei diesem Preis.
  • Die Basisliquidität (baseL) ist nach diesem Schritt Null, aber die Reinvestitionsliquidität (reinvestL) ist nicht Null.
  1. Hinzufügen von Liquidität zum Pool und anschließendes Entfernen eines Teils der Liquidität. Dieser Schritt dient zur Kontrolle des Bereichs und der Gesamtliquidität auf einen gewünschten Wert.
  • Der Tick-Bereich wird basierend auf der currentSqrtP gewählt.
  • Die gewünschte Liquidität für den Angriff könnte aus dem Tick-Bereich abgeleitet werden, obwohl die entsprechende Berechnungslogik weiter untersucht werden muss.
  1. Tauschen von 387,170 WETH gegen 0,06 frxETH im Pool. Dieser Schritt dient dazu, den aktuellen Tick so zu manipulieren, dass nextTick == currentTick gilt. Insbesondere täuscht der Swap in Schritt 4 den Pool geschickt vor, dass der Tick 111.310 nicht überschritten wird. Tatsächlich ist die currentSqrtP jedoch größer als die sqrtP des Ticks 111.310.
  1. Tauschen von 0,06 frxETH gegen 396,244 WETH im Pool. Beachten Sie, dass die Tauschrichtung im Vergleich zum vorherigen Schritt umgekehrt ist. In diesem Schritt wird die Liquidität doppelt gezählt, um den Swap profitabel zu machen und den Pool folglich leerzuräumen.
  1. Zurückzahlen des Flash Loans und Ernten von 6,364 WETH und 1,117 frxETH.

Für eine eingehende Untersuchung verweisen wir auf unsere umfassende Analyse, die weitere Details mit Berechnungen und Abbildungen enthält.

Zusammenfassung

Das grundlegende Problem dieses Vorfalls liegt in der fehlerhaften Rundung während des Reinvestitionsprozesses von KyberSwap, die zu ungenauen Tick-Berechnungen und letztendlich zu einer doppelten Liquiditätsverbuchung führt. Dieser Vorfall unterstreicht die komplexe und schwer fassbare Natur von Präzisionsverlustproblemen in DeFi-Protokollen und stellt eine ernsthafte Herausforderung für die gesamte Community dar.

Dieser Angriff aus dem Jahr 2023 zeichnet sich durch seine Komplexität aus, mit außerordentlich nuancierten Berechnungen und dient als Paradebeispiel für die vielen präzisionsbezogenen Sicherheitsvorfälle, die die Community stark auf die Probe gestellt haben. Darüber hinaus gab der Angreifer nach umfangreichen Verhandlungen mit den Behörden eine provokativ gefärbte Nachricht an die Öffentlichkeit und forderte die vollständige Kontrolle über das Protokoll.

Lesen Sie weitere Artikel in dieser Reihe:

Sign up for the latest updates