
DOM fuzzer
Geschrieben und gepflegt von Ivan Fratric, [email protected]
Copyright 2017 Google Inc. All Rights Reserved.
Lizenziert unter der Apache-Lizenz, Version 2.0 (die "License"); Sie dürfen diese Datei nur in Übereinstimmung mit der Lizenz verwenden. Eine Kopie der Lizenz erhalten Sie unter
http://www.apache.org/licenses/LICENSE-2.0
Sofern nicht durch geltendes Recht gefordert oder schriftlich vereinbart, wird die Software unter der Lizenz auf einer "AS IS"-BASIS verteilt, OHNE GEWÄHRLEISTUNG ODER BEDINGUNGEN JEGLICHER ART, weder ausdrücklich noch implizit. Siehe die Lizenz für die spezifischen Berechtigungen und Beschränkungen der Lizenz.
Um die Verwendungsinformationen anzuzeigen, führen Sie den folgenden Befehl aus:
python3 generator.py --help
Um eine einzelne .html-Beispieldatei zu generieren, führen Sie Folgendes aus:
python generator.py --file <output file>
Um eine einzelne .html-Beispieldatei mit einer selbst erstellten Vorlage zu generieren, führen Sie Folgendes aus:
python generator.py --file <output file> --template <your custom template file>
Um mehrere Beispieldateien mit einem einzigen Aufruf zu generieren, führen Sie Folgendes aus:
python generator.py --output_dir <output directory> --no_of_files <number of output files>
Die generierten Beispieldateien werden im angegebenen Verzeichnis abgelegt und wie folgt benannt: fuzz-<number>.html, z. B. fuzz-00001.html, fuzz-00002.html usw. Das Generieren mehrerer Beispieldateien ist schneller, da die Eingabe-Grammatikdateien nur einmal geladen und geparst werden müssen.
generator.py enthält das Hauptskript. Es verwendet grammar.py als Bibliothek und enthält zusätzlichen Hilfscode für das DOM-Fuzzing.
grammar.py enthält die Generierungsengine, die weitgehend anwendungsunabhängig ist und daher auch in anderen (d. h. Nicht-DOM-) generierungsbasierten Fuzzern verwendet werden kann. Da sie als Bibliothek verwendet werden kann, wird ihre Verwendung weiter unten in einem separaten Abschnitt beschrieben.
.txt-Dateien enthalten Grammatikdefinitionen. Es gibt 3 Hauptdateien: html.txt, css.txt und js.txt, die die HTML-, CSS- bzw. JavaScript-Grammatiken enthalten. Diese Wurzel-Grammatikdateien können Inhalte aus anderen Dateien einbinden.
Um die Generierungsengine mit einer benutzerdefinierten Grammatik zu verwenden, können Sie den folgenden Python-Code nutzen:
from grammar import Grammar
my_grammar = Grammar()
my_grammar.parse_from_file('input_file.txt')
result_string = my_grammar.generate_symbol('symbol_name')
Die folgenden Abschnitte beschreiben die Syntax der Grammatikdateien.
Domato basiert auf einer Engine, die Beispieldateien aus einer kontextfreien Grammatik in einem einfachen, unten angegebenen Format generiert.
Eine Grammatik wird als eine Reihe von Regeln im folgenden Grundformat beschrieben:
<symbol> = a mix of constants and <other_symbol>s
Jede Grammatikregel enthält eine linke Seite und eine rechte Seite, die durch das Gleichheitszeichen getrennt sind. Die linke Seite enthält ein Symbol, während die rechte Seite die Details darüber enthält, wie dieses Symbol expandiert werden darf. Beim Expandieren eines Symbols werden alle Symbole auf der rechten Seite rekursiv expandiert, während alles, was kein Symbol ist, einfach in die Ausgabe kopiert wird. Beachten Sie, dass eine einzelne Regel nicht über mehrere Zeilen der Eingabedatei verlaufen kann.
Betrachten Sie das folgende vereinfachte Beispiel eines Teils der CSS-Grammatik:
<cssrule> = <selector> { <declaration> }
<selector> = a
<selector> = b
<declaration> = width:100%
Wenn wir die Grammatik-Engine anweisen, diese Grammatik zu parsen und 'cssrule' zu generieren, erhalten wir möglicherweise entweder:
a { width:100% }
oder
b { width:100% }
Beachten Sie, dass es zwei Regeln für das Symbol 'selector' gibt. In solchen Fällen wählt der Generator, wenn er aufgefordert wird, einen 'selector' zu generieren, die zu verwendende Regel zufällig aus. Es ist auch möglich, die Wahrscheinlichkeit der Regel über das 'p'-Attribut anzugeben, zum Beispiel:
<selector p=0.9> = a
<selector p=0.1> = b
In diesem Fall würde die Zeichenkette 'a' häufiger ausgegeben als 'b'.
Zusätzlich zur Wahrscheinlichkeit gibt es weitere Attribute, die auf Symbole angewendet werden können. Diese sind in einem separaten Abschnitt aufgeführt.
Betrachten Sie ein weiteres Beispiel zum Generieren von HTML-Beispieldateien:
<html> = <lt>html<gt><head><body><lt>/html<gt>
<head> = <lt>head<gt>...<lt>/head<gt>
<body> = <lt>body<gt>...<lt>/body<gt>
Beachten Sie, dass '<' und '>' in der Grammatiksyntax eine besondere Bedeutung haben; daher verwenden wir hier stattdessen <lt> und <gt>. Diese Symbole sind eingebaut und müssen nicht vom Benutzer definiert werden. Eine Liste aller eingebauten Symbole befindet sich in einem separaten Abschnitt.
Zum Generieren von Programmiersprachencode kann eine ähnliche Syntax verwendet werden, allerdings gibt es einige Unterschiede. Jede Zeile der Programmiersprachen-Grammatik entspricht einer Zeile der Ausgabe. Aus diesem Grund ist die Grammatiksyntax freier gestaltet, um Konstrukte in verschiedenen Programmiersprachen ausdrücken zu können. Zweitens können beim Generieren einer Zeile zusätzlich zur Ausgabe der Zeile eine oder mehrere Variablen erstellt werden, die bei der Generierung anderer Zeilen wiederverwendet werden können. Auch hierzu schauen wir uns das vereinfachte Beispiel an:
!varformat fuzzvar%05d
!lineguard try { <line> } catch(e) {}
!begin lines
<new element> = document.getElementById("<string min=97 max=122>");
<element>.doSomething();
!end lines
Wenn wir die Engine anweisen, 5 Zeilen zu generieren, erhalten wir möglicherweise etwa Folgendes:
try { var00001 = document.getElementById("hw"); } catch(e) {}
try { var00001.doSomething(); } catch(e) {}
try { var00002 = document.getElementById("feezcqbndf"); } catch(e) {}
try { var00002.doSomething(); } catch(e) {}
try { var00001.doSomething(); } catch(e) {}
Beachten Sie, dass
<new element> anstelle von <element> verwendet. Dies weist den Generator an, eine neue Variable vom Typ 'element' zu erstellen, anstatt das Symbol 'element' zu generieren.<string> ist eines der eingebauten Symbole, daher muss es nicht definiert werden.Alles nach dem ersten '#'-Zeichen in der Zeile gilt als Kommentar, zum Beispiel:
#This is a comment
Die Grammatiksyntax bietet eine Möglichkeit, dem Fuzzer mitzuteilen, welche Regeln nichtrekursiv sind und sicher verwendet werden können, selbst wenn die maximale Rekursionstiefe erreicht wurde. Dies geschieht über das Attribut ‘nonrecursive’. Ein Beispiel finden Sie unten.
!max_recursion 10
<test root=true> = <foobar>
<foobar> = foo<foobar>
<foobar nonrecursive> = bar
Zunächst definiert eine optionale ‘!max_recursion’-Anweisung die maximale Rekursionstiefe (standardmäßig 50). Beachten Sie, dass die zweite Produktionsregel für ‘foobar’ als nichtrekursiv markiert ist. Wenn die maximale Rekursionstiefe jemals erreicht wird, erzwingt der Generator die Verwendung der nichtrekursiven Regel für das Symbol ‘foobar’ und verhindert so unendliche Rekursionen.
In Domato sind das Einbinden und das Importieren von Grammatiken zwei verschiedene Kontexte.
Das Einbinden ist einfacher. Sie können Folgendes verwenden:
!include other.txt
um Regeln aus other.txt in die aktuell geparste Grammatik einzubinden.
Das Importieren funktioniert etwas anders:
!import other.txt
weist den Parser an, ein neues Grammar()-Objekt zu erstellen, auf das von der aktuellen Grammatik über das spezielle <import>-Symbol verwiesen werden kann, zum Beispiel so:
<cssrule> = <import from=css.txt symbol=rule>
Sie können sich Importieren und Einbinden im Sinne von Namespaces vorstellen: !include fügt die eingebundene Grammatik in den einzelnen Namespace ein, während !import einen neuen Namespace erstellt, auf den dann über das <import>-Symbol und den über das 'from'-Attribut angegebenen Namespace zugegriffen werden kann.
Manchmal möchten Sie möglicherweise benutzerdefinierten Python-Code in Ihrer Grammatik aufrufen. Angenommen, Sie möchten die Engine verwenden, um eine HTTP-Antwort zu generieren, und möchten, dass die Länge des Bodys mit dem 'Size'-Header übereinstimmt. Da dies mit normalen Grammatikregeln nicht möglich ist, können Sie benutzerdefinierten Python-Code einbinden, um dies wie folgt zu erreichen:
!begin function savesize
context['size'] = ret_val
!end function
!begin function createbody
n = int(context['size'])
ret_val = 'a' * n
!end function
<foo root> = <header><cr><lf><body>
<header> = Size: <int min=1 max=20 beforeoutput=savesize>
<body> = <call function=createbody>
Die Python-Funktionen werden zwischen den Befehlen ‘!begin function <function_name>’ und ‘!end function’ definiert. Die Funktionen können auf zwei Arten aufgerufen werden: über das Attribut ‘beforeoutput’ und über das <call>-Symbol.
Durch Angabe des Attributs ‘beforeoutput’ in einem Symbol wird die entsprechende Funktion aufgerufen, wenn dieses Symbol expandiert wird, unmittelbar bevor das Ergebnis der Expansion in die Beispieldatei ausgegeben wird. Das Expansionsergebnis wird der Funktion in der Variablen ret_val übergeben. Die Funktion kann ret_val dann nach Belieben modifizieren, für die spätere Verwendung speichern oder beliebige andere Operationen ausführen.
Bei Verwendung eines speziellen <call>-Symbols wird die Funktion (angegeben in einem Attribut ‘function’) aufgerufen, wenn das Symbol während der Sprachgenerierung erreicht wird. Jeder Wert, den die Funktion in ret_val speichert, wird als Ergebnis der Expansion betrachtet (ret_val wird in die Beispieldatei aufgenommen).
Ihr Python-Code hat Zugriff auf die folgenden Variablen:
context - ein Wörterbuch, das während der gesamten Beispieldatei-Generierung durchgereicht wird. Sie können es verwenden, um Werte zu speichern (z. B. die Größe im obigen Beispiel) und sie in den Regeln abzurufen, die anschließend ausgelöst werden.attributes - ein Wörterbuch, das dem aktuell verarbeiteten Symbol entspricht. Sie können es verwenden, um Parameter an Ihre Funktionen zu übergeben. Wenn Sie beispielsweise etwas wie <call function=func foo=bar> verwenden, um Ihre Funktion aufzurufen, wird attributes[‘foo’] auf ‘bar’ gesetzt.ret_val - Der Wert, der als Ergebnis des Funktionsaufrufs ausgegeben wird. Er wird mit einem leeren Wert initialisiert, wenn das <call>-Symbol zum Aufrufen einer Funktion verwendet wird, andernfalls mit dem vom Symbol generierten Wert.Die folgenden Symbole haben eine besondere Bedeutung und sollten von Benutzern nicht neu definiert werden:
<lt> - das Zeichen ‘<’<gt> - das Zeichen ‘>’<hash> - das Zeichen ‘#’<cr> - das CR-Zeichen<lf> - das LF-Zeichen<space> - das Leerzeichen<tab> - das Tabulatorzeichen<ex> - das Zeichen ‘!’<char> - kann verwendet werden, um ein beliebiges ASCII-Zeichen über das Attribut ‘code’ zu generieren. Zum Beispiel entspricht <char code=97> einem ‘a’. Wenn nicht angegeben, wird ein zufälliges Zeichen generiert. Unterstützt die Attribute ‘min’ und ‘max’.<hex> - generiert eine zufällige Hexadezimalziffer.<int>, <int 8>, , , , , , , - können verwendet werden, um zufällige Ganzzahlen zu generieren. Unterstützt die Attribute ‘min’ und ‘max’, mit denen der Bereich der generierten Ganzzahlen eingeschränkt werden kann. Unterstützt die Attribute ‘b’ und ‘be’, die die Ausgabe binär im Little-/Big-Endian-Format anstelle von Textausgabe erzeugen.Die folgenden Attribute werden unterstützt:
‘doSomething(<int id=1>, <int id=1>)’ beide Ints denselben Wert haben. Nur die erste Instanz wird tatsächlich expandiert, die zweite wird einfach von der ersten kopiert.<call>-Symbol verwendet; weitere Informationen finden Sie im Abschnitt ‘Einbinden von Python-Code’.Einige der mit Domato gefundenen Bugs:
Dies ist kein offizielles Google-Produkt.
<uint8><int16><uint16><int32><uint32><int64><uint64><float>, <double> - generieren eine zufällige Gleitkommazahl. Unterstützt die Attribute ‘min’ und ‘max’ (0 und 1, wenn nicht angegeben). Unterstützt das Attribut ‘b’, das die Ausgabe binär macht.<string> - generiert eine zufällige Zeichenkette. Unterstützt die Attribute ‘min’ und ‘max’, die den minimalen und maximalen generierten Zeichencode steuern, sowie die Attribute ‘minlength’ und ‘maxlength’, die die Länge der Zeichenkette steuern.<htmlsafestring> - wie <string>, außer dass HTML-Metazeichen maskiert werden, sodass die Zeichenkette sicher als Teil von HTML-Text oder Attributwerten eingebettet werden kann.<lines> - gibt die angegebene Anzahl (über das Attribut ‘count’) von Codezeilen aus. Ein Beispiel finden Sie im Abschnitt zum Generieren von Programmiersprachencode.<import> - importiert ein Symbol aus einer anderen Grammatik; Details finden Sie im Abschnitt zum Einbinden externer Grammatiken.<call> - ruft eine benutzerdefinierte Funktion auf, die dem Funktionsattribut entspricht. Weitere Informationen finden Sie im Abschnitt zum Einbinden von Python-Code in die Grammatik.