Willkommen zurück zu Sherlocks Schwachstellen-Spotlight, wo wir eine bedeutende Schwachstelle hervorheben, die während eines Sherlock-Audits entdeckt wurde. In dieser Woche untersuchen wir eine fehlerhafte Berechnung des Sicherheitenlevels, die im @plaza_finance Wettbewerb von @0xadrii, @KupiaSecurity, @f, @farman1094_ und @0xnovaman33 gefunden wurde.
Zusammenfassung der Schwachstelle: In getRedeemAmount(...) berechnet der Vertrag das Sicherheiteniveau für BOND-Einlösungen unter Verwendung des Nachhandelszustands: // aktueller Code (anfällig) collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISION) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); Da die Einlösungsbewertung dann dieses geschätzte Sicherheiteniveau verwendet, um zu entscheiden, ob der Höchstpreis (100 $) gezahlt wird, kann ein Angreifer BOND zu gemischten (manchmal rabattierten) Erstellungsraten kaufen, bis das (aktuelle) Sicherheiteniveau auf ≤ 1,2 sinkt, und dann alle BOND zum Höchstpreis von 100 $ einlösen – unter Verwendung eines manipulierten depositAmount, der das geschätzte Sicherheiteniveau wieder über 1,2 drückt. Dies ermöglicht es dem Angreifer, ETH („risikofreie“ Spanne) zu extrahieren, bis das aktuelle Sicherheiteniveau des Pools den Schwellenwert erreicht.
Angriffs Schritte: 1) Einrichtungsphase Beispielparameter für den Pool: - poolReserve = 120 ETH, bondSupply = 3000, levSupply = 200, ETH Preis = $3075 Preisregeln: Erstellen (kaufen) BOND: - Wenn collateralLevel ≤ 1.2: creationRate = tvl * 0.8 / bondSupply - Sonst: creationRate = $100 Einlösen (verkaufen) BOND: - Wenn geschätztes collateralLevel ≤ 1.2: redeemRate = tvl * 0.8 / bondSupply - Sonst: redeemRate = $100
2) Phase A – BOND zu gemischten/vergünstigten Preisen kaufen - Kaufen Sie BOND, während Sie den (aktuellen) collateralLevel = tvl / (bondSupply * 100) überwachen. - Der erste Kauf erfolgt, während der collateralLevel > 1,2 → minten Sie nahe $100 pro BOND. - Nachfolgende Käufe drücken den aktuellen collateralLevel unter 1,2 → minten Sie zu einem vergünstigten Erstellungsrate (z.B. ~$94,07 in), wodurch Sie einen großen BOND-Bestand günstig ansammeln.
3) Phase B – Alle BOND zum maximalen Preis einlösen - Rufe redeem(BOND, depositAmount = attackerBondBalance, ...) - Der Vertrag berechnet das geschätzte Sicherheitenlevel unter Verwendung des Post-Einlösestatus (bondSupply - depositAmount) und (tvl - depositAmount * 100) und (im Beispiel) erhält einen Wert > 1,2 - Da dieser geschätzte Wert die Schwelle überschreitet, wird der redeemRate auf maximal $100 festgelegt, was es dem Angreifer ermöglicht, alle angesammelten BOND zu $100 einzulösen.
4) Gewinnrealisierung - Der Unterschied zwischen rabattierten Käufen und $100 Verkäufen ergibt den Nettogewinn in ETH. - In den PoC-Zahlen: Ausgaben von 60 ETH für zwei Käufe bringen ~61,89 ETH bei der Einlösung → ~1,89 ETH Gewinn. - Der Angreifer kann iterieren, bis das aktuelle Pool-Kollateralniveau auf ~1,2 fällt, und dabei ungefähr extrahierbar: extrahierbares USD ≈ ethPrice × poolReserve − 120 × bondSupply
Was ist die Auswirkung? Direkte Mittelentnahme / Wertverlust: Angreifer mintet zu ermäßigten Preisen und löst für 100 $ ein, wodurch ETH aus dem Pool abgezweigt wird. Unbegrenzt bis zum Schwellenwert: Kann fortgesetzt werden, bis das aktuelle Sicherheiteniveau des Pools auf ≈ 1,2 sinkt. Systemische Preisinkonsistenz: Schafft Arbitrage, die ehrliche Nutzer über schlechtere Poolreserven/-ergebnisse bezahlen.
Die Grundursache: Verwendung des Nachhandelsstatus zur Preisgestaltung von Rücknahmen Der Code berechnet collateralLevel so, als ob die Rücknahme bereits stattgefunden hätte: // verwendet (tvl - depositAmount*100) und (bondSupply - depositAmount) collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISION) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); 1. Dies ermöglicht es einem Angreifer, depositAmount so zu wählen, dass der geschätzte Wert die Schwelle überschreitet (> 1,2), wodurch der $100 redeemRate freigeschaltet wird, selbst wenn der aktuelle Poolstatus dies nicht rechtfertigen würde. 2. Schwellenwertgating auf einer manipulierten Kennzahl Die Entscheidung über die $100-Obergrenze hängt von diesem manipulierbaren geschätzten Sicherheitenlevel ab, anstatt vom aktuellen (Vorhandels-)Level, was Preismanipulationen ermöglicht.
Die Minderung: Berechnen Sie die Rückzahlungspreise basierend auf dem aktuellen Poolzustand, nicht auf den geschätzten Salden nach der Rückzahlung. Der vorgeschlagene Fix des Projekts ist korrekt: - collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISION) - / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); + collateralLevel = (tvl * PRECISION) / (bondSupply * BOND_TARGET_PRICE);
Zusätzliche Härtung (empfohlen): 1. Preismonotonie: Sicherstellen, dass der Einlösungsbetrag nicht steigt, wenn der Einzahlungsbetrag steigt (kein "mehr verkaufen, besseren Einheitspreis erhalten"). 2. Invariant-basierte Preisgestaltung: Ableitung von Erstellen/Einlösen aus einer einzigen Invarianz, sodass die Kauf/Verkauf-Symmetrie einseitige Arbitrage verhindert. 3. Slippage-Prüfungen: Benutzerdefinierte Mindest-/Höchstpreise für sowohl Erstellen als auch Einlösen verlangen. 4. Obergrenze & Drosselung: Obergrenzen für Einlösungen pro Transaktion und pro Block, um Schäden zu begrenzen, wenn Schwellenwerte erreicht werden. 5. Konsistenz über Pfade: Erstellen- und Einlösungswege anpassen, um dieselbe Definition des Sicherheitenlevels zu verwenden (nur aktueller Zustand).
Wir sind stolz darauf, @plaza_finance durch diese Entdeckung abgesichert zu haben. Wenn es absolut sicher sein muss, ist Sherlock die richtige Wahl.
3,26K