
Fuzzer DOM
Scritto e mantenuto da Ivan Fratric, [email protected]
Copyright 2017 Google Inc. Tutti i diritti riservati.
Concesso in licenza secondo l'Apache License, Versione 2.0 (la "Licenza"); non è consentito usare questo file se non in conformità con la Licenza. È possibile ottenere una copia della Licenza al seguente indirizzo:
http://www.apache.org/licenses/LICENSE-2.0
Salvo quanto previsto dalla legge applicabile o concordato per iscritto, il software distribuito nell'ambito della Licenza è distribuito "COSÌ COM'È", SENZA GARANZIE O CONDIZIONI DI ALCUN TIPO, esplicite o implicite. Consultare la Licenza per il linguaggio specifico che disciplina i permessi e le limitazioni previsti dalla Licenza.
Per visualizzare le informazioni sull'utilizzo eseguire il seguente comando:
python3 generator.py --help
Per generare un singolo campione .html eseguire:
python generator.py --file <output file>
Per generare un singolo campione .html usando un template da te creato eseguire:
python generator.py --file <output file> --template <your custom template file>
Per generare più campioni con una singola chiamata eseguire:
python generator.py --output_dir <output directory> --no_of_files <number of output files>
I campioni generati verranno inseriti nella directory specificata e saranno denominati fuzz-<numero>.html, ad esempio fuzz-00001.html, fuzz-00002.html, ecc. La generazione di più campioni è più veloce perché i file di grammatica di input devono essere caricati e analizzati una sola volta.
generator.py contiene lo script principale. Utilizza grammar.py come libreria e contiene ulteriore codice di supporto per il fuzzing del DOM.
grammar.py contiene il motore di generazione che è in gran parte indipendente dall'applicazione e può quindi essere utilizzato in altri fuzzer basati sulla generazione (cioè non-DOM). Poiché può essere usato come libreria, il suo utilizzo è descritto in una sezione separata più avanti.
I file .txt contengono le definizioni delle grammatiche. Ci sono 3 file principali: html.txt, css.txt e js.txt, che contengono rispettivamente le grammatiche HTML, CSS e JavaScript. Questi file di grammatica radice possono includere contenuti da altri file.
Per utilizzare il motore di generazione con una grammatica personalizzata, si può usare il seguente codice Python:
from grammar import Grammar
my_grammar = Grammar()
my_grammar.parse_from_file('input_file.txt')
result_string = my_grammar.generate_symbol('symbol_name')
Le sezioni seguenti descrivono la sintassi dei file di grammatica.
Domato si basa su un motore che, data una grammatica context-free nel semplice formato specificato sotto, genera campioni da quella grammatica.
Una grammatica è descritta come un insieme di regole nel seguente formato di base:
<symbol> = a mix of constants and <other_symbol>s
Ogni regola grammaticale contiene un lato sinistro e un lato destro separati dal carattere uguale. Il lato sinistro contiene un simbolo, mentre il lato destro contiene i dettagli su come quel simbolo può essere espanso. Quando si espande un simbolo, tutti i simboli sul lato destro vengono espansi ricorsivamente, mentre tutto ciò che non è un simbolo viene semplicemente copiato nell'output. Si noti che una singola regola non può estendersi su più righe del file di input.
Consideriamo il seguente esempio semplificato di una parte della grammatica CSS:
<cssrule> = <selector> { <declaration> }
<selector> = a
<selector> = b
<declaration> = width:100%
Se istruiamo il motore grammaticale ad analizzare questa grammatica e a generare 'cssrule', potremmo ottenere:
a { width:100% }
oppure
b { width:100% }
Si noti che ci sono due regole per il simbolo 'selector'. In questi casi, quando al generatore viene chiesto di generare un 'selector', sceglierà la regola da usare in modo casuale. È anche possibile specificare la probabilità della regola usando l'attributo 'p', per esempio:
<selector p=0.9> = a
<selector p=0.1> = b
In questo caso, la stringa 'a' verrebbe emessa più spesso di 'b'.
Oltre alla probabilità, ci sono altri attributi che possono essere applicati ai simboli. Questi sono elencati in una sezione separata.
Consideriamo un altro esempio per generare campioni HTML:
<html> = <lt>html<gt><head><body><lt>/html<gt>
<head> = <lt>head<gt>...<lt>/head<gt>
<body> = <lt>body<gt>...<lt>/body<gt>
Si noti che, poiché '<' e '>' hanno un significato speciale nella sintassi delle grammatiche, qui usiamo <lt> e <gt> al loro posto. Questi simboli sono integrati e non devono essere definiti dall'utente. Un elenco di tutti i simboli integrati è fornito in una sezione separata.
Per generare codice di linguaggi di programmazione si può usare una sintassi simile, ma ci sono alcune differenze. Ogni riga della grammatica del linguaggio di programmazione corrisponderà a una riga dell'output. Per questo motivo, la sintassi della grammatica sarà più libera, per consentire di esprimere costrutti in vari linguaggi di programmazione. In secondo luogo, quando viene generata una riga, oltre a emettere la riga, possono essere create una o più variabili e queste variabili possono essere riutilizzate nella generazione di altre righe. Di nuovo, guardiamo l'esempio semplificato:
!varformat fuzzvar%05d
!lineguard try { <line> } catch(e) {}
!begin lines
<new element> = document.getElementById("<string min=97 max=122>");
<element>.doSomething();
!end lines
Se istruiamo il motore a generare 5 righe, potremmo ottenere qualcosa come:
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) {}
Si noti che
<new element> invece di <element>. Questo istruisce il generatore a creare una nuova variabile di tipo 'element' invece di generare il simbolo 'element'.<string> è uno dei simboli integrati, quindi non è necessario definirlo.Tutto ciò che segue il primo carattere '#' nella riga è considerato un commento, per esempio:
#This is a comment
La sintassi delle grammatiche offre un modo per indicare al fuzzer quali regole sono non ricorsive e possono essere usate in sicurezza anche quando è stato raggiunto il livello massimo di ricorsione. Questo viene fatto con gli attributi 'nonrecursive'. Un esempio è riportato di seguito.
!max_recursion 10
<test root=true> = <foobar>
<foobar> = foo<foobar>
<foobar nonrecursive> = bar
Innanzitutto, un'istruzione opzionale '!max_recursion' definisce il livello massimo di profondità di ricorsione (50 di default). Si noti che la seconda regola di produzione per 'foobar' è marcata come non ricorsiva. Se il livello massimo di ricorsione viene raggiunto, il generatore forzerà l'uso della regola non ricorsiva per il simbolo 'foobar', prevenendo così ricorsioni infinite.
In Domato, includere e importare grammatiche sono due contesti diversi.
L'inclusione è più semplice. Si può usare:
!include other.txt
per includere le regole di other.txt nella grammatica attualmente analizzata.
L'importazione funziona in modo leggermente diverso:
!import other.txt
indica al parser di creare un nuovo oggetto Grammar() a cui è possibile fare riferimento dalla grammatica corrente usando il simbolo speciale <import>, per esempio in questo modo:
<cssrule> = <import from=css.txt symbol=rule>
Si possono pensare l'importazione e l'inclusione in termini di namespace: !include inserirà la grammatica inclusa in un unico namespace, mentre !import creerà un nuovo namespace a cui si può accedere tramite il simbolo <import> e il namespace specificato tramite l'attributo 'from'.
A volte si potrebbe voler chiamare codice Python personalizzato nella propria grammatica. Per esempio, supponiamo di voler usare il motore per generare una risposta http e di volere che la lunghezza del corpo corrisponda all'header 'Size'. Poiché questo non è possibile con le normali regole grammaticali, si può includere codice Python personalizzato per realizzarlo in questo modo:
!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>
Le funzioni Python sono definite tra i comandi '!begin function <nome_funzione>' e '!end function'. Le funzioni possono essere chiamate in due modi: usando l'attributo 'beforeoutput' e usando il simbolo <call>.
Specificando l'attributo 'beforeoutput' in un simbolo, la funzione corrispondente verrà chiamata quando questo simbolo viene espanso, appena prima che il risultato dell'espansione venga emesso nel campione. Il risultato dell'espansione verrà passato alla funzione nella variabile ret_val. La funzione è quindi libera di modificare ret_val, memorizzarlo per un uso successivo o eseguire qualsiasi altra operazione.
Quando si usa il simbolo speciale <call>, la funzione (specificata nell'attributo 'function') verrà chiamata quando il simbolo viene incontrato durante la generazione del linguaggio. Qualsiasi valore memorizzato dalla funzione in ret_val sarà considerato il risultato dell'espansione (ret_val viene incluso nel campione).
Il codice Python ha accesso alle seguenti variabili:
context - un dizionario che viene passato per l'intera generazione del campione. Può essere usato per memorizzare valori (come la memorizzazione della dimensione nell'esempio precedente) e recuperarli nelle regole che scattano successivamente.attributes - un dizionario corrispondente al simbolo attualmente in elaborazione. Può essere usato per passare parametri alle funzioni. Per esempio, se si usa qualcosa come <call function=func foo=bar> per chiamare la funzione, attributes['foo'] sarà impostato su 'bar'.ret_val - Il valore che verrà emesso come risultato della chiamata di funzione. Viene inizializzato a un valore vuoto quando si usa il simbolo <call> per chiamare una funzione, altrimenti verrà inizializzato al valore generato dal simbolo.I seguenti simboli hanno un significato speciale e non dovrebbero essere ridefiniti dagli utenti:
<lt> - carattere '<'<gt> - carattere '>'<hash> - carattere '#'<cr> - carattere CR<lf> - carattere LF<space> - carattere spazio<tab> - carattere tabulazione<ex> - carattere '!'<char> - può essere usato per generare un carattere ascii arbitrario usando l'attributo 'code'. Per esempio <char code=97> corrisponde ad 'a'. Genera un carattere casuale se non specificato. Supporta gli attributi 'min' e 'max'.<hex> - genera una cifra esadecimale casuale.<int>, <int 8>, , , , , , , - possono essere usati per generare interi casuali. Supportano gli attributi 'min' e 'max', che possono essere usati per limitare l'intervallo degli interi generati. Supportano gli attributi 'b' e 'be', che rendono l'output binario in formato little/big endian invece che testuale.Sono supportati i seguenti attributi:
'doSomething(<int id=1>, <int id=1>)' entrambi gli int avranno lo stesso valore. Solo la prima istanza viene effettivamente espansa, la seconda viene semplicemente copiata dalla prima.<call>, vedere la sezione 'Inclusione di codice Python' per maggiori informazioni.Alcuni dei bug trovati con Domato:
Questo non è un prodotto ufficiale Google.
<uint8><int16><uint16><int32><uint32><int64><uint64><float>, <double> - generano un numero a virgola mobile casuale. Supportano gli attributi 'min' e 'max' (0 e 1 se non specificati). Supportano l'attributo 'b' che rende l'output binario.<string> - genera una stringa casuale. Supporta gli attributi 'min' e 'max', che controllano il valore charcode minimo e massimo generato, oltre agli attributi 'minlength' e 'maxlength', che controllano la lunghezza della stringa.<htmlsafestring> - come <string>, tranne per il fatto che i metacaratteri HTML vengono escaped, rendendo sicuro incorporare la stringa come parte di testo HTML o valori di attributi.<lines> - emette il numero indicato (tramite l'attributo 'count') di righe di codice. Vedere la sezione sulla generazione di codice di linguaggi di programmazione per un esempio.<import> - importa un simbolo da un'altra grammatica, vedere la sezione sull'inclusione di grammatiche esterne per i dettagli.<call> - chiama una funzione definita dall'utente corrispondente all'attributo function. Vedere la sezione sull'inclusione di codice Python nella grammatica per maggiori informazioni.