Leitfaden zur automatischen Prüfung intelligenter Verträge. Teil 2: Rutschen

Analysator: Slither
Beschreibung: Open-Source-Framework für statische Analysen für Solidity
githib: https://github.com/trailofbits/slither


Dies ist ein in Python geschriebener statischer Code-Analysator. Er weiß, wie man Variablen überwacht, aufruft und eine solche Liste von Schwachstellen erkennt. Jede Sicherheitsanfälligkeit hat einen Link mit einer Beschreibung. Wenn Sie Solidity noch nicht kennen, ist es sinnvoll, alle kennenzulernen.


Slither kann als Python-Modul arbeiten und dem Programmierer eine Schnittstelle für die Prüfung nach seinem eigenen Plan zur Verfügung stellen. Ein einfaches und anschauliches Beispiel dafür, was Slither tun kann, finden Sie hier .


Wir werden am Ende des Artikels zu den Analyseszenarien zurückkehren, aber vorerst Slither ausführen:


git clone https://github.com/trailofbits/slither.git cd slither docker build -t slither . 

und versuchen Sie unseren Vertrag zu analysieren.


Wir gehen mit constructor-eth-booking in das Verzeichnis und versuchen, vom Docker aus zu rutschen:


 $ docker run -v $(pwd)/contracts:/slither/contracts slither contracts/flattened.sol 

Wir erhalten den Fehler " solc=0.4.20 erfordert andere Compilerversion", und jetzt müssen wir die Version von solc=0.4.20 in den Slither-Docker solc=0.4.20 . Dazu korrigieren wir das Dockerfile von Slither selbst, wie in Abschnitt 2 in der Einleitung angegeben , d. H. Irgendwo am Ende der Docker-Datei fügen wir die Zeile hinzu:


 COPY --from=ethereum/solc:0.4.20 /usr/bin/solc /usr/bin 

, das Image neu erstellen, laufen, jubeln, alles kompiliert.


Wir sehen die Ausgabe, die vor verschiedenen „Pragma“ und vor falschen Variablennamen wie dem Parameter '_price' of Booking.Booking (flattened.sol#73) is not in mixedCase . Die Analysegeräte geben viele Warnungen aus, aber wir suchen nach echten Fehlern und werden die Kleinigkeit nicht beachten. Wir filtern alle Nachrichten über MixedCase heraus, jetzt liegt es nicht mehr am Stil:


 $ docker run -v $(pwd)/contracts:/slither/contracts slither contracts/flattened.sol 2>&1 | fgrep -v 'mixedCase' 

Echte Programmierer vermissen alles Grün, sehen alles Rot aus, und hier ist, was Slither im Vertrag neben falsch-positiven Ergebnissen gefunden hat:


 Booking.refundWithoutCancellationFee (flattened.sol#243-250) sends eth to arbirary user Dangerous calls: - client.transfer(address(this).balance) (flattened.sol#249) Reference: https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#functions-that-send-ether-to-arbitrary-destinations INFO:Detectors: Booking.refundWithCancellationFee (flattened.sol#252-259) sends eth to arbirary user Dangerous calls: - owner.transfer(m_cancellationFee) (flattened.sol#257) - client.transfer(address(this).balance) (flattened.sol#258) Reference: https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#functions-that-send-ether-to-arbitrary-destinations 

Schauen Sie nun, was mit dieser Funktion im Vertrag nicht stimmt:


  /************************** PRIVATE **********************/ function refundWithoutCancellationFee() private { address client = m_client; m_client = address(0); changeState(State.OFFER); client.transfer(address(this).balance); } function refundWithCancellationFee() private { address client = m_client; m_client = address(0); changeState(State.CANCELED); owner.transfer(m_cancellationFee); client.transfer(address(this).balance); } 

In diesem Fall wird beispielsweise die Funktion refundWithoutCancellationFee() folgendermaßen aufgerufen:


  function rejectPayment() external onlyOwner onlyState(State.PAID) { refundWithoutCancellationFee(); } function refund() external onlyClient onlyState(State.PAID) { refundWithoutCancellationFee(); } 

Hmm, formal gibt es keine Fehler: Anrufe werden von allen Arten von nur onlyOwner geschützt, aber Slither schwört, dass die Sendung ohne Überprüfung innerhalb der Rückerstattung ohne CancellationFee () gesendet wird. Und er hat Recht, die Funktion selbst hat wirklich fast keine Einschränkungen. Lassen Sie es privat sein, und es wird von den Wrappern "ablehnenPayment ()" "Rückerstattung ()" mit den erforderlichen Einschränkungen aufgerufen. Wenn Sie jedoch in dieser Form den Vertrag ändern, besteht ein großes Risiko, dass Sie die Einschränkungen vergessen und die refundWithoutCancellationFee() an einen anderen Ort verschieben. für den Angreifer verfügbar. Selbst wenn formal keine Sicherheitslücke besteht, erwiesen sich die Informationen als nützlich - dies ist zumindest eine „Warnstufe“, wenn der Auftrag die Weiterentwicklung des Vertragscodes plant. In diesem Fall verwenden zwei Funktionen von verschiedenen Teilnehmern denselben Code, und diese Entscheidung wurde getroffen, um Gas zu sparen - der Vertrag ist einmalig und die Kosten für seine Berechnung sind ein wichtiger Faktor.


Ich überprüfte erneut, dass Slither für jede Sendung flucht, und übertrug den Funktionskörper direkt auf die oben genannten "RejectPayment ()" und "Reverage ()". Die Warnung verschwand, d. H. Slither erkannte, dass die Sendung jetzt nicht ohne Adressprüfung gesendet wird. Toller Start!


Lassen Sie uns nun überprüfen, wie Slither die Initialisierung von Variablen überwacht. Dazu kommentieren wir zwei Initialisierungen:


 - m_fileHash = _fileHash; + // m_fileHash = _fileHash; - m_price = _price; + // m_price = _price; 

Das erste ist in Bezug auf Lücken nicht sehr wichtig, außer für die Verschwendung von Ressourcen, da m_fileHash nirgendwo verwendet wird, sondern beim Erstellen eines Vertrags einfach in der Blockchain gespeichert wird. Aber m_price wird verwendet und Slither schwört korrekt, dass m_price nirgendwo initialisiert wird, obwohl es verwendet wird:


 Booking.m_price (flattened.sol#128) is never initialized. It is used in: - fallback (flattened.sol#144-156) 

Nun, das ist ein einfacher Trick, wie erwartet, alles hat gut funktioniert.


Jetzt fügen wir jedem Vertragspartner den so beliebten Wiedereintritt hinzu: Wir werden den Vertragsstatus nach einem externen Anruf ändern. Wir nehmen folgende Änderungen vor:


  function refundWithoutCancellationFee() private { address client = m_client; - m_client = address(0); - changeState(State.OFFER); - client.transfer(address(this).balance); + client.call.value(address(this).balance)(); + m_client = address(0); + changeState(State.OFFER); } 

Ich musste die Übertragung durch einen Anruf ersetzen, weil Die Übertragungsvariante schwört nicht, da die Übertragung einen Anruf mit einem Minimum an Gas sendet und ein Rückruf nicht möglich ist (obwohl beim Wechsel zur Konstantinopel-Gabel in Ethereum der Gaspreis geändert wurde und dies den Wiedereintrittsangriff mithilfe der Übertragung wieder aktivierte.


Ergebnis der Wiedereintrittssuche:


 Reentrancy in Booking.refundWithoutCancellationFee (flattened.sol#243-253): External calls: - client.call.value(address(this).balance)() (flattened.sol#245) State variables written after the call(s): - m_client (flattened.sol#246) 

Es ist in Ordnung, zumindest werden nach externen Aufrufen keine Statusvariablen angegeben, und das ist sehr gut.


Wenn Sie sich entlang der Liste bewegen, suchen die verbleibenden Schwachstellen in der Liste entweder einfach nach bestimmten Methoden im Code oder nach bekannten Mustern, die natürlich recht zuverlässig funktionieren, wenn der Zugriff auf das Markup über Python verfügbar ist. Das heißt, bekannte Muster Slither wird nicht fehlen.


Jetzt werde ich Änderungen vornehmen, die die Besonderheiten der Arbeit von statischen Analysatoren perfekt zeigen:


 - client.transfer(address(this).balance + for (uint i=0; i < 1; i++) { + client.transfer(address(this).balance - 999999999999999999); + } 

und das Ergebnis:


 Booking.refundWithoutCancellationFee has external calls inside a loop: - client.transfer(address(this).balance - 999999999999999999) (flattened.sol#252) Reference: https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description/_edit#calls-inside-a-loop 

Der Zyklus wird einmal ausgeführt und ist entartet. Daher ist die ausgegebene Warnung falsch positiv, und das Fehlen einer Warnung vor gefährlicher Arithmetik ist falsch negativ. Analyse von Typen, Betriebsergebnissen, Anrufzählung - Aufgaben sind nicht für statische Analysatoren. Verstehen Sie daher genau, welche Fehler Slither finden wird und nach welchen mit anderen Tools gesucht werden sollte.


Wir haben versprochen, die Möglichkeit zu erwähnen, eigene Skripte zum Testen zu schreiben und interessante Informationen über den Vertrag mit dem Schlüssel --print . Unter diesem Gesichtspunkt ist Slither ein großartiges Werkzeug für CI. Die Entwickler eines großen Vertragssystems kennen die Namen sicherheitskritischer Variablen: Salden, Provisionsgrößen, Flags und können ein Testskript schreiben, das alle Änderungen im Code blockiert, die beispielsweise eine wichtige Variable überschreiben oder Statusvariablen nach einem externen Aufruf ändern Die hoch vorhersehbare Analyse ist ein großartiges Werkzeug für die Verwendung in Haken.
Die Aufgabe von Slither ist es, Sie vor dummen Fehlern zu bewahren, bekannte gefährliche Muster zu finden und den Entwickler zu warnen. In dieser Version eignet es sich gut als Tool für einen unerfahrenen Entwickler von Solidity, mit dem sofort gefragt wird, wie Code in Solidity korrekt geschrieben werden soll.


Zusammenfassung


Bei meinen persönlichen Tests würde ich Slither 4 für Vielseitigkeit, Einfachheit und Benutzerfreundlichkeit sowie für einfache und verständliche Testskripte und Anpassungsfähigkeit an CI einsetzen.


Slither fand zuversichtlich eine echte WARNUNG im Zusammenhang mit der Verwendung der Funktion, die die Sendung sendet, und fand alle eingeführten Fehler. Er konnte nicht nur mit dynamischen Analysen fertig werden, die formal nicht durchgeführt werden sollten, sonst müsste er auf Universalität, Vorhersehbarkeit und Benutzerfreundlichkeit verzichten.


Im nächsten Artikel werden wir uns mit dem Mythril-Analysegerät befassen. Hier ist jedoch das Inhaltsverzeichnis für Artikel, die zum Schreiben bereit oder geplant sind:


Teil 1. Einführung. Zusammenstellung, Abflachung, Versionen von Solidity
Teil 2. Slither (dieser Artikel)
Teil 3. Mithril
Teil 4. Mantikor (während des Schreibens)
Teil 5. Echidna (während des Schreibens)

Source: https://habr.com/ru/post/de438338/


All Articles