Auffinden von Schwachstellen in Java-Bytecode: Was tun mit den Ergebnissen?


    Solar inCode kann Schwachstellen im Java-Bytecode erkennen. Es reicht jedoch nicht aus, die Bytecode-Anweisung anzuzeigen, die die Sicherheitsanfälligkeit enthält. Wie kann eine nicht vorhandene Sicherheitsanfälligkeit im Quellcode angezeigt werden?

    In der Praxis muss bei Verwendung des Schwachstellensuchwerkzeugs eine von drei Aktionen auf jede gefundene Schwachstelle angewendet werden:

    • beseitigen;
    • Risiken eingehen;
    • beweisen, dass dies kein falsch positives ist.

    Für alle drei Aktionen ist eine Analyse der Sicherheitsanfälligkeit erforderlich (Sie müssen beispielsweise wissen, wie die Sicherheitsanfälligkeit beseitigt werden kann und welche Risiken damit verbunden sind).

    Wie wird eine solche Analyse durchgeführt, wenn wir Sicherheitslücken im Java-Bytecode finden, aber keinen Quellcode haben?

    Die Hauptmethode für die Suche nach Sicherheitslücken bei der Erstellung von inCode wurde als statische Analyse ausgewählt - Suche nach Sicherheitslücken, ohne Code auszuführen. Für die statische Analyse benötigen Sie:

    • ein Codemodell erstellen (Zwischendarstellung);
    • Ergänzen des Modells mit Dateninformationen unter Verwendung statischer Analysealgorithmen (Datenflussanalyse, Kontrollfluss - Datenflussanalyse, Verschmutzungsanalyse);
    • Regeln für die Suche nach Sicherheitslücken anwenden (die Regeln geben an, wo sich Sicherheitslücken im Codemodell befinden, in Bezug auf dieses Modell und die Informationen, mit denen es ergänzt wird).

    Wenn Sie Schwachstellen in einer Zwischendarstellung finden, müssen Sie diese zur weiteren Analyse im Quellcode anzeigen.

    Die ersten Anwendungen, die Solar inCode nach Sicherheitslücken suchte, waren Android- und Java-Anwendungen. Die Suche nach Schwachstellen in ausführbaren Dateien ist sehr gefragt:

    1. Gemäß den Vertragsbedingungen darf der Kunde den Quellcode nicht übermitteln.
    2. Selbst wenn der Quellcode übertragen wurde, kann ein ausführbarer Code, der nicht mit dem übertragenen Quellcode übereinstimmt, in den Kampfstand (oder auf Google Play) gebracht werden.
    3. Entwickler verwenden Komponenten von Drittanbietern ohne Quellcode. Dieser Code muss ebenfalls kontrolliert werden.

    Daher haben wir für Android- und Java-Anwendungen Java-Bytecode als Zwischendarstellung für die statische Analyse ausgewählt. Nachdem der Quellcode der mobilen Anwendung in Java-Bytecode kompiliert wurde, kombiniert der Dalvik-Compiler die Klassendateien und kompiliert den Code erneut in Bytecode für Dalvik, um eine ausführbare Dex-Datei zu erhalten. Die ausführbare Datei sowie die Ressourcen und die Konfigurationsdatei sind im apk-Paket enthalten, das über Google Play verteilt wird. Es gibt Tools, die die Verarbeitung und Konvertierung von apk-Paketen implementieren: Entpacken, Entschlüsseln von Ressourcen und Konfigurationsdateien, Übersetzen von Dalvik-Code in Java-Bytecode ( apktool , dex2jar ).

    Bytecode kann auch durch Kompilieren aus dem Quellcode abgerufen werden (wie bei der Analyse von Java- und Scala-Quellcode). Java-Bytecode eignet sich daher gut als einzelne interne Darstellung bei der Analyse des Quellcodes und des ausführbaren Codes von Java- und Android-Anwendungen (Sie können auch alle Sprachen analysieren, die in Java-Bytecode kompiliert wurden).

    Java-Bytecode kann dekompiliert werden, und der Code ist von ausreichender Qualität. Es gibt viele Dekompilierer für Java ( JD , Fernflower , Procyon ). Wir haben den wiederhergestellten Java-Code nicht als Zwischendarstellung verwendet, da bei Dekompilierungswerkzeugen Fehler auftreten, die die Qualität der Schwachstellensuche beeinträchtigen können.

    Wir haben also Schwachstellen in Java-Bytecode gefunden (wir werden in den folgenden Artikeln darüber schreiben, wie das gemacht wird). Was tun mit den Ergebnissen?

    Wir müssen sie in Bezug auf den "Quellcode" zeigen, genauer gesagt, den wiederhergestellten Code auf hoher Ebene. Unter Sicherheitsanfälligkeit wird hier eine Reihe von Bytecode-Anweisungspositionen verstanden, die die Sicherheitsanfälligkeit definieren (ein unsicherer Methodenaufruf, eine Reihe von Anweisungen, durch die ein unsicherer Datenstrom geleitet wird). Daher müssen wir für jeden Befehl im Bytecode die Zeilennummer im wiederhergestellten Code abgleichen. In der Klassendatei (Bytecode-Datei, die der Klasse im Quellcode entspricht) befindet sich ein Attribut LineNumberTable, der die Zuordnung von Positionen im Bytecode zu Zeilennummern im Quellcode speichert. Um Schwachstellen in Bezug auf die Java-Sprache anzuzeigen, sollte sich das LineNumberTable-Attribut im Bytecode befinden.

    Bei der Analyse des Bytecodes (einschließlich des aus der APK-Datei erhaltenen Codes) wird das LineNumberTable-Attribut möglicherweise nicht angezeigt. Es kann während der Kompilierung oder während der Rückwärtsübersetzung von apk gelöscht werden. Obwohl dies nicht so wichtig ist, entsprach die aus dem Bytecode entfernte LineNumberTable dem vom Entwickler geschriebenen Quellcode und nicht dem wiederhergestellten "Quellcode". Dies bedeutet, dass Sie das LineNumberTable-Attribut im analysierten Bytecode wiederherstellen müssen, das den wiederhergestellten Code angibt.

    Der grundlegende Algorithmus basiert auf dem Aufbau eines abstrakten Syntaxbaums für dekompilierten Code (AST) unter Verwendung von Java-Bytecode und der Ausgabe des Quellcodes während des AST-Durchlaufs. Während der AST-Überquerung (Tiefenüberquerung) werden auch Informationen über die Entsprechung der Zeilennummern des wiederhergestellten Codes und die Positionen der Anweisungen in Bytecode-Methoden gespeichert.

    In jedem Knoten des Baums kennen wir die Position des Befehls in Bytecode relativ zum Beginn der aktuellen Methode und die Zeilennummer in der Datei des wiederhergestellten Codes. Während der Umgehung werden daher auch die Grenzen der Methoden im wiederhergestellten Code für die nachfolgende Filterung der Paare "Position im Methodenbytecode" - "Zeilennummer in der Datei" gespeichert.

    Anonyme Klassen werden separat verarbeitet, da sie ineinander verschachtelte Methoden erzeugen. Zu diesem Zweck wird am Ende der Runde eine Analyse der Verschachtelung der Intervalle der Positionen der Methoden im Quellcode durchgeführt.

    Formalisierung des wiederhergestellten Zuordnungs- und Wiederherstellungsalgorithmus



    In der Praxis erhalten wir häufig Projekte, die sowohl Quellcode (dann können wir Bytecode mit einer Tabelle von Zeilennummern durch Kompilieren erhalten) als auch Bytecode (verschiedene Komponenten, Bibliotheken usw. von Drittanbietern) enthalten. Um solche Projekte zu analysieren, implementiert inCode eine kombinierte Vorverarbeitung des Projekts in Java. Es besteht aus den folgenden Schritten:

    • Alle Klassendateien, Dateien mit Quellcode für Java und Scala, JAR / WAR-Dateien mit Bytecode werden im Projekt erkannt.
    • Abhängig von der vom Benutzer festgelegten Einstellung für das Scannen von Projekten werden Klassendateien aus JAR / WAR-Dateien in die Liste der Klassendateien aufgenommen (in den meisten Fällen bedeutet dies, dass das Projekt zusammen mit Bibliotheken analysiert wird).
    • Aus Klassendateien und Quellcodedateien erhalten wir die vollständigen Klassennamen, mit Klassennamen werden Quellcodedateien und Bytecodedateien verglichen.
    • Bytecode-Dateien, für die keine Quellcode-Dateien gefunden wurden, werden mit dem obigen Verfahren zum Wiederherstellen von Informationen über Zeilennummern dekompiliert.

    Bei einer solchen Vorverarbeitung werden anonyme und verschachtelte Klassen berücksichtigt - eine einzelne Quellcodedatei kann mehreren Bytecodedateien entsprechen.

    Als Ergebnis haben wir für jede Bytecode-Datei eine Datei mit Java-Code (entweder wiederhergestellt oder als Quelle) und eine Tabelle mit Zeilennummern, die sie mit dieser Datei verbinden.

    Mit den im Artikel beschriebenen Verfahren und Algorithmen zeigt inCode alle in einer Java- oder Android-Anwendung gefundenen Schwachstellen im Quellcode an, unabhängig davon, ob sie zur Analyse übermittelt wurden.

    Ein ähnlicher Ansatz wird bei der Analyse von Binärdateien von iOS-Anwendungen verwendet, aber dort ist alles viel komplizierter: Die Dekompilierung des Binärcodes der ARM-Architektur ist viel weniger erforscht. Weitere Publikationen widmen sich diesem Thema.

    Jetzt auch beliebt: