
Fuzzer basato su grammatica con feedback
Nautilus è un fuzzer guidato dalla copertura e basato su grammatiche. Puoi usarlo per migliorare la copertura dei test e trovare più bug. Specificando la grammatica di input semi-validi, Nautilus è in grado di eseguire mutazioni complesse e scoprire casi di test più interessanti. Molte delle idee alla base di questo fuzzer sono documentate in un articolo pubblicato a NDSS 2019.
La versione 2.0 ha aggiunto molti miglioramenti a questo prototipo iniziale ed è ora compatibile al 100% con AFL++. Oltre ai miglioramenti generali di usabilità, la versione 2.0 include molte nuove interessanti funzionalità:
Specifica una grammatica utilizzando regole come EXPR -> EXPR + EXPR o EXPR -> NUM e NUM -> 1. Da queste regole, il fuzzer costruisce un albero. Questa rappresentazione interna permette di applicare mutazioni molto più complesse rispetto ai byte grezzi. Questo albero viene poi trasformato in un input reale per l'applicazione target. Nelle grammatiche context-free normali, questo processo è semplice: tutte le foglie vengono concatenate. L'albero di sinistra nell'esempio qui sotto produrrebbe l'input a=1+2 e quello di destra a=1+1+1+2. Per aumentare l'espressività delle tue grammatiche, con Nautilus puoi fornire funzioni Python per il processo di unparsing, permettendo specifiche molto più complesse.
# checkout the git
git clone '[email protected]:nautilus-fuzz/nautilus.git'
cd nautilus
/path/to/AFLplusplus/afl-clang-fast test.c -o test #afl-clang-fast fornito da AFL
# tutti gli argomenti possono anche essere impostati tramite il file config.ron
cargo run --release -- -g grammars/grammar_py_example.py -o /tmp/workdir -- ./test @@
# oppure se vuoi usare la modalità QEMU:
cargo run /path/to/AFLplusplus/afl-qemu-trace -- ./test_bin @@
Qui usiamo Python per generare una grammatica per input simili a XML validi. Nota l'uso di una regola script per garantire che i tag di apertura e chiusura corrispondano.
#ctx.rule(NONTERM: string, RHS: string|bytes) aggiunge una regola NONTERM->RHS. Possiamo usare {NONTERM} in RHS per richiedere una ricorsione.
ctx.rule("START","<document>{XML_CONTENT}</document>")
ctx.rule("XML_CONTENT","{XML}{XML_CONTENT}")
ctx.rule("XML_CONTENT","")
#ctx.script(NONTERM:string, RHS: [string]], func) aggiunge una regola NONTERM->func(*RHS).
# A differenza di `rule` normale, RHS è un array di nonterminali.
# Sta alla funzione combinare i valori restituiti per i NONTERMINALI con qualsiasi contenuto fisso utilizzato.
ctx.script("XML",["TAG","ATTR","XML_CONTENT"], lambda tag,attr,body: b"<%s %s>%s</%s>"%(tag,attr,body,tag) )
ctx.rule("ATTR","foo=bar")
ctx.rule("TAG","some_tag")
ctx.rule("TAG","other_tag")
#a volte non vogliamo esplorare più in dettaglio l'insieme di possibili input. Ad esempio, se fuzziamo un interprete
#di script, non vogliamo perdere tempo a fuzzare tutti i diversi nomi di variabili. In questi casi possiamo usare terminali
#Regex. I terminali Regex vengono mutati solo durante la generazione, ma non durante le normali fasi di mutazione, risparmiando molto tempo.
#Il fuzzer esplora comunque diversi valori per la regex, ma non sarà in grado di imparare valori interessanti in modo incrementale.
#Usalo quando l'esplorazione incrementale probabilmente sprecherebbe tempo.
ctx.regex("TAG","[a-z]+")
Per testare le tue grammatiche puoi usare il generatore:
$ cargo run --bin generator -- -g grammars/grammar_py_exmaple.py -t 100
<document><some_tag foo=bar><other_tag foo=bar><other_tag foo=bar><some_tag foo=bar></some_tag></other_tag><some_tag foo=bar><other_tag foo=bar></other_tag></some_tag><other_tag foo=bar></other_tag><some_tag foo=bar></some_tag></other_tag><other_tag foo=bar></other_tag><some_tag foo=bar></some_tag></some_tag></document>
Puoi anche usare Nautilus in combinazione con AFL. Basta puntare AFL -o alla stessa directory di lavoro e AFL si sincronizzerà con Nautilus. Nota che questo è unidirezionale: AFL importa gli input di Nautilus, ma non viceversa.
#Terminale/Schermo 1
./afl-fuzz -Safl -i /tmp/seeds -o /tmp/workdir/ ./test @@
#Terminale/Schermo 2
cargo run --release -- -o /tmp/workdir -- ./test @@