Hintergrund

Im Mai 2026 wurde der EtherRouterCreate3 Vertrag des ShapeShift FOX Colony Projekts, der auf Arbitrum deployt wurde, angegriffen. Der Angreifer nutzte die Fähigkeit zur "willkürlichen Selbstaufruf" im Transaktionsmechanismus des Vertrags zusammen mit der automatischen Autorisierungslogik von DSAuth für address(this), um den auth Modifikator zu umgehen und die Kern-Routing-Komponente resolver durch eine bösartige Version zu ersetzen. Dadurch konnte er über delegatecall alle ERC20-Assets des Vertrags leeren. Der Angriff war im Grunde genommen ein vollständiger Berechtigungsumgehung, verursacht durch den semantischen Konflikt zwischen "Meta-Transaktionsmetasprache" und dem internen Selbstaufruf-Autorisierungsmodus.

Angriff Überblick

Ursache der Schwachstelle

Die beliebige Selbstaufruf-Funktion von executeMetaTransaction: address(this).call(callData) filtert keine sensiblen Selektoren.

Der Vertrag EtherRouter selbst ist eine auf Resolver basierende upgradefähige Proxy-Architektur: Für unbekannte Funktionsselektoren ruft fallback() resolver.lookup(msg.sig) auf, um die Implementierungsadresse zu finden und führt sie durch delegatecall aus. Die Meta-Transaktionsfunktion (executeMetaTransaction) wird durch den alten Resolver 0x7490022b0e44aa65c030ac0d6728382a29458fc5 zum Implementierungsvertrag 0x4e7f1e1e263678590007e89b7e129686ba7758d4 geleitet. Dieser Implementierungsvertrag ist nicht open-source, die folgenden Informationen basieren auf den Dekompilierungsergebnissen:

Essenz des Problems: Das Design von executeMetaTransaction soll es Benutzern ermöglichen, bestimmte nicht-sensible Operationen durch Signaturen auszuführen, aber es filtert nicht nach functionSignature. Angreifer können ihre eigenen gültigen Signaturen verwenden, um den Vertrag dazu zu bringen, setResolver (bösartige Adresse) aufzurufen.

Automatische Autorisierung von DSAuth.isAuthorized: src == address(this) wird freigegeben

EtherRouter.setResolver(address) ist durch den auth Modifikator geschützt und sollte nur vom Owner oder der Authority aufgerufen werden:

Aber in DSAuth.isAuthorized(address src, bytes4 sig) gibt es selbstaufrufende automatische Autorisierungslogik:

Wenn executeMetaTransaction durch address(this).call(setResolver(...)) die Selbstaufruf auslöst, ist msg.sender in setResolver der Vertrag selbst 0x5c59..., deshalb wird es von DSAuth automatisch freigegeben.

Im Einzelnen sind diese beiden Designs nicht unbedingt als offensichtliche Schwachstellen zu betrachten – die Selbstaufrufautorisierung ist in vielen Proxy-Modellen gängig, und die Meta-Transaktionsmechanik ist selbst auch vernünftig. Aber wenn beide Logiken gleichzeitig vorhanden sind, entsteht ein semantischer Konflikt: Die Fähigkeit zur 'beliebigen Selbstaufruf' durch die Meta-Transaktion trifft genau auf die 'Selbstaufruf ist vertrauenswürdig' Logik von DSAuth, was zusammen eine vollständige Berechtigungsumgehungskette ergibt.

Dynamisches Routing von delegatecall in EtherRouter.fallback(): Vollständige Kontrolle nach der Übernahme des Resolvers

Nach dem Austausch des Resolvers muss der Angreifer nur eine beliebige Funktionsauswahl auf EtherRouter aufrufen, die nicht existiert; fallback() wird bedingungslos auf die bösartige Implementierung des Angreifers delegiert.

Bösartiger Resolver und Drain-Implementierung: Funktionsmapping-Register ohne Berechtigung + address(this) Vermögenswerte leeren

Der Angreifer hat im Voraus zwei Verträge bereitgestellt:

Bösartiger Resolver

0x4e321af09012e15a67756522187c05b108b7ee0a (nicht open-source, dekompiliert):

Bösartige Drain-Implementierung

0x0b971e0a8ecc7d5b2465c903cf75aeaedbfc39e2 (nicht open-source, dekompiliert):

Angreifer Gewinnformel

Angriffsablauf

Der Angriffsprozess wird in einer einzigen Transaktion abgeschlossen, alle Logik wird im Konstruktor des temporären Angriffsvertrags 0x835a701fd76b96a76ee84de037d41f059ee29f5c ausgeführt.

Erste Phase: Bösartige Infrastruktur bereitstellen

  1. Der Angreifer EOA 0xeed236afb6967f74099a0a6bf078bc6b865fbf28 initiiert die Transaktion und erstellt den temporären Angriffsvertrag 0x835a701fd76b96a76ee84de037d41f059ee29f5c.

  2. Angreifer ruft den bösartigen Resolver 0x4e321af09012e15a67756522187c05b108b7ee0a mit set(bytes4,address) auf, um den drain(address,address) Selektor 0x837971e4 auf die bösartige Drain-Implementierung 0x0b971e0a8ecc7d5b2465c903cf75aeaedbfc39e2 zu mappen.

Zweite Phase: Über Meta-Transaktionen den Resolver übernehmen

  1. Der Angriffsvertrag ruft die executeMetaTransaction() des Opfervertrags 0x5c59d0ec51729e40c413903be6a4612f4e2452da auf. Da diese Funktion nicht im eigenen ABI von EtherRouter ist, geht der Aufruf in fallback() und wird durch den alten Resolver zur Meta-Transaktionsimplementierung 0x4e7f1e... geleitet.

  2. executeMetaTransaction überprüft die Signatur durch ecrecover, um den Angreifer EOA wiederherzustellen, die Signaturverifikation schlägt fehl (der Angreifer verwendet seine eigene gültige Signatur, nicht eine gefälschte Signatur).

  3. executeMetaTransaction konstruiert den Selbstaufruf calldata setResolver(0x4e321af...) und führt address(this).call(callData) aus. Zu diesem Zeitpunkt ist der Kontext der Opfervertrag, also msg.sender == 0x5c59.... DSAuth.isAuthorized() gibt true zurück, weil src == address(this), der Resolver wurde erfolgreich ersetzt.

Dritte Phase: Vermögenswerte über den gehackten Resolver leeren

  1. Der Angriffsvertrag ruft die drain(USDC, 0xeed236...) Funktion des Opfernvertrags auf. Diese Funktion befindet sich nicht im nativen ABI von EtherRouter und geht in fallback(). Der gehackte Resolver gibt die bösartige Drain-Implementierung 0x0b971e0... zurück, der Opfervertrag führt dann die delegatecall aus. Der bösartige Code fragt USDC.balanceOf(0x5c59...) ab und erhält 132704591501 (d.h. 132,704.591501 USDC) und ruft dann USDC.transfer() auf, um direkt an den Angreifer EOA zu überweisen.

  2. Der Angriffsvertrag ruft erneut drain(0xf929..., 0x835a701f...) auf, um auf die gleiche Weise die gestohlenen Zwischenmittel von 841086343608217839604694 Einheiten an den Angriffsvertrag zu überweisen.

  3. Der Angriffsvertrag verwendet Router 0x4752ba5dbc23f44d87826276bf6fd6b1c372ad24, um die gestohlenen Zwischenmittel im Pair 0x5f6ce0ca... gegen 1.949506469643782660 WETH zu tauschen, wobei WETH direkt an den Angreifer EOA überwiesen wird.

Gewinne sichern

Geldverfolgung

Durch SlowMist MistTrack Analyse der Angreifer EOA 0xeed236afb6967f74099a0a6bf078bc6b865fbf28:

  • Hauptspuren:

    • Relay.link — $4,368.08, DEX Aggregator für den Vermögenswechsel.

    • LI.FI — $137,073.66, Cross-Chain/DEX Aggregator.

Die vom Angreifer gestohlenen Mittel flossen in Spark.fi Saving; SlowMist MistTrack wird die Geldbewegungen der relevanten Adressen weiterhin überwachen.

Zusammenfassung

Die zentrale Lektion aus diesem Angriff ist: Wenn ein Vertrag sowohl die Semantik von 'beliebiger Selbstaufruf in Meta-Transaktionen' als auch 'automatische Autorisierung bei Selbstaufrufen' hat, entsteht eine vollständige Berechtigungsumgehungskette – das ist kein Einzelfehler im Code, sondern das unvermeidliche Ergebnis eines semantischen Konflikts zwischen Komponenten. Smart Contract-Entwickler müssen klare Grenzen für sensible Funktionen ziehen, wenn sie Meta-Transaktionen oder Relay-Mechanismen entwerfen, und mindestens eine Liste von nicht erlaubten Selektoren in executeMetaTransaction pflegen sowie vorsichtig mit der bedingungslosen Selbstaufrufautorisierung von src == address(this) umgehen. Das SlowMist-Sicherheitsteam empfiehlt den Projektteams, vor der Bereitstellung eine umfassende externe Sicherheitsprüfung durchzuführen.

Hinweis: Dieser Artikel ist eine technische Sicherheitsanalyse, dient nur zu Lernzwecken und stellt keine Investitionsberatung dar. Alle On-Chain-Daten stammen aus öffentlichen Informationen.