
Ein JavaScript-Engine-Fuzzer
Ein (abdeckungs-)geleiteter Fuzzer für dynamische Sprachinterpreter, basierend auf einer benutzerdefinierten Zwischensprache ("FuzzIL"), die mutiert und nach JavaScript übersetzt werden kann.
Die grundlegenden Schritte zur Verwendung dieses Fuzzers sind:
swift build [-c release].swift run [-c release] FuzzilliCli --profile=<profile> [other cli options] /path/to/jsshell. Siehe auch swift run FuzzilliCli --help.Das Bauen und Ausführen von Fuzzilli und den unterstützten JavaScript-Engines in Docker und auf Google Compute Engine wird ebenfalls unterstützt.
Schauen Sie sich main.swift an, um ein Nutzungsbeispiel der Fuzzilli-Bibliothek zu sehen und mit den verschiedenen Konfigurationsoptionen zu spielen. Als nächstes werfen Sie einen Blick auf Fuzzer.swift für die hochrangige Fuzzing-Logik. Von dort aus tauchen Sie in jeden Teil ein, der interessant erscheint.
Patches, Ergänzungen und andere Beiträge zu diesem Projekt sind sehr willkommen! Überprüfen Sie jedoch kurz die Hinweise für Mitwirkende. Fuzzilli folgt grob Googles Codestyle-Guide für Swift.
Es wäre sehr geschätzt, wenn Sie eine kurze Notiz (möglicherweise mit einer CVE-Nummer) an [email protected] senden oder einen Pull-Request für eine mit Hilfe dieses Projekts gefundene Schwachstelle öffnen, damit sie in den Bug-Showcase-Abschnitt aufgenommen werden kann. Ansonsten können Sie natürlich jede Bug-Bounty, CVE-Credits usw. für die Schwachstellen beanspruchen :)
Beim Fuzzing auf Kern-Interpreter-Fehler, z.B. in JIT-Compilern, wird die semantische Korrektheit generierter Programme zu einem Problem. Dies steht im Gegensatz zu den meisten anderen Szenarien, z.B. dem Fuzzing von Laufzeit-APIs, bei dem die semantische Korrektheit leicht durch das Umschließen des generierten Codes in Try-Catch-Konstrukte umgangen werden kann. Es gibt verschiedene Möglichkeiten, eine akzeptable Rate semantisch korrekter Samples zu erreichen, eine davon ist ein mutativer Ansatz, bei dem alle Samples im Korpus ebenfalls semantisch gültig sind. In diesem Fall hat jede Mutation nur eine geringe Chance, ein gültiges Sample in ein ungültiges zu verwandeln.
Um einen mutationsbasierten JavaScript-Fuzzer zu implementieren, müssen Mutationen an JavaScript-Code definiert werden. Anstatt den AST oder andere syntaktische Elemente eines Programms zu mutieren, wird eine benutzerdefinierte Zwischensprache (IL) definiert, an der Mutationen des Kontroll- und Datenflusses eines Programms direkter durchgeführt werden können. Diese IL wird anschließend zur Ausführung nach JavaScript übersetzt. Die Zwischensprache sieht grob wie folgt aus:
v0 <− LoadInteger '0'
v1 <− LoadInteger '10'
v2 <− LoadInteger '1'
v3 <− LoadInteger '0'
BeginFor v0, '<', v1, '+', v2 −> v4
v6 <− BinaryOperation v3, '+', v4
Reassign v3, v6
EndFor
v7 <− LoadString 'Result: '
v8 <− BinaryOperation v7, '+', v3
v9 <− LoadGlobal 'console'
v10 <− CallMethod v9, 'log', [v8]
Was z.B. trivial in den folgenden JavaScript-Code übersetzt werden kann:
const v0 = 0;
const v1 = 10;
const v2 = 1;
let v3 = 0;
for (let v4 = v0; v4 < v1; v4 = v4 + v2) {
const v6 = v3 + v4;
v3 = v6;
}
const v7 = "Result: ";
const v8 = v7 + v3;
const v9 = console;
const v10 = v9.log(v8);
Oder in den folgenden JavaScript-Code durch Inlinen von Zwischenausdrücken:
let v3 = 0;
for (let v4 = 0; v4 < 10; v4++) {
v3 = v3 + v4;
}
console.log("Result: " + v3);
FuzzIL hat eine Reihe von Eigenschaften:
Reassign-Anweisung neu zugewiesen werden.Eine Reihe von Mutationen kann dann an diesen Programmen durchgeführt werden:
Eine viel ausführlichere Diskussion darüber, wie Fuzzilli funktioniert, finden Sie hier.
Der Fuzzer ist in Swift implementiert, wobei einige Teile (z.B. Coverage-Messungen, Socket-Interaktionen usw.) in C implementiert sind.
Eine Fuzzer-Instanz (implementiert in Fuzzer.swift) besteht aus den folgenden zentralen Komponenten: