sfregamento parte 2

Fuzzing Parte 2 – Fuzzing con AFL

Come ho scritto nel ultimo capitolo, in questo articolo spiegherò solo come fuzz quando c'è accesso al codice sorgente usando AFL. Per dimostrare, ho preso un vecchio programma open source che ho trovato su GitHub chiamato ccalc. Come suggerisce il nome, è una semplice calcolatrice scritta in C. L'ho scelta perché era probabile che il fuzzing trovasse facilmente molti crash al suo interno e, come vedrai, questa ipotesi si è rivelata corretta.

Per prima cosa, dobbiamo determinare un obiettivo. Ho deciso di fuzz la funzione main(), invece di trovare qualche funzione specifica per fuzz. Il ragionamento è che il programma analizza l'input non elaborato in una serie di funzioni prima di consegnare l'input elaborato alle funzioni che effettivamente eseguono i calcoli stessi. Poiché il programma filtra gli input errati durante l'analisi, non è corretto eseguire il fuzz di una funzione specifica.

L'originale main() assomiglia più o meno a questo (l'ho abbreviato un po'):

Funzione principale

Generalmente, contiene due rami: riceve l'input tramite argc, argv[]o tramite input standard (stdin). Non sono sicuro del perché, ma il creatore di AFL lo raccomanda esplicitamente non sfocare argc, argv[], così come tutte le altre fonti online. Quindi l'ho cambiato un po': prima ho rimosso la sezione che si occupa dell'input proveniente da argc, argv, e poi ho cercato di ottimizzarlo il più possibile. Dopotutto, ogni volta che il fuzzer tenta un altro input, questa funzione viene eseguita. Piccole possono quindi Le inefficienze possono portare a gravi rallentamenti. Ecco il risultato finale:

Funzione principale dopo la modifica

Notare il ciclo che ho aggiunto. Indica ad AFL di utilizzare la modalità persistente. Nella modalità predefinita di AFL, ogni volta che AFL esegue i programmi, utilizza la chiamata di sistema fork() per creare un nuovo sottoprocesso. Questa chiamata di sistema ha un serio sovraccarico, che rallenta seriamente l'intero processo di fuzzing. Quando viene utilizzata la modalità persistente, il fuzzer utilizza lo stesso processo più e più volte. L'unico requisito è cancellare tutte le variabili e i buffer ogni volta che viene eseguito il ciclo.

Ho chiamato il file con la funzione modificata harness.c, e per poter compilare il programma con questo file e non con main.c, ho modificato la forma del file automake:

automake prima della modifica

a

automake dopo la modifica

È giunto il momento di sfocare il programma. Ho eseguito automake per generare i file make, quindi crearmi da solo con il comando:

comando automake

In modo che il programma venga compilato con la strumentazione AFL.

L'unica cosa che resta da fare ora è creare la cartella di input iniziale per AFL, che ho chiamato 'in', e la cartella di output 'out'. Nella cartella di input, si inseriscono semplici file di testo (senza formato) che contengono input legittimi, come "20/2" o "5*4". Poiché vogliamo eseguire un paio di processi AFL (ciascun processo in esecuzione sul proprio core della CPU), creiamo una cartella di input separata per ciascun processo.

cartella di input per ogni processo

Per fuzz, si esegue semplicemente il comando:

Comando di esecuzione AFL per il primo utilizzo

Per il primo processo, il secondo è

Comando di esecuzione AFL per il secondo utilizzo

E così via e così via.

Come puoi vedere nello screenshot seguente, AFL ha una GUI che fornisce informazioni importanti sul processo di fuzzing. Ad esempio, le misurazioni della copertura sono fornite sotto copertura della mappae sotto approfondite scoperte la GUI fornisce anche il numero di arresti anomali e le relative informazioni.

GUI di avanzamento AFL
GUI di avanzamento AFL

Dopo che è stato rilevato un numero sufficiente (il numero esatto dipende da te) di arresti anomali, di solito dopo un lungo periodo di tempo senza un nuovo arresto univoco, puoi terminare il processo. Per ogni processo di AFL eseguito, verrà generata una cartella separata nella cartella di output:

Cartella di output

All'interno di queste cartelle, i dati sono disposti in questo modo:

Contenuto della cartella di output

È importante spiegare quali informazioni contiene ciascuno di questi:

  • plot_data – mette le informazioni in una forma conduttiva per la generazione di un grafico.
  • fuzzer_stats – statistiche sul processo di fuzzing.
  • fuzz_bitmap – una bitmap in cui ogni byte corrisponde a un ramo del programma.
    “AFL mantiene una “bitmap fuzz”, con ogni byte all'interno della bitmap che rappresenta un conteggio del numero di volte in cui è stato preso un particolare ramo all'interno del programma fuzz. AFL non esegue una mappatura uno a uno tra un particolare ramo e un byte all'interno della bitmap. Invece, la strumentazione incorporata di AFL inserisce un ID costante a due byte casuale in ogni ramo. Ogni volta che l'esecuzione raggiunge un ramo dotato di strumentazione, AFL esegue uno XOR dell'ID del nuovo ramo e dell'ultimo ID del ramo visto prima di arrivare al nuovo ramo. Ciò acquisisce sia il ramo corrente che il percorso univoco intrapreso per raggiungerlo (come quando la stessa funzione viene chiamata da più posizioni nel codice). AFL quindi applica una funzione di hashing al valore XOR per determinare quale voce nella bitmap rappresenta quella combinazione di rami. Ogni volta che viene esercitata una particolare combinazione di rami, il byte appropriato viene incrementato all'interno della bitmap.
  • cmdline: il comando fornito ad AFL. In pratica il nome del programma.
  • .cur_input – l'input corrente.
  • coda – tutti gli input che sono stati provati fino ad ora.
  • si blocca e si blocca: i risultati. Ogni file in queste cartelle contiene l'input che ha causato arresti anomali o blocchi. Nel nome di ogni file è specificato il segnale del kernel del crash (non rilevante in caso di blocco, ovviamente), l'ID dell'input utilizzato da AFL per creare l'input che ha causato il crash, il tempo trascorso dall'inizio dell'esecuzione di AFL, e la mutazione utilizzata per generare l'input dal suo seme.
Salta al contenuto