
DOM fuzzer
Écrit et maintenu par Ivan Fratric, [email protected]
Copyright 2017 Google Inc. Tous droits réservés.
Licencié sous la Licence Apache, version 2.0 (la « Licence ») ; vous ne pouvez utiliser ce fichier que conformément à la Licence. Vous pouvez obtenir une copie de la Licence à l'adresse suivante :
http://www.apache.org/licenses/LICENSE-2.0
Sauf lorsque la loi applicable l'exige ou lorsque cela a été convenu par écrit, le logiciel distribué sous la Licence est distribué « TEL QUEL », SANS GARANTIES NI CONDITIONS D'AUCUNE SORTE, expresses ou implicites. Voir la Licence pour connaître les autorisations et limitations spécifiques régissant la Licence.
Pour afficher les informations d'utilisation, exécutez la commande suivante :
python3 generator.py --help
Pour générer un seul échantillon .html, exécutez :
python generator.py --file <output file>
Pour générer un seul échantillon .html à l'aide d'un modèle que vous avez écrit :
python generator.py --file <output file> --template <your custom template file>
Pour générer plusieurs échantillons en un seul appel, exécutez :
python generator.py --output_dir <output directory> --no_of_files <number of output files>
Les échantillons générés seront placés dans le répertoire spécifié et seront nommés fuzz-<number>.html, par exemple fuzz-00001.html, fuzz-00002.html, etc. La génération de plusieurs échantillons est plus rapide car les fichiers de grammaire d'entrée ne doivent être chargés et analysés qu'une seule fois.
generator.py contient le script principal. Il utilise grammar.py comme bibliothèque et contient du code d'assistance supplémentaire pour le fuzzing DOM.
grammar.py contient le moteur de génération, qui est en grande partie indépendant de l'application et peut donc être utilisé dans d'autres fuzzers basés sur la génération (c'est-à-dire non-DOM). Comme il peut être utilisé comme bibliothèque, son utilisation est décrite dans une section séparée ci-dessous.
Les fichiers .txt contiennent les définitions de grammaire. Il y a 3 fichiers principaux, html.txt, css.txt et js.txt, qui contiennent respectivement les grammaires HTML, CSS et JavaScript. Ces fichiers de grammaire racines peuvent inclure du contenu provenant d'autres fichiers.
Pour utiliser le moteur de génération avec une grammaire personnalisée, vous pouvez utiliser le code Python suivant :
from grammar import Grammar
my_grammar = Grammar()
my_grammar.parse_from_file('input_file.txt')
result_string = my_grammar.generate_symbol('symbol_name')
Les sections suivantes décrivent la syntaxe des fichiers de grammaire.
Domato est basé sur un moteur qui, à partir d'une grammaire hors contexte dans un format simple spécifié ci-dessous, génère des échantillons à partir de cette grammaire.
Une grammaire est décrite comme un ensemble de règles au format de base suivant :
<symbol> = a mix of constants and <other_symbol>s
Chaque règle de grammaire contient un côté gauche et un côté droit séparés par le caractère égal. Le côté gauche contient un symbole, tandis que le côté droit contient les détails sur la façon dont ce symbole peut être développé. Lors du développement d'un symbole, tous les symboles du côté droit sont développés récursivement, tandis que tout ce qui n'est pas un symbole est simplement copié dans la sortie. Notez qu'une seule règle ne peut pas s'étendre sur plusieurs lignes du fichier d'entrée.
Considérez l'exemple simplifié suivant d'une partie de la grammaire CSS :
<cssrule> = <selector> { <declaration> }
<selector> = a
<selector> = b
<declaration> = width:100%
Si nous demandons au moteur de grammaire d'analyser cette grammaire et de générer 'cssrule', nous pouvons obtenir soit :
a { width:100% }
soit
b { width:100% }
Notez qu'il existe deux règles pour le symbole 'selector'. Dans de tels cas, lorsque le générateur est invité à générer un 'selector', il choisit la règle à utiliser de manière aléatoire. Il est également possible de spécifier la probabilité de la règle à l'aide de l'attribut 'p', par exemple :
<selector p=0.9> = a
<selector p=0.1> = b
Dans ce cas, la chaîne 'a' serait produite plus souvent que 'b'.
Il existe d'autres attributs qui peuvent être appliqués aux symboles en plus de la probabilité. Ils sont listés dans une section séparée.
Considérez un autre exemple pour la génération d'échantillons HTML :
<html> = <lt>html<gt><head><body><lt>/html<gt>
<head> = <lt>head<gt>...<lt>/head<gt>
<body> = <lt>body<gt>...<lt>/body<gt>
Notez que, comme les caractères '<' et '>' ont une signification particulière dans la syntaxe de la grammaire, nous utilisons ici <lt> et <gt> à la place. Ces symboles sont intégrés et n'ont pas besoin d'être définis par l'utilisateur. Une liste de tous les symboles intégrés est fournie dans une section séparée.
Pour générer du code de langage de programmation, une syntaxe similaire peut être utilisée, mais il y a quelques différences. Chaque ligne de la grammaire du langage de programmation correspondra à une ligne de la sortie. Pour cette raison, la syntaxe de la grammaire sera plus libre afin de permettre d'exprimer des constructions dans divers langages de programmation. Deuxièmement, lorsqu'une ligne est générée, en plus de produire la ligne, une ou plusieurs variables peuvent être créées et ces variables peuvent être réutilisées lors de la génération d'autres lignes. Reprenons l'exemple simplifié :
!varformat fuzzvar%05d
!lineguard try { <line> } catch(e) {}
!begin lines
<new element> = document.getElementById("<string min=97 max=122>");
<element>.doSomething();
!end lines
Si nous demandons au moteur de générer 5 lignes, nous pouvons obtenir quelque chose comme :
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) {}
Notez que
<new element> au lieu de <element>. Cela demande au générateur de créer une nouvelle variable de type 'element' au lieu de générer le symbole 'element'.<string> est l'un des symboles intégrés, il n'est donc pas nécessaire de le définir.Tout ce qui suit le premier caractère '#' sur la ligne est considéré comme un commentaire, par exemple :
#This is a comment
La syntaxe de la grammaire permet d'indiquer au fuzzer quelles règles ne sont pas récursives et peuvent être utilisées en toute sécurité même si le niveau maximal de récursion a été atteint. Cela se fait à l'aide des attributs ‘nonrecursive’. Un exemple est donné ci-dessous.
!max_recursion 10
<test root=true> = <foobar>
<foobar> = foo<foobar>
<foobar nonrecursive> = bar
Premièrement, une instruction facultative ‘!max_recursion’ définit le niveau maximal de profondeur de récursion (50 par défaut). Remarquez que la deuxième règle de production pour ‘foobar’ est marquée comme non récursive. Si jamais le niveau maximal de récursion est atteint, le générateur forcera l'utilisation de la règle non récursive pour le symbole ‘foobar’, empêchant ainsi la récursion infinie.
Dans Domato, l'inclusion et l'importation de grammaires sont deux contextes différents.
L'inclusion est plus simple. Vous pouvez utiliser :
!include other.txt
pour inclure les règles d'other.txt dans la grammaire en cours d'analyse.
L'importation fonctionne un peu différemment :
!import other.txt
indique au parseur de créer un nouvel objet Grammar() qui peut ensuite être référencé depuis la grammaire courante à l'aide du symbole spécial <import>, par exemple comme ceci :
<cssrule> = <import from=css.txt symbol=rule>
Vous pouvez considérer l'importation et l'inclusion en termes d'espaces de noms : !include placera la grammaire incluse dans l'espace de noms unique, tandis que !import créera un nouvel espace de noms qui pourra ensuite être accessible à l'aide du symbole <import> et de l'espace de noms spécifié via l'attribut 'from'.
Il peut arriver que vous souhaitiez appeler du code Python personnalisé dans votre grammaire. Par exemple, supposons que vous vouliez utiliser le moteur pour générer une réponse http et que vous souhaitiez que la longueur du corps corresponde à l'en-tête 'Size'. Comme cela n'est pas possible avec les règles de grammaire normales, vous pouvez inclure du code Python personnalisé pour y parvenir, comme ceci :
!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>
Les fonctions Python sont définies entre les commandes ‘!begin function <function_name>’ et ‘!end function’. Les fonctions peuvent être appelées de deux manières : à l'aide de l'attribut ‘beforeoutput’ et à l'aide du symbole <call>.
En spécifiant l'attribut ‘beforeoutput’ dans un symbole, la fonction correspondante sera appelée lorsque ce symbole sera développé, juste avant que le résultat du développement ne soit produit dans l'échantillon. Le résultat du développement sera transmis à la fonction dans la variable ret_val. La fonction est alors libre de modifier ret_val, de la stocker pour une utilisation ultérieure ou d'effectuer toute autre opération.
Lorsque vous utilisez le symbole spécial <call>, la fonction (spécifiée dans un attribut ‘function’) sera appelée lorsque le symbole sera rencontré pendant la génération du langage. Toute valeur stockée par la fonction dans ret_val sera considérée comme le résultat du développement (ret_val est incluse dans l'échantillon).
Votre code Python a accès aux variables suivantes :
context - un dictionnaire qui est transmis tout au long de la génération de l'échantillon. Vous pouvez l'utiliser pour stocker des valeurs (comme la taille dans l'exemple ci-dessus) et les récupérer dans les règles qui se déclenchent ensuite.attributes - un dictionnaire correspondant au symbole en cours de traitement. Vous pouvez l'utiliser pour passer des paramètres à vos fonctions. Par exemple, si vous avez utilisé quelque chose comme <call function=func foo=bar> pour appeler votre fonction, attributes\[‘foo’\] sera défini sur ‘bar’.ret_val - la valeur qui sera produite en sortie à la suite de l'appel de fonction. Elle est initialisée à une valeur vide lors de l'utilisation du symbole <call> pour appeler une fonction ; sinon, elle est initialisée à la valeur générée par le symbole.Les symboles suivants ont une signification particulière et ne doivent pas être redéfinis par les utilisateurs :
<lt> - le caractère ‘<’<gt> - le caractère ‘>’<hash> - le caractère ‘#’<cr> - le caractère CR<lf> - le caractère LF<space> - le caractère espace<tab> - le caractère de tabulation<ex> - le caractère ‘!’<char> - peut être utilisé pour générer un caractère ASCII arbitraire à l'aide de l'attribut ‘code’. Par exemple, <char code=97> correspond à ‘a’. Génère un caractère aléatoire s'il n'est pas spécifié. Prend en charge les attributs ‘min’ et ‘max’.<hex> - génère un chiffre hexadécimal aléatoire.<int>, <int 8>, , , , , , , - peuvent être utilisés pour générer des entiers aléatoires. Ils prennent en charge les attributs ‘min’ et ‘max’ qui peuvent être utilisés pour limiter la plage d'entiers générés. Ils prennent en charge les attributs ‘b’ et ‘be’ qui rendent la sortie binaire au format little/big endian au lieu d'une sortie texte.Les attributs suivants sont pris en charge :
‘doSomething(<int id=1>, <int id=1>)’, les deux entiers finiraient par avoir la même valeur. Seule la première instance est réellement développée, la seconde est simplement copiée depuis la première.<call>, voir la section ‘Inclusion de code Python’ pour plus d'informations.Voici quelques-uns des bogues trouvés avec Domato :
Ceci n'est pas un produit officiel de Google.
<uint8><int16><uint16><int32><uint32><int64><uint64><float>, <double> - génèrent un nombre à virgule flottante aléatoire. Ils prennent en charge les attributs ‘min’ et ‘max’ (0 et 1 s'ils ne sont pas spécifiés). Ils prennent en charge l'attribut ‘b’ qui rend la sortie binaire.<string> - génère une chaîne de caractères aléatoire. Il prend en charge les attributs ‘min’ et ‘max’ qui contrôlent le code de caractère minimal et maximal généré, ainsi que les attributs ‘minlength’ et ‘maxlength’ qui contrôlent la longueur de la chaîne.<htmlsafestring> - identique à <string>, sauf que les métacaractères HTML seront échappés, ce qui rend la chaîne sûre à intégrer dans du texte HTML ou des valeurs d'attributs.<lines> - produit le nombre donné (via l'attribut ‘count’) de lignes de code. Voir la section sur la génération de code de langage de programmation pour un exemple.<import> - importe un symbole depuis une autre grammaire, voir la section sur l'inclusion de grammaires externes pour plus de détails.<call> - appelle une fonction définie par l'utilisateur correspondant à l'attribut function. Voir la section sur l'inclusion de code Python dans la grammaire pour plus d'informations.