Back to Blog

7,04 Mio. $ verloren: GiddyDefi, Volo Vault u.a. | BlockSec Weekly

Code Auditing
April 29, 2026
24 min read
Key Insights

Während der letzten Woche (19.04.2026 - 26.04.2026) hat BlockSec acht Angriffsvorfälle mit geschätzten Gesamtschäden von ca. 7,04 Mio. $ erkannt und analysiert. Die folgende Tabelle fasst diese Vorfälle zusammen, und detaillierte Analysen für jeden Fall werden in den folgenden Unterabschnitten bereitgestellt.

Datum Vorfall Typ Geschätzter Schaden
19.04.2026 Custom Rebalancer Contract Arbitrary Call ~64 Tsd. $
20.04.2026 REVLoans (Juicebox) Improper Validation ~50,7 Tsd. $
22.04.2026 Volo Vault / Navi Key Compromise ~3,5 Mio. $
22.04.2026 Kipseli Router Improper Validation ~72,35 Tsd. $
23.04.2026 GiddyDefi Incomplete Signature Validation ~1,3 Mio. $
25.04.2026 Purrlend Key Compromise ~1,5 Mio. $
26.04.2026 SingularityFinance Oracle Misconfiguration ~413 Tsd. $
26.04.2026 Scallop Accounting Flaw ~142,7 Tsd. $

Erste Schritte mit Phalcon Explorer

Tauchen Sie tief in Transaktionen ein, um klug zu handeln

Jetzt kostenlos testen

Wöchentliches Highlight: GiddyDefi

Der Angreifer knackte die Signatur nicht, verwendete keinen Flash-Loan und manipulierte keine Preise. Er spielte eine legitime Signatur mit vertauschten unsignierten Feldern gegen seinen eigenen Vertrag ab. "Benutze deine eigene Signatur gegen dich" ist die sauberste Demonstration dafür, wie eine partielle EIP-712-Abdeckung eine gültige Signatur in eine generische Genehmigung verwandelt.

Am 23. April 2026 wurde GiddyVaultV3 auf Ethereum für ca. 1,3 Mio. $ ausgenutzt. Das Signaturschema umfasste nur SwapInfo.data, wodurch aggregator, fromToken, toToken und amount außerhalb des EIP-712-Hashs lagen, sodass eine gültige Signatur mit manipulierten Feldern wiederverwendet werden konnte. Der Angreifer lenkte aggregator auf einen bösartigen Vertrag und fromToken auf den LP-Token der Strategie und entzog so ca. 1,3 Mio. $.

Hintergrund

GiddyVaultV3 (0x5f0a...4318) ist ein Yield-Farming-Vault-Vertrag, über den Benutzer Gelder über deposit() und withdraw() einzahlen und abheben. Jeder Vorgang muss eine von einem Backend signierte VaultAuth-Autorisierungsstruktur enthalten, die eine EIP-712-Signatur und ein SwapInfo[]-Array mit den Token-Swap-Routen enthält. Bei der Ausführung eines Swaps ruft der Vertrag GiddyLibraryV3.executeSwap() auf, der eine forceApprove auf swap.fromToken durchführt, die Genehmigung an swap.aggregator erteilt und dann den Swap über aggregator.call(swap.data) ausführt. Der Strategie-Vertrag verwaltet anschließend die Gelder gemäß seiner konfigurierten Strategie.

EIP-712 ist ein Standard zum Signieren strukturierter Off-Chain-Daten: das Protokoll, das die Signatur verwendet, rekonstruiert dieselbe Struktur On-Chain, hasht sie unter einem vereinbarten Domain-Separator und stellt die Adresse des Unterzeichners wieder her. Die Sicherheit eines jeden EIP-712-Flows hängt daher davon ab, dass der On-Chain-Hash alle Felder abdeckt, deren Wert die Ausführung beeinflusst. Im Design von Giddy signiert das Backend eine VaultAuth, die sowohl die Absicht des Benutzers als auch die Routing-Anweisungen für erforderliche Swaps enthält, und _validateAuthorization() rekonstruiert diese Struktur, um die Signatur zu überprüfen, bevor die Strategie Gelder bewegen darf.

Schwachstellenanalyse

Die Schwachstelle liegt in der Funktion _validateAuthorization() von GiddyVaultV3. Beim Erstellen der signierten Nutzlast wird nur das data-Feld jeder SwapInfo gehasht; aggregator, fromToken, toToken und amount sind alle von der Signatur ausgeschlossen. Das bedeutet, dass jeder, der im Besitz einer gültigen Signatur ist, die restlichen Felder von SwapInfo frei ersetzen kann, während die Signaturprüfung weiterhin bestanden wird.

Jedes ausgeschlossene Feld ist ein separater Hebel, den die Signatur frei lässt: aggregator wird sowohl zum Spender als auch zum Aufrufziel über forceApprove und aggregator.call(swap.data); fromToken wählt aus, welcher Strategie-Asset genehmigt wird; amount legt die Genehmigungs-Obergrenze fest; toToken dient nur einer returnAmount > 0-Prüfung. Die signierten data schränken keines davon ein, da keines dieser Ziele darin referenziert wird.

Angriffsanalyse

Die folgende Analyse basiert auf der Transaktion 0x5edb66...5482e5.

  • Schritt 1: Der Angreifer erhielt eine legitim vom Backend autorisierte VaultAuth-Signatur aus der Blockchain und behielt das data-Feld intakt. Da jeder vorherige deposit()- oder withdraw()-Aufruf die vollständige VaultAuth-Nutzlast auf der Blockchain sendete, war jede historische Transaktion eine kostenlose Quelle für eine wiederverwendbare Signatur; der Angreifer benötigte nur eine, deren data-Feld für den beabsichtigten Swap-Aufruf geeignet war.

  • Schritt 2: Mit der erhaltenen Signatur behielt der Angreifer die ursprüngliche signature, nonce und data unverändert bei, während er die restlichen Felder manipulierte. fromToken wurde auf den LP-Token gesetzt, den der Strategie-Vertrag hielt (ein echtes Asset), sodass forceApprove eine Genehmigung für einen Token erteilte, den das Protokoll tatsächlich hielt. aggregator wurde durch den bösartigen Vertrag des Angreifers ersetzt, sodass sowohl die Genehmigung als auch der anschließende aggregator.call() auf vom Angreifer kontrollierten Code gerichtet waren. Da diese Felder außerhalb des Signaturprüfungsbereichs lagen, akzeptierte _validateAuthorization() die manipulierte Struktur unverändert. Um die endgültige Prüfung require(returnAmount > 0, "SWAP_NO_TOKENS_RECEIVED") zu umgehen, implementierte der bösartige Aggregator eine Mint-Funktion, die gefälschte Token an das Protokoll zurückmintete und die Prüfung erfüllte, ohne einen echten Swap durchzuführen.

  • Schritt 3: Da dem bösartigen Aggregator in Schritt 2 eine Genehmigung erteilt worden war, rief der Angreifer transferFrom auf, um die LP-Token des Vaults direkt an den bösartigen Aggregator zu übertragen und so den Diebstahl abzuschließen. Dieser Schritt lag vollständig außerhalb des geschützten Ausführungspfads des Protokolls; als executeSwap() zurückkehrte, war die Genehmigung bereits geschrieben und die Nachkontrolle des Guthabens nach dem Aufruf bestanden, sodass das Protokoll keine weitere Möglichkeit hatte, einzugreifen.

Schlussfolgerung

Die Grundursache dieses Angriffs war die unvollständige EIP-712-Signaturabdeckung. Die Kernfelder von SwapInfo, die den Fluss der Gelder direkt steuern, blieben ungeschützt, sodass der Angreifer die Swap-Route und die Adresse des Aggregators austauschen konnte, während er eine gültige Signatur vorlegte. Entwickler, die externe Aggregatoren integrieren, sollten:

  • Sicherstellen, dass EIP-712-Signaturen alle Felder abdecken, die sich auf die Ausführungsergebnisse auswirken, einschließlich aggregator, fromToken, toToken und amount.

  • Eine Whitelist für Aggregatoren erzwingen, um Aufrufe an nicht geprüfte externe Verträge zu verhindern.

  • toToken auf erwartete Basistoken beschränken, um zu verhindern, dass gefälschte Token die Guthabenprüfungen umgehen.

Im Allgemeinen muss EIP-712 in jeder Architektur mit "Genehmigung dann Aufruf" jedes Feld hashen, das den resultierenden On-Chain-Status beeinflusst, nicht nur die benutzerorientierte Absicht. Immer wenn eine Backend-Signatur das alleinige Tor zwischen vom Benutzer bereitgestellten Parametern und einer privilegierten Vertragshandlung ist, müssen alle Parameter, die in diese Handlung einfließen (Aufrufziel, Asset, Betrag, Empfänger), innerhalb der signierten Struktur liegen. Die Behandlung von data als Proxy für die Identität des Aufrufs ist ein Kategorienfehler: die Identität des Aufrufs ist das Tupel aller seiner Parameter, und jeder Parameter, der außerhalb der Signatur verbleibt, wird per Definition von dem kontrolliert, der die Transaktion einreicht.

Bester Sicherheitsauditor für Web3

Validieren Sie Design, Code und Geschäftslogik vor dem Launch


Weitere Vorfälle diese Woche


Custom Rebalancer Contract

Am 19. April 2026 wurde ein sAVAX-Rebalancer-Vertrag auf Avalanche ausgenutzt, um ca. 64 Tsd. $ (~7.000 WAVAX) aus der Kreditdelegation eines Benutzers auf Aave V3 zu ziehen. Eine öffentliche Funktion führte einen beliebigen target.call(data) aus, während sie noch die Kreditdelegation des Benutzers hielt, sodass der Angreifer Aaves borrow() mit onBehalfOf auf das Opfer setzen konnte. Ein Whitehat-Bot hat den Exploit im Voraus ausgeführt und die Gelder vor jeder Auszahlung gerettet.

Hintergrund

Der Rebalancer-Vertrag (0x7a7b...a8c9) bietet eine Funktion b2a13230(), die dazu dient, die gehebelte Position eines Benutzers auf Aave neu auszubalancieren. Die Funktion operiert im Namen des Benutzers über die Aave V3-Kreditdelegation: der Benutzer erteilt dem Rebalancer die Erlaubnis, in seinem Namen zu leihen, und der Rebalancer kombiniert diese Kredite mit vom Benutzer bereitgestellten Geldern, um die Position anzupassen (z. B. ein Borrow + Supply-Workflow).

Schwachstellenanalyse

Die Grundursache ist, dass b2a13230() einen Schritt target.call(data) enthält, dessen Ziel und Calldata vom Aufrufer gesteuert werden. Dieser Aufruf erfolgt, während der Vertrag noch unter der Aave V3-Kreditdelegation des Benutzers operiert, sodass jede während dieses Schritts aufgerufene Logik die Kreditfähigkeit des Benutzers erbt. Es gibt keine Whitelist zulässiger Ziele und keine Formbeschränkung für die Calldata, sodass der Aufruf jede Vertragsmethode aufrufen kann, einschließlich Aaves borrow() mit dem Opfer als onBehalfOf.

Angriffsanalyse

Die folgende Analyse basiert auf der Transaktion: 0xaaa1b2...35001b.

  • Schritt 1: Der Angreifer führte einen Flash-Loan über eine Menge an sAVAX und USDC durch. Dann zahlte er den geliehenen USDC über den Rebalancer-Vertrag in Aave V3 ein, um eine ausreichende Besicherung für die Kreditaufnahme zu schaffen. In der Zwischenzeit wurde der geliehene sAVAX direkt an den Rebalancer-Vertrag übertragen, um den anschließenden Versorgungsschritt nach der Kreditaufnahme vorzubereiten.

  • Schritt 2: Der Angreifer rief die Funktion b2a13230() auf. Die Funktion führte zunächst eine normale Kreditaufnahme durch und erreichte dann den Abschnitt für beliebige Aufrufe. Zu diesem Zeitpunkt führte der Angreifer den Aufruf so aus, dass die borrow()-Funktion von Aave V3 direkt aufgerufen wurde, wobei das Opfer als onBehalfOf angegeben wurde. Da das Opfer dem Rebalancer-Vertrag eine Kreditdelegation erteilt hatte, war die Kreditaufnahme erfolgreich. Der geliehene WAVAX wurde in den Rebalancer-Vertrag übertragen.

  • Schritt 3: Der Angreifer rief die Funktion b2a13230() erneut auf und nutzte dabei den Rebalancer, um WAVAX in seinem eigenen Namen zu leihen. Der Vertrag nutzte dann den zuvor geliehenen WAVAX (der aus der Position des Opfers stammte), um ihn in die Position des Angreifers einzuspeisen und zurückzuzahlen, was es dem Angreifer ermöglichte, Gewinn zu erzielen.

Schlussfolgerung

Der Fehler ist die Kombination eines willkürlichen externen Aufrufs innerhalb eines privilegierten Kontexts, der über eine delegierte Kreditlinie verfügt. Jede Ebene allein wäre sicher: ein eingeschränkter externer Aufruf kann die Delegation nicht missbrauchen, und ein willkürlicher Aufruf ohne Delegation kann die Gelder des Benutzers nicht bewegen. Verträge, die Kreditlinien halten, sollten niemals einen willkürlichen externen Aufruf zulassen; wenn solche Aufrufe erforderlich sind, müssen Ziele einer Whitelist zugeordnet und die Calldata auf Form geprüft werden.


REVLoans (Juicebox)

Am 20. April 2026 wurde REVLoans, eine Kredit-Erweiterung auf Juicebox, auf Ethereum für ca. 50,7 Tsd. $ ausgenutzt. borrowFrom() akzeptierte eine vom Aufrufer bereitgestellte Buchhaltungsquelle, ohne zu überprüfen, ob sie im Protokoll registriert war; ein gefälschter 36-stelliger Kontext löste eine Same-Currency-Abkürzung aus, die die Guthaben um den Faktor 1e18 falsch skalierte. Zwei Transaktionen, eine zur Seed-Befüllung des aufgeblähten Buchhaltungseintrags und eine zur Kreditaufnahme gegen den legitimen Pool zum aufgeblähten Anteilspreis, entzogen 21,77 ETH.

Hintergrund

Juicebox ist ein hybrides Fundraising- und Kreditprotokoll auf Ethereum. Jedes Projekt hat seinen eigenen ERC20-Anteil-Token (hier als REV bezeichnet) und eine Kasse, die sich über einen oder mehrere Terminals verteilt, wobei ein Terminal der Vertrag ist, der physisch eine Teilmenge der Assets des Projekts verwahrt und als benutzerseitiger Ein-/Ausstiegspunkt dient. Ein Projekt kann mehrere Terminals, die in JBDirectory registriert sind, haben, und jedes (terminal, project, token)-Triple trägt einen JBAccountingContext, der die (decimals, currency) deklariert, die für die Buchhaltung dieses Tokens innerhalb dieses Terminals verwendet werden. REV ist daher ein Anspruch auf die Summe der Überschüsse über alle Terminals des Projekts, nicht ein Anspruch gegen einen einzelnen Terminal.

Ein Benutzer kann einen Asset in ein Terminal einzahlen im Austausch gegen neu geprägte REV, oder REV an einem Terminal für einen proportionalen Anteil am Überschuss einlösen (abzüglich einer konfigurierbaren Auszahlungssteuer, die etwas Wert für die verbleibenden Halter zurücklässt). REVLoans (0x2db6...1846), ein separater Vertrag, der darauf aufbaut, fügt eine Kreditfazilität hinzu: ein Benutzer verbrennt REV als Sicherheit und nimmt einen Kredit gegen eines der Terminals des Projekts auf, wobei der Kredit später im Austausch gegen die Neuprägung der Sicherheit zurückgezahlt werden kann. Der Kreditbetrag wird mit derselben Mathematik wie eine Einlösung bepreist, sodass eine Kreditaufnahme wirtschaftlich äquivalent zur Auszahlung derselben Sicherheit ist.

Der Anteilspreis von REV ist (totalSurplus + totalBorrowed) / (REV.totalSupply + totalCollateral). Die Einbeziehung von totalBorrowed im Zähler hält den Kredit-/Rückzahlungspreis neutral; sie bedeutet auch, dass ein aufgeblasener totalBorrowed den Anteilspreis direkt erhöht und einer kleinen Sicherheit eine unverhältlich hohe Auszahlung ermöglicht.

Schwachstellenanalyse

Die Grundursache ist eine nicht verifizierte Eingabe im Parameter source. borrowFrom() akzeptiert eine vom Aufrufer bereitgestellte REVLoanSource source (eine Struktur mit den Feldern .terminal und .token), ohne zu überprüfen, ob dieses Paar für die gegebene revnetId registriert ist. Beide Felder fließen direkt in die Auszahlungsmathematik ein, sodass der von source.terminal zurückgegebene Buchhaltungskontext vollständig vom Aufrufer gesteuert wird. Wenn das Feld currency dieses Kontexts mit dem des Zielterminals übereinstimmt, nutzt das Protokoll eine Same-Currency-Abkürzung, überspringt den Preis-Oracle und behandelt die bereitgestellten Dezimal- und Guthabenangaben als maßgeblich.

Die nicht validierte source wird dann von _addTo(), das ebenfalls keine Registrierungsprüfung durchführt, in _loanSourcesOf[revnetId] und totalBorrowedFrom[revnetId][source.terminal][source.token] geschrieben.

Sobald (source, revnetId) in der Buchhaltung steht, ist _borrowableAmountFrom() die Funktion, die eine Kreditierungsanforderung in einen zahlbaren Betrag übersetzt. Sie baut surplus = totalSurplus + totalBorrowed aus _totalBorrowedFrom() auf und übergibt diesen Überschuss dann zusammen mit der Anzahl der Sicherheiten des Aufrufers und dem Anteilsvorrat in JBCashOuts.cashOutFrom().

Der Dezimalfehler liegt eine Ebene tiefer, in _totalBorrowedFrom(). Er iteriert über _loanSourcesOf und faltet jeden Eintrag mittels mulDiv(tokensLoaned, 10**decimals, pricePerUnit). Auf dem Same-Currency-Pfad ist pricePerUnit = 10**decimals (die 18-stellige Präzision des Ziels), sodass die Formel auf tokensLoaned unverändert reduziert wird und ein unter 36-stelliger Buchhaltung gespeichertes Guthaben mit dem 18-stelligen ETH-Summe 1e18-mal zu groß landet.

Die Verstärkung erfolgt in cashOutFrom(). base = mulDiv(surplus, cashOutCount, totalSupply): bei surplus, das von aufgeblasenen totalBorrowed dominiert wird, entspricht selbst ein winziges cashOutCount (Sicherheit) einer unverhältlich großen Auszahlung.

Angriffsanalyse

Der Angriff verwendet zwei Transaktionen. Die erste verseucht die Buchhaltung von REVLoans: 0xc46cb7...dead1f. Die zweite entzieht den Pool gegen einen legitimen Terminal: 0x9adbd6...a8f938.

  • Schritt 1: Der Angreifer rief borrowFrom() mit terminal und token als Kreditquelle auf einen gefälschten Vertrag auf und hinterlegte eine kleine Menge REV als Sicherheit. REVLoans prüfte nicht, ob der bereitgestellte Terminal für das Revnet registriert war, noch ob der Token von ihm anerkannt wurde.
  • Schritt 2: REVLoans forderte einen Buchhaltungskontext vom gefälschten Terminal an, der ein gefälschtes (decimals=36, currency=ETH-Code(61166)) zurückgab. Da die Quell- und Zielwährungen übereinstimmten, nutzte REVLoans eine Same-Currency-Abkürzung und übersprang den Preis-Oracle, dann führte es die Auszahlungsmathematik über die realen ETH-Überschüsse der legitimen Terminals aus, die in der 36-stelligen Zieleinheit des Angreifers neu ausgedrückt wurden, und blähte die Zahl um den Faktor 1e18 auf.
  • Schritt 3: REVLoans registrierte (fake terminal, fake token) in _loanSourcesOf und schrieb die aufgeblasene Zahl in totalBorrowedFrom. Der gefälschte Terminal "zahlte aus", indem er den Empfang einfach bestätigte; es wurde kein echtes ETH bewegt. Die erste Transaktion endete mit manipuliertem totalBorrowed nach oben und nur dem verbrannten kleinen REV-Collateral.
  • Schritt 4: Der Angreifer rief erneut borrowFrom() auf, diesmal mit dem legitimen ETH-Terminal als Kreditquelle und einer winzigen REV-Sicherheit. Die Auszahlungsmathematik lief in echten 18-stelligen ETH-Einheiten.
  • Schritt 5: Bei der Berechnung von totalBorrowed iterierte REVLoans über _loanSourcesOf und stieß auf den Eintrag aus Schritt 3. Da die currency dieses Eintrags immer noch mit ETH übereinstimmte, feuerte die Same-Currency-Abkürzung erneut und die gespeicherten 36-stelligen Guthaben wurden 1e18-mal zu groß in die 18-stellige ETH-Summe gefaltet. totalBorrowed wurde nun von gefälschten Schulden dominiert, und der Zähler des Anteilspreises war stark aufgebläht.
  • Schritt 6: Die Auszahlungsmathematik lieferte einen Kreditbetrag, der auf den aufgeblähten Zähler abgestimmt war, den der Angreifer knapp unter dem realen Überschuss des legitimen Terminals voreingestellt hatte. Der legitime Terminal zahlte ihn aus und entzog fast den gesamten Pool an den Angreifer.

Schlussfolgerung

Die Grundursache sind zwei sich überlagernde Lücken: (terminal, token)-Paare werden ohne Überprüfung der Revnet-Registrierung akzeptiert, und die Same-Currency-Abkürzung faltet Quellguthaben in die Zielsumme, ohne die Dezimalunterschiede zu normalisieren. Jede Lücke allein wäre weniger gefährlich; zusammen erlauben sie einem Aufrufer, einen willkürlichen totalBorrowedFrom-Eintrag einzufügen und ihn zum Nennwert auszuzahlen. Abhilfemaßnahmen: Überprüfen Sie (terminal, token) gegen die registrierten Terminals des Revnets und normalisieren Sie die Guthaben anhand der gespeicherten Dezimalgröße der Quelle, bevor Sie sie falten.


Volo Vault

Am 22. April 2026 verlor Volo, ein Yield-Vault auf Sui, der durch Routing von Benutzereinzahlungen in das Navi-Kreditprotokoll Zinsen erwirtschaftet, ca. 3,5 Mio. $, nachdem der private Schlüssel des Betreibers geleakt wurde. Der Vault-Vertrag hatte keinen Code-Fehler; der Angreifer führte einfach den legitimen Betreiberpfad mit gestohlenen Anmeldeinformationen aus und entzog Volos Navi-Einlagen.

Hintergrund

Volo ist der benutzerseitige Vault (0xcd86...27fefa); Navi ist das zugrunde liegende Kreditprotokoll. Der Vault hält eine Navi AccountCap (ein Sui-Berechtigungs-Objekt, das Auszahlungen vom Navi-Konto von Volo autorisiert) und delegiert Strategiebewegungen an eine Betreiberrolle. Um auf Navi einzuzahlen oder abzuheben, ruft der Betreiber start_op_with_bag_v2() auf, um die AccountCap aus dem Vault in eine temporäre Tasche zu heben, und dann verwenden deposit_with_account_cap() / withdraw_with_account_cap_v2() diese Kappe, um Gelder zu bewegen.

Schwachstellenanalyse

Die Grundursache ist ein betrieblicher Fehler/ein Fehler bei der Schlüsselverwaltung und nicht eine vertragsbasierte Schwachstelle. Der Volo-Strategiepfad delegiert die Auszahlungsautorität an denjenigen, der den privaten Schlüssel des Betreibers hält: start_op_with_bag_v2() führt nur zwei Prüfungen durch (assert_operator_not_freezed(operation, cap) und assert_single_vault_operator_paired(operation, vault.vault_id(), cap)), die beide nur verifizieren, dass die bereitgestellte Berechtigung der registrierte Betreiber ist. withdraw_with_account_cap_v2() akzeptiert dann jeden Aufrufer, der die gehobene AccountCap vorlegen kann. Jeder, der im Besitz des privaten Schlüssels des Betreibers ist, kann daher denselben Pfad ausführen, den legitime Vorgänge verwenden, und zwar ununterscheidbar.

Angriffsanalyse

Die folgende Analyse basiert auf der Transaktion AQw9wM...3RUS.

  • Schritt 1: Der Angreifer rief start_op_with_bag_v2 auf @volosui/volo-vault::operation mit dem geleakten Betreiber-Schlüssel auf und hob die Navi AccountCap in eine temporäre Tasche.
  • Schritt 2: Der Angreifer verwendete bag::remove, um die AccountCap aus der temporären Tasche zu extrahieren.

  • Schritt 3: Der Angreifer rief withdraw_with_account_cap_v2 auf @navi-protocol/lending::incentive_v3 mit der extrahierten AccountCap auf und zog Volos Einlagen von Navi ab.

  • Schritt 4: Der Angreifer verwendete bag::add, um die AccountCap zurückzulegen, schloss die Operation und überwies die Gelder ab.

Schlussfolgerung

Der Defekt ist strukturell: ein Betreiber-Schlüssel, volle Auszahlungsbefugnis, keine zweite Prüfung. Drei Änderungen reduzieren den Schaden durch eine Schlüsselkompromittierung. Die Aufteilung der Betreiberrolle in ein Multisig- oder Schwellenwertschema bedeutet, dass ein geleakter Schlüssel nicht allein eine Auszahlung autorisieren kann. Das Hinzufügen einer Zeitschleife für ausgehende Auszahlungen gibt ungewöhnlichen Anrufen ein anfechtbares Fenster vor der Abrechnung. Die Eingrenzung der Befugnisse des Betreibers auf Einzahlung und Neuausgleich, wobei benutzerspezifische Auszahlungen über einen separaten Pfad geleitet werden, verhindert, dass die Betreiberrolle überhaupt an die Gelder des Benutzers gelangt.


Kipseli Router

Am 22. April 2026 wurde der Kipseli Router auf Base für ca. 72,35 Tsd. $ ausgenutzt. Der Router verwendet ein Angebot, das von einem externen Nur-USDC-Quoter zurückgegeben wird, als rohen Ausgabetoken-Überweisungsbetrag, ohne zu überprüfen, ob der Ausgabetoken dem Quoted-Token entspricht. Ein Angreifer tauschte 0,04 WETH gegen cbBTC auf einem Pfad, den der Quoter tatsächlich nicht unterstützt, und erhielt den USDC-skalierten Rückgabewert des Quoters (92.610.395) als rohe cbBTC-Einheiten (≈0,926 cbBTC).

Hintergrund

Kipseli Router (0x579f...9a07) ist ein Swap-Ausführungsvertrag, der auf einem externen Quoting-System basiert. Der Vertrag ist nicht Open-Source; die folgende Analyse basiert auf seinem dekompilierten Bytecode, weshalb die Funktionsnamen als 4-Byte-Selektoren erscheinen (0xcce096f3(), 0x592(), 0xd88()). Anstatt Swap-Preise direkt aus On-Chain-AMM-Pools zu berechnen, fragt er den Quoter nach einem Ausgabebetrag (amountOut) und führt dann die Token-Übertragung basierend auf diesem Wert aus. Im Normalbetrieb sendet der Benutzer tokenIn an das Protokoll-Wallet, und der Router zieht tokenOut aus demselben Wallet und leitet es an den Empfänger weiter. Das Protokoll ist mit einem einzigen QUOTE_TOKEN konfiguriert, und die Quoting-Logik ist in USDC mit 6-stelliger Buchhaltung denominiert; das System ist nur für USDC-denominierte Angebote konzipiert.

Schwachstellenanalyse

Der Defekt erstreckt sich über zwei Schichten, die sich gegenseitig verstärken. Auf der Router-Seite ruft die Funktion 0xcce096f3() ein Angebot v0 über die Quoter-Funktion 0x592() ab und übergibt es unverändert an 0xd88() als tokenOut.transferFrom(_wallet, receiver, v0). Der Router prüft niemals, ob tokenOut dem QUOTE_TOKEN des Protokolls entspricht, sodass ein USDC-skalierter Wert (6-stellige Präzision) übertragen wird, als wäre er eine cbBTC-Menge (8-stellige Präzision). Auf der Quoter-Seite ist der zugrunde liegende PropAMM AMM ausschließlich für Token-zu-USDC-Paare konzipiert, akzeptiert aber nicht unterstützte Routing-Pfade (WETHcbBTC), ohne zurückzusetzen, ignoriert tokenIn stumm und gibt einen USDC-skalierten Wert zurück, als wäre der Swap gültig.

Angriffsanalyse

Die folgende Analyse basiert auf der Transaktion 0x96edee...3db3bb.

  • Schritt 1: Der Angreifer rief den Router mit tokenIn=WETH und tokenOut=cbBTC auf. Der zugrunde liegende AMM unterstützte diesen Pfad nicht, setzte aber nicht zurück, und der Quoter 0x592() gab einen USDC-skalierten Wert von 92.610.395 (≈92,61 USDC) zurück.
  • Schritt 2: Der Router verwendete diesen Wert direkt als cbBTC-Überweisungsbetrag. 0,04 WETH (≈95 $) flossen über transferFrom hinein; 92.610.395 rohe cbBTC-Einheiten (≈0,926 cbBTC, ≈72,35 Tsd. $) flossen aus dem Protokoll-Wallet an den Angreifer.

Schlussfolgerung

Der Exploit tritt auf, weil auf beiden Seiten des Quoter-Aufrufs zwei Annahmen unüberprüft bleiben. Der Quoter geht davon aus, dass seine Ausgabe in seinem eigenen 6-stelligen USDC-Rahmen verbraucht wird; der Router geht davon aus, dass alles, was der Quoter zurückgibt, in den angeforderten tokenOut-Einheiten denominiert ist. Jede Korrektur behebt den Fehler:

  • Beim Router: Assert tokenOut == QUOTE_TOKEN oder konvertieren Sie das USDC-skalierte Angebot über ein Orakel in tokenOut-Einheiten, bevor Sie es übertragen.

  • Beim Quoter: Rücksetzung bei Routing-Pfaden, deren Token nicht für den unterstützten Paar-Satz registriert sind, anstatt stillschweigend einen USDC-skalierten Fallback zurückzugeben.


Purrlend

Am 25. April 2026 verlor Purrlend, ein Kreditprotokoll auf HyperLiquid und MegaETH, ca. 1,5 Mio. $, nachdem ein privater Schlüssel kompromittiert wurde. Der Angreifer übernahm die Brückenrolle und prägte nicht gedeckte pTokens (Purrlends Aave-ähnliche Beleg-Token), nutzte diese pTokens dann als Sicherheit, um reale Assets aus dem Pool zu leihen.

Hintergrund

Purrlend (0x81d5...a702) ist ein Kreditprotokoll mit einem Aave-ähnlichen Abrechnungsmodell. Wenn Benutzer Assets in das Protokoll einzahlen, erhalten sie entsprechende pTokens, ähnlich wie Aaves aTokens, die ihre Einzahlungsposition repräsentieren und als Sicherheit für die Kreditaufnahme anderer Assets verwendet werden können.

Das Protokoll umfasst auch privilegierte Rollen, darunter pool admin, risk admin und bridge. Die Brückenrolle ist für die plattformübergreifende Buchhaltung vorgesehen: sie kann pTokens prägen, um Einzahlungen auf einer Gegenstelle abzubilden. Die anderen Admin-Rollen ändern Risikoparameter und konfigurieren ausleihbare Assets.

Schwachstellenanalyse

Der unmittelbare Auslöser war eine Kompromittierung eines privilegierten Schlüssels: Der Angreifer erhielt die Schlüssel, die die Admin- und Brückenrollen von Purrlend kontrollierten. Ein Designfehler auf Vertragsebene verstärkte das Leck: Der pToken-Mint-Pfad der bridge-Rolle ist nicht an einen verifizierbaren Nachweis einer Cross-Chain-Hinterlegung gekoppelt. Die Funktion erlaubt einem Aufrufer mit Brückenrolle, pTokens an jede Adresse in beliebiger Menge auszugeben, ohne zu prüfen, ob auf der Quellseite eine entsprechende Einzahlung stattgefunden hat. Überall sonst im Protokoll werden pTokens als gültige Sicherheit behandelt, und der Kreditpfad prüft die Deckung zum Zeitpunkt der Kreditaufnahme nicht erneut. Eine nicht autorisierte Brückenrollen-Mintung übersetzt sich daher direkt in Kreditkapazität, ohne ein zweites Tor zwischen Minting und Asset-Auszahlung.

Angriffsanalyse

Die folgende Analyse basiert auf der Transaktion 0xb96cff...dbbf24 auf MegaETH.

  • Schritt 1: Der Angreifer, der über kompromittierte privilegierte Schlüssel verfügte, verwendete einen MultiSendCallOnly-Batch über GnosisSafeProxy, um sich selbst als pool admin, risk admin, bridge und emergency admin über ACLManager einzurichten, aktivierte dann WETH als ausleihbares Asset und setzte dessen BorrowCap auf 200.
  • Schritt 2: Als Brücke nutzte der Angreifer die Möglichkeit, eine große Menge pTokens an seine eigene Adresse zu prägen. Der Brücken-Mint-Pfad führte keine Überprüfung der Cross-Chain-Hinterlegung durch, sodass die neuen pTokens keine zugrunde liegenden Assets hatten, die sie deckten.

  • Schritt 3: Der Angreifer nutzte die nicht gedeckten pTokens als Sicherheit. Da der Kreditpfad jeden pToken-Saldo als gültige Einzahlungsposition behandelt, ohne die Deckung erneut zu prüfen, bestand die Sicherheitsprüfung und WETH wurde aus dem Pool geliehen.

Schlussfolgerung

Dies war eine Kompromittierung eines privaten Schlüssels, die durch einen Designfehler auf Vertragsebene verstärkt wurde. Die geleakten Schlüssel gaben dem Angreifer nur die beabsichtigte Autorität der Brückenrolle, aber diese Autorität beinhaltete ein unbeschränktes pToken-Minting, das sich direkt in ausleihbare Sicherheit übersetzt. Jede Ebene kann unabhängig gestrafft werden. Auf der Betriebsebene sollte die Brückenrolle in ein Multisig- oder Schwellenwertschema aufgeteilt werden, damit ein einzelner Schlüssel-Leak sie nicht ausüben kann. Auf der Vertragsebene sollte der Brücken-Mint eine verifizierbare Hinterlegungsprüfung tragen (z. B. eine Nachrichtenzusage eines vertrauenswürdigen Cross-Chain-Verifizierers) und bei fehlendem Nachweis zurücksetzen. Die Überprüfung des Nachweises zum Zeitpunkt des Mintings ist die haltbarere Lösung, da sie die Abhängigkeit von der Schlüsselverwaltung vollständig beseitigt.


SingularityFinance

Am 26. April 2026 verlor das dynBaseUSDCv3 Vault von SingularityFinance auf Base ca. 413 Tsd. $. Das Vault war mit einer ungültigen Uniswap V3-Gebührenstufe (42, die in V3 nicht existiert) konfiguriert, sodass das Preisorakel jedes Nicht-USDC-Assets zu einem nicht existierenden Pool aufgelöst wurde. Die Preisbildungsfunktion gab 0 stillschweigend zurück, anstatt zurückzusetzen, das Vault bewertete seine Nicht-USDC-Reserven mit Null, und ein Angreifer prägte fast die gesamte Anteilsupply, indem er eine winzige Menge USDC einzahlte, und löste dann die tatsächlichen zugrunde liegenden Assets ein.

Hintergrund

Das dynBaseUSDCv3 Vault (0x67b9...4dcd) hält mehrere ertragsgenerierende Token und bewertet Nicht-USDC-Reserven über Uniswap V3. Der Preisbildungshelfer getPrice(base, fee, quote, amount) löst das (base, quote, fee)-Tupel zu einem Uniswap V3-Pool über die Factory auf und liest dann den TWAP aus diesem Pool. Die totalAssets() des Vaults aggregiert die bewerteten Reserven; die Verhältnisse für die Prägung und Einlösung von Anteilen werden aus dieser Gesamtsumme abgeleitet.

Schwachstellenanalyse

Der Defekt liegt im frühen Rückgabepfad von getPrice(). Wenn IUniswapV3Factory.getPool(base, quote, fee) address(0) zurückgibt (kein Pool für die angegebene Gebührenstufe existiert), fällt die Funktion durch und gibt ihre null-initialisierte Variable price zurück, anstatt zurückzusetzen. Das Vault wurde mit fee=42 bereitgestellt, was keine der von Uniswap V3 unterstützten Stufen ist (500/3000/10000), sodass jeder Nicht-USDC-Token-Lookup diesen Pfad trifft. totalAssets() summiert sich daher nur ungefähr auf das USDC-Guthaben des Vaults, während die tatsächlichen ertragsgenerierenden Token null beitragen. Die Prägungs- und Einlösungsverhältnisse, die von totalAssets() abhängen, werden gegenüber diesem fast null-Nenner berechnet.

Angriffsanalyse

Die folgende Analyse basiert auf der Transaktion 0x00b949...8d3732.

  • Schritt 1: Der Angreifer führte einen Flash-Loan von ca. 100.000 USDC durch.

  • Schritt 2: Der Angreifer zahlte den USDC in das Vault ein. Da totalAssets() nur das USDC-Guthaben zählte, bewertete das Vault sich selbst ungefähr mit dem Einzahlungsbetrag und der Angreifer erhielt fast 100 % der Anteil-Supply.

  • Schritt 3: Der Angreifer löste die Anteile ein, was die zugrunde liegenden Reserven proportional zum Anteilseigentum verteilt. Der Angreifer erhielt einen großen Teil jedes ertragsgenerierenden Tokens, das das Vault hielt.

  • Schritt 4: Der Angreifer zahlte den Flash-Loan zurück und behielt die abgezogenen ertragsgenerierenden Token als Gewinn.

Schlussfolgerung

Zwei Prüfungen fehlten. Die Bereitstellung validierte fee=42 nicht gegen die unterstützten Stufen von Uniswap V3 (500/3000/10000); getPrice() gab 0 bei einem fehlenden Pool zurück, anstatt zurückzusetzen. Jede Korrektur ist ausreichend: Validieren Sie die Orakelparameter zur Konfigurationszeit oder setzen Sie bei getPool() == address(0) zurück. Als zusätzliche Sicherheit sollte die Logik zur Anteilprägung totalAssets() gegen einen externen Referenzwert prüfen, bevor Einzahlungen akzeptiert werden.


Scallop

Am 26. April 2026 verlor das Staking-Rewards-Programm von Scallop auf Sui ca. 142,7 Tsd. $. Die Funktion zur Aktualisierung der gutgeschriebenen Belohnungen eines Benutzers prüfte nicht, ob das übergebene Belohnungs-Tracking-Objekt mit dem Konto des Benutzers übereinstimmte, sodass ein Angreifer ein fiktives Punkteguthaben von einem verlassenen, lange ruhenden Belohnungs-Tracking-Objekt abrufen und gegen den legitimen Belohnungspool einlösen konnte, bis das Guthaben erschöpft war.

Hintergrund

Scallop ist ein Kreditprotokoll auf Sui. Neben seinem Kreditprodukt betreibt Scallop ein Spool-Programm: Benutzer zahlen einen einzelnen Asset in Scallops Markt ein, um MarketCoin<T> zu erhalten (die Kreditbeleg-Token; für SUI-Einzahlungen ist dies MarketCoin<SUI>, die On-Chain-Darstellung von "sSUI"), staken dann diese MarketCoin in einen Spool, um im Laufe der Zeit Protokollpunkte zu verdienen, die sie später gegen einen gepaarten RewardsPool gegen tatsächliche Belohnungstoken einlösen. Jeder Spool ist ein gemeinsam genutztes Sui-Objekt, das einen globalen Pro-Anteil-index verfolgt; jeder Benutzer besitzt ein persönliches SpoolAccount, das sein gestaktes Guthaben und die gutgeschriebenen points aufzeichnet.

Schwachstellenanalyse

Der Defekt liegt in spool::user::update_points: Die Funktion behauptet nicht account.spool_id == object::id(spool) (noch account.stake_type == spool.stake_type). Geschwistereinträge stake, unstake und redeem_rewards führen alle diese Bindungsprüfung beim Eintritt durch; nur update_points überspringt sie. Ohne die Prüfung kann spool_account::accrue_points account.points += stake * (spool.index - account.index) / 1e9 gegen jeden übergebenen Spool berechnen und dessen index so behandeln, als wäre er der eigene Belohnungsstrom dieses Kontos.

Der Pfad wird ausnutzbar, da Sui niemals gemeinsam genutzte Objekte sammelt: Ein verlassener Scallop-Spool, dessen stakes zu Staub zerfallen sind, sammelt weiterhin Belohnungsanteile (periodische Inkrementierung 1e9 * reward / stakes), sodass sein index im Laufe der Zeit kumulativ ansteigt und beliebig große Werte erreichen kann. Ohne die Bindungsprüfung kann update_points diesen aufgeblasenen index verwenden, um eine riesige Punkte-Delta in jedes Konto zu schreiben. Die verschmutzten points werden dann 1:1 gegen den RewardsPool des Ziel-Spools eingelöst, da das Konto legitim an diesen Ziel-Spool gebunden ist und die eigene Bindungsprüfung von redeem_rewards bestanden wird.

Angriffsanalyse

Die folgende Analyse basiert auf der Transaktion 6WNDjC...NfVL.

  • Schritt 1: Mit 0,2 SUI als Köder prägte der Angreifer MarketCoin<SUI>, rief dann new_spool_account + stake gegen den Ziel-Spool auf, um einen legitim gebundenen SpoolAccount mit account.spool_id = target_spool zu erstellen.

  • Schritt 2: Der Angreifer rief update_points<MarketCoin<SUI>>(donor_spool, account, clock) mit donor_spool als verlassenen Spool auf. Der index des Spenders (≈8.91e14) wurde als points in das Konto geschrieben: points = stake * (8.91e14 - 1.19e9) / 1e9 ≈ 1.62e14.

  • Schritt 3: Der Angreifer rief redeem_rewards<MarketCoin<SUI>, SUI>(target_spool, target_rp, account) auf. Die Bindungsassertionen akzeptierten das Ziel-gebundene Konto, die innere Re-Accrue gab früh zurück, und die verschmutzten points wurden zum 1:1-Satz des Belohnungspools bis zu seinem Guthaben umgerechnet: rewards = 150.098.061.595.978 rohe SUI.

  • Schritt 4: Der Angreifer rief unstake und redeem auf, um den 0,2-SUI-Köder zurückzuerhalten, dann TransferObjects, um alles zu verschieben.

Schlussfolgerung

Die Korrektur besteht darin, die gleiche assert!(account.spool_id == object::id(spool)) Prüfung am Eingang von update_points hinzuzufügen, die stake, unstake und redeem_rewards bereits durchführen. Als zusätzliche Sicherheit könnte das Protokoll auch die von einem einzelnen accrue_points-Aufruf akzeptierte index-Delta begrenzen ( Deltas größer als eine konfigurierte Obergrenze ablehnen), sodass auch wenn die Bindungsprüfung in Zukunft erneut umgangen würde, kein einzelner Aufruf einem Konto eine points-Menge gutschreiben könnte, die unverhältnismäßig zu seiner tatsächlichen Stake-Dauer ist.

Erste Schritte mit Phalcon Security

Erkenne jede Bedrohung, alarmiere, was wichtig ist, und blockiere Angriffe.

Jetzt kostenlos testen

Über BlockSec

BlockSec ist ein Full-Stack-Anbieter für Blockchain-Sicherheit und Krypto-Compliance. Wir entwickeln Produkte und Dienstleistungen, die unseren Kunden helfen, Code-Audits (einschließlich Smart Contracts, Blockchains und Wallets) durchzuführen, Angriffe in Echtzeit abzufangen, Vorfälle zu analysieren, illegale Gelder zu verfolgen und AML/CFT-Verpflichtungen über den gesamten Lebenszyklus von Protokollen und Plattformen zu erfüllen.

BlockSec hat mehrere Blockchain-Sicherheitsarbeiten auf renommierten Konferenzen veröffentlicht, mehrere Zero-Day-Angriffe auf DeFi-Anwendungen gemeldet, mehrere Hacking-Angriffe blockiert, um mehr als 20 Millionen Dollar zu retten, und Milliarden von Kryptowährungen gesichert.

Sign up for the latest updates
~$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.

The Decentralization Dilemma: Cascading Risk and Emergency Power in the KelpDAO Crisis
Security Insights

The Decentralization Dilemma: Cascading Risk and Emergency Power in the KelpDAO Crisis

This BlockSec deep-dive analyzes the KelpDAO $290M rsETH cross-chain bridge exploit (April 18, 2026), attributed to the Lazarus Group, tracing a causal chain across three layers: how a single-point DVN dependency enabled the attack, how DeFi composability cascaded the damage through Aave V3 lending markets to freeze WETH liquidity exceeding $6.7B across Ethereum, Arbitrum, Base, Mantle, and Linea, and how the crisis forced decentralized governance to exercise centralized emergency powers. The article examines three parameters that shaped the cascade's severity (LTV, pool depth, and cross-chain deployment count) and provides an exclusive technical breakdown of Arbitrum Security Council's forced state transition, an atomic contract upgrade that moved 30,766 ETH without the holder's signature.

Weekly Web3 Security Incident Roundup | Apr 13 – Apr 19, 2026
Security Insights

Weekly Web3 Security Incident Roundup | Apr 13 – Apr 19, 2026

This BlockSec weekly security report covers four attack incidents detected between April 13 and April 19, 2026, across multiple chains such as Ethereum, Unichain, Arbitrum, and NEAR, with total estimated losses of approximately $310M. The highlighted incident is the $290M KelpDAO rsETH bridge exploit, where an attacker poisoned the RPC infrastructure of the sole LayerZero DVN to fabricate a cross-chain message, triggering a cascading WETH freeze across five chains and an Arbitrum Security Council forced state transition that raises questions about the actual trust boundaries of decentralized systems. Other incidents include a $242K MMR proof forgery on Hyperbridge, a $1.5M signed integer abuse on Dango, and an $18.4M circular swap path exploit on Rhea Finance's Burrowland protocol.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit