Autor: Xiaobai
Bearbeitet von: Liz
Hintergrundübersicht
Im Ethereum-Ökosystem bietet der deterministische Generierungsmechanismus für Vertragsadressen Entwicklern Vorteile, bringt jedoch auch neue Angriffsflächen mit sich. In dieser Ausgabe werden wir die Angriffsstrategien und Abwehrmaßnahmen analysieren, die durch die Verwendung der Opcodes CREATE und CREATE2 bei gleichzeitiger Bereitstellung verschiedener Verträge an derselben Adresse zu unterschiedlichen Zeiten entstehen. Frühere Artikel zur Sicherheit von Smart Contracts sind in der Sammlung zu finden.
Vorwissen
Lass uns zunächst die zwei Regeln der Generierung von Ethereum-Adressen verstehen:
1. CREATE
CREATE ist der native Opcode zur dynamischen Bereitstellung von Smart Contracts in der Ethereum Virtual Machine (EVM). Seit dem Genesis-Block von Ethereum hängt jede Vertragsbereitstellung von diesem Mechanismus ab. Die Hauptmerkmale sind, dass die Adressengenerierung von der Nonce des Kontos des Deployers abhängt, daher ist die Adresse nicht deterministisch (kann vor der Bereitstellung nicht genau vorhergesagt werden).
Die von CREATE generierte Vertragsadresse wird durch die Adresse des Deployers und die Nonce der Adresse bestimmt:

2. CREATE2
CREATE2 ist der neue Opcode zur Erstellung von Verträgen, der im Hard Fork von Konstantinopel (Februar 2019) in Ethereum eingeführt wurde (EIP-1014). Im Gegensatz zu dem traditionellen CREATE können die Teilnehmer off-chain vor der Bereitstellung des Vertrags die Vertragsadresse vorab berechnen, was off-chain-Interaktionen (wie Statuskanäle) und komplexe Vertragsarchitekturen ermöglicht.
Die von CREATE2 generierte Vertragsadresse wird durch die folgenden vier Parameter bestimmt:

Bis hierhin sollte jeder die beiden Möglichkeiten zur Generierung von Vertragsadressen in Ethereum verstanden haben. Sorgfältige Leser könnten sich fragen, was passiert, wenn die berechnete Vertragsadresse bereits existiert? Darüber muss man sich keine Sorgen machen. In Ethereum, unabhängig davon, ob die Adresse durch CREATE oder CREATE2 generiert wurde, wird jede Adresse, die bereits on-chain existiert (ob externes Konto EOA oder Vertragskonto), von der EVM abgelehnt, einen Vertrag zu erstellen. Hier sind zwei Szenarien für Adresskonflikte:
1. Zieladresse ist ein externes Konto (EOA)
Regel: Wenn die Zieladresse ein bereits bestehendes EOA (z.B. die Benutzer-Wallet-Adresse) ist, wird die EVM den Vertragbereitstellungsantrag ablehnen.
Ergebnis: Transaktion fehlgeschlagen, Gas verbraucht, Vertrag wird nicht erstellt und überschreibt keine Daten an dieser Adresse.
2. Zieladresse ist ein Vertragskonto
Regel: Wenn die Zieladresse ein bereits bereitgestelltes Vertragskonto ist, wird die EVM ebenfalls den Bereitstellungsantrag ablehnen.
Ergebnis: Transaktion fehlgeschlagen, Gas verbraucht, der Code und die speichernden Daten des ursprünglichen Vertrags bleiben unverändert.
Natürlich gibt es Ausnahmen. Wenn die Zieladresse ein Vertrag ist und durch selfdestruct selbst zerstört wurde, kann an dieser Adresse ein neuer Vertrag neu bereitgestellt werden.
Bis hierhin sind die verschiedenen Eigenschaften der beiden Opcodes CREATE und CREATE2 vorgestellt worden. Schauen wir uns nun an, wie wir diese beiden Opcodes nutzen können, um eine Kombination aus Angriffen durchzuführen.
Beispiel für eine Schwachstelle

Schwachstellenanalyse
Dieser DAO-Vertrag implementiert einen grundlegenden Governance-Mechanismus: Der Owner überprüft und dokumentiert die Adresse des Proposal-Vertrags im Proposals-Array über die approve-Funktion. Jeder Benutzer kann anschließend über die execute-Funktion die genehmigten Vorschläge ausführen. Auf den ersten Blick scheint die Berechtigungssteuerung (nur der Owner kann Vorschläge genehmigen) zusammen mit der Ausführungsprüfung (Vorschläge müssen genehmigt und nicht ausgeführt sein) streng zu sein, aber es gibt tatsächlich einen versteckten logischen Fehler: Die Adresse des genehmigten Vorschlags könnte beim Ausführen auf völlig andere Vertragscodes verweisen. Der Angreifer kann den Angriff in drei Schritten durchführen:
1. Deploye einen normalen Vertrag, um Genehmigungen zu erhalten
Der Angreifer deployt zunächst einen Vertrag A, der die harmlose Funktion executeProposal() enthält, und fügt die Adresse durch die Genehmigung des Owners zur Vorschlagsliste hinzu.
2. Zerstöre den ursprünglichen Vertrag und beanspruche die Adresse
Vertrag A führt die Selbstzerstörungsoperation (selfdestruct) aus, um den Code zu löschen, danach verwendet der Angreifer den CREATE2-Opcode, um an derselben Adresse den bösartigen Vertrag B (der gefährliche Logik in executeProposal() enthält) bereitzustellen.
3. Trigger die Ausführung, übernehme die Kontrolle
Wenn der Benutzer execute aufruft, wird der Vertrag den Code des neu bereitgestellten bösartigen Vertrags B durch delegatecall ausführen. Da delegatecall den aktuellen Kontext des Vertrags beibehält, kann der Angreifer durch diese Operation den Status des DAO-Vertrags (z.B. den Owner ändern) oder Vermögenswerte transferieren.
Lass uns nun den spezifischen Angriffsprozess anhand des Angriffsvertrags betrachten.
Angriffsvertrag

Der Angriffsprozess ist wie folgt:
1. Alice hat den DAO-Vertrag deployt.
2. Evil deployt den DeployerDeployer-Vertrag, Adresse ist DD.
3. Evil ruft DD.deploy() auf, um den Deployer mit CREATE2 an Adresse D (fester salt) bereitzustellen.
4. Rufe D.deployProposal() auf, um die Adresse des Proposal-Vertrags P zu erstellen, zu diesem Zeitpunkt ist D's Nonce 0.
5. Alice genehmigt die Adresse P.
6. Der Angreifer ruft D.kill() auf, um D zu zerstören. Zu diesem Zeitpunkt wurde das Konto D gelöscht, einschließlich der Nonce, die ebenfalls auf 0 zurückgesetzt wurde.
7. Der Angreifer ruft erneut DD.deploy() auf, um den Deployer an Adresse D (die gleichen CREATE2-Parameter) neu bereitzustellen.
8. Rufe D.deployAttack() auf, da zu diesem Zeitpunkt D's Nonce auf 0 zurückgesetzt wurde und D's Adresse unverändert bleibt, ist die Adresse des erstellten Attack-Vertrags identisch mit der vorherigen Proposal-Vertragsadresse P.
9. Zu diesem Zeitpunkt gibt es einen Vorschlag im Proposals-Array des DAO, der auf die Adresse P verweist, aber jetzt wurde die Adresse P bereits als Angriffsvertrag neu bereitgestellt. Daher wird beim Aufruf von execute() die executeProposal() von Attack ausgeführt, was schließlich durch delegatecall den Owner des DAO auf msg.sender ändert, also auf den Angriffsvertrag.
Die Angriffsprinzipien können zusammengefasst werden, dass der Angreifer die Möglichkeit nutzt, mit CREATE2 einen Vertrag an derselben Adresse neu bereitzustellen, indem er zunächst einen legitimen Proposal-Vertrag deployt und diesen vom DAO genehmigen lässt. Danach wird der Deployer-Vertrag zerstört, der Deployer an derselben Adresse neu bereitgestellt, die Nonce dieser Adresse zurückgesetzt, und schließlich wird ein bösartiger Vertrag an derselben Adresse wie der vorherige Proposal erfolgreich bereitgestellt. Da die vom DAO gespeicherte Vorschlagsadresse jetzt auf den bösartigen Vertrag verweist, wird beim Ausführen des Vorschlags der bösartige Code im Kontext des DAO ausgeführt, was erfolgreich den Owner auf den Angreifer ändert.
Reparaturvorschlag
Als Entwickler:
Bei der Genehmigung eines Vorschlags sollte nicht nur die Adresse aufgezeichnet werden, sondern auch der Code-Hash dieser Adresse, und beim Ausführen sollte überprüft werden, ob der Code-Hash konsistent ist.
Vermeide die Verwendung von delegatecall, um externe Verträge aufzurufen, es sei denn, es sind ausreichende Sicherheitsvorkehrungen vorhanden.
Berücksichtige die Möglichkeit, dass ein Vertrag nach seiner Selbstzerstörung an derselben Adresse neu bereitgestellt wird. Bei externen Aufrufen muss überprüft werden, ob der externe Vertrag vertrauenswürdig ist. Wenn ein unbekannter externer Vertrag aufgerufen werden muss, wird empfohlen zu überprüfen, ob im Vertrag Mechanismen zur Verhinderung der Selbstzerstörung vorhanden sind.
Als Prüfer:
Überprüfe, ob in den Verträgen, die externe Aufrufe enthalten, eine selfdestruct-Funktion vorhanden ist. Wenn ja, sollte man auf die Gefahr einer möglichen Angriffsrisiko achten, dass der externe Vertrag nach der Selbstzerstörung bösartigen Code an derselben Adresse bereitstellt.
Überprüfe, ob die Vertragsbereitstellung die CREATE2-Methode verwendet hat, und stelle sicher, dass der verwendete salt-Wert zur Generierung der Vertragsadresse ausreichend zufällig ist, um zu verhindern, dass die Vertragsadresse aufgrund einer vorzeitigen Vorhersage von Angreifern vorab beansprucht wird.
Überprüfe, ob der DAO bei der Ausführung des Vorschlags die Konsistenz des Zieladresscodes validiert, z.B. durch den Vergleich des aktuellen Code-Hashes mit dem Genehmigungs-Hash.
Analysiere sorgfältig die Verwendung von delegatecall, um sicherzustellen, dass die Zieladresse vertrauenswürdig ist und kein Risiko für Aufrufe von beliebigen Adressen besteht.
Überprüfe das Lebenszyklusmanagement des Vorschlags, z.B. ob es Mechanismen gibt, um zu verhindern, dass Vorschläge geändert oder ersetzt werden, z.B. durch Sperren des Zieladressstatus nach der Genehmigung.
