Un generico software deve essere scritto in modo da resistere ad ogni concepibile condizione di errore. I conseguenti benefici si rifletteranno in modo positivo non solo sulla robustezza del codice ma anche sull’interoperabilità tra i sistemi. Da questo criterio (raccomandazione RFC 1122 sul principio di Robustness del Software della IETF, Internet Engineering Task Force), nasce la tecnica del fuzzing per scoprire le vulnerabilità nascoste.
Che cos’è il fuzzing
Un professore dell’Università del Wisconsin si trovava nel suo appartamento, nell’autunno del 1988, mentre imperversava un violento temporale. Stava lavorando con il suo computer che utilizzava un sistema Unix ed era collegato alla rete dell’Università tramite una connessione dial-up e un modem a 1200 baud di velocità. A causa delle perturbazioni meteorologiche, la linea era così disturbata da interferire con i comandi e i programmi che stava usando. Improvvisamente, la situazione peggiorò talmente che gli stessi programmi, prima semplicemente rallentati, smisero di funzionare e gli impedirono di terminare il lavoro. L’intuito del professore trasformò il suo disappunto momentaneo in un’opportunità. Decise di investigare il problema e condivise la sfida con i suoi studenti, proponendo loro anche un esame del suo corso di laurea e battezzò fuzz la tecnica di testing che potesse aiutare a comprendere i problemi intervenuti nel caso di connessione perturbata che lui aveva sperimentato. Nelle sue intenzioni originali, l’attività di fuzzing doveva essere un nuovo tipo di test, da aggiungere a quelli tradizionali, focalizzato alla scoperta delle vulnerabilità nascoste nel codice.
Da allora il fuzzing, spesso detto anche test “negativo”, è diventato un test sempre più ricercato, perché può servire sia a rendere più robusto il software sia i protocolli di rete implementati o in generale qualunque applicazione web o mobile o di altro genere. Il fuzzing consiste nell’invio ad una applicazione software di dati casuali in input. Se si osserva un blocco dell’applicazione, il test potrebbe evidenziare una debolezza presente nel codice che, a sua volta, probabilmente nasconderebbe una vulnerabilità non ancora nota. Per poter effettuare questo tipo di test in modo efficace, è ovviamente consigliabile automatizzare la procedura con opportuni tool.
Le caratteristiche e tipologie dei tool fuzzing
La maggioranza dei tool di fuzzing sono caratterizzati da tre componenti fondamentali:
- il generatore di dati malformati
- il distributore dei dati verso il sistema da analizzare
- il controllore della reazione del sistema per rilevare eventuali comportamenti anomali.
Descriveremo nel seguito l’utilizzo dei questa tecnica nei diversi campi di applicazione.
Fuzzing per protocolli di comunicazione
Per comprendere l’utilizzo del fuzzing proviamo a considerare un’operazione che compiamo ogni qualvolta desideriamo accedere ad Internet e ci connettiamo ad un server tramite una richiesta HTTP. Questa richiesta avrebbe la forma seguente:
GET / HTTP/1.1
Accept: image/gif, image/bitmap
Accept- Encoding: gzip, deflate
Accept- Language: en-us
Connection: keep-alive
Ricevendola, il server risponderebbe normalmente, inviando al client la seguente risposta:
HTTP/1.1 200 OK
Date: xxx
Server: xxx
last modified: xxx
Accept- ranges: bytes
Content-length: 100
Connection: close
Content-type: text/html
Ora, supponiamo di inviare questa nuova richiesta al server:
GET aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAA HTTP/1.1
Accept: image/gif, image/bitmap
Accept- Encoding: gzip, deflate
Accept- Language: en-us
Connection: keep-alive
Alla richiesta, ovviamente anomala, il server risponderà in questo modo:
HTTP/1.1 404 Not Found
Date: xxx
Server: xxx
last modified: xxx
Accept- ranges: bytes
Content-length: 100
Connection: close
Content-type: text/html
Inviamo, in seguito, ulteriori richieste anomale, cambiando i caratteri casuali inseriti nella stessa, per esempio:
GET snaaaaaaaaaaaaa?????????????????????????????????aaaaaaa HTTP/1.1
Accept: image/gif, image/bitmap
Accept- Encoding: gzip, deflate
Accept- Language: en-us
Connection: keep-alive
Potrebbe capitare che già alla terza richiesta anomala, il server non risponda più e il sistema si blocchi.
Questo semplice esempio ci aiuta a capire la logica con cui dobbiamo sollecitare il sistema sotto esame al fine di comprendere se il software sia in grado di riconoscere ogni possibile condizione anomala, senza che essa possa causare un’indisponibilità del sistema. Se arrivando caratteri anomali che il sistema non riuscisse ad interpretare, esso si bloccasse, non rispondendo più nemmeno alle richieste legittime, l’effetto su di esso sarebbe simile ad un attacco DoS (Denial of Service). Il fuzzing, quindi, é risultato essere uno strumento prezioso e unico per individuare vulnerabilità non ancore note, come CVE-2014-0160 (Common Vulnerability Enumeration) più conosciuto col nome di HeartBleed, scoperto nel 2014 e di cui riportiamo, qui di seguito, la descrizione.
Secondo la raccomandazione RFC 6520, che racchiude la descrizione Hearbeat Extension per testare le connessioni sicure TLS/DTLS, un computer ad un estremo della connessione deve mandare messaggi Heartbeat Request che consistono in un payload, tipicamente una stringa di testo, insieme alla lunghezza del payload, come un integer di 16 bit, come messaggio di tipo keep-alive. Il computer ricevente deve quindi mandare lo stesso esatto payload in risposta al mittente, per segnalare la continuità della connessione. Può accadere che a causa del fallimento nel controllo preciso dei dati di input, il messaggio di ritorno consista nel payload, seguito da altri dati che si trovano presenti nel buffer di memoria. È ciò che é accaduto con la vulnerabilità Heartbleed che è stata sfruttata mandando proprio una richiesta heartbeat malformata con payload minuscolo, indicando una lunghezza fuori standard, in modo da provocare una risposta da parte della vittima, più estesa di quanto effettivamente richiesto. Questo ha permesso all’attaccante di ottenere più informazioni di quelle strettamente necessarie.
L’utilizzo del fuzzing ha dimostrato l’esistenza di questa vulnerabilità, una porta pericolosamente aperta sulla confidenzialità dei messaggi della vittima. Il fuzzing sui protocolli è possibile su una grande diversità di tecnologie, dal mondo wireless come sul protocollo Bluetooth, al mondo networking, con i ben noti protocolli che si appoggiano sul TCP/IP, fino a settori più specifici come ad esempio quello dell’automotive (fuzzing del protocollo CAN) o quello dell’industria 4.0 (fuzzing dei protocolli Profinet, MQTT etc..) e cosi via.
Fuzzing per applicazioni web
Molte vulnerabilità basate su input non validati, come i famosi SQL-Injection, XSS (Cross Site Scripting) e “file-path traversal”, possono essere rilevate grazie a test di fuzzing, cioé immettendo varie stringhe di caratteri, nei parametri di input dell’applicazione, come per esempio, login-password, e analizzando le risposte dell’applicazione, alla ricerca di messaggi di errore, condizioni di blocco, altre anomalie o persino ottenendo un accesso non autorizzato. Per creare sequenze di test di fuzzing, considerata la complessità e le dimensioni delle applicazioni, occorre far ricorso a tool di automazione, usando i cosiddetti generatori che utilizzano combinazioni varie di dati casuali: caratteri, numeri, stringhe, dati presi da file di input, etc..
Le stesse caratteristiche di fuzzing sono valide per testare anche URL, modulistica online, caselle che richiedono dati sensibili agli utenti e in generale su qualunque parametro variabile delle richieste inviate ad un server.
Standard disponibili
Dall’iniziale concepimento di questa tecnica ad oggi, numerosi sono gli studi compiuti e alcune organizzazioni internazionali hanno emanato Best Practices e linee guida in materia. Ad esempio la nota OWASP (Open Web Application Security Project) ha inserito il fuzzing tra le modalità di testing per verificare la possibile presenza di alcune vulnerabilità nel software come SQL Injection, XSS (Cross Site Scripting) o Buffer overflow.
Il IEEE (Institute of Electrical and Electronics Engineers) ed il NIST (National Institute of Standards and Technology) hanno, inoltre, pubblicato articoli sull’utilità di questa metodologia.
Conclusioni
Eseguire un test di fuzzing significa inviare dati malformati ad un sistema con l’intento dichiarato di mandarlo in crash, rivelando così problematiche di affidabilità. Dalle statistiche ormai note sulla qualità del software, si dichiara che esistono all’incirca dai 15 ai 50 possibili difetti ogni 1000 linee di codice, per cui la ricerca di queste vulnerabilità è senz’altro un campo di attività importante.
Il fuzzing come visto, è comunque una tecnica molto utile per cercare di scoprire vulnerabilità nascoste nei sistemi. Spesso, quando esce una nuova versione di un software o di un sistema operativo, l’esigenza é dettata sia dalla necessità di risolvere problemi di base come quella rappresentata dai cosiddetti “bachi”, sia dall’inserimento di correzioni per la sicurezza. Il rovescio della medaglia è che spesso si opera in modalità black-box, vale a dire si testa il sistema senza essere opportunamente aggiornati sulle sue caratteristiche o sul suo comportamento, il che rende difficoltosa la valutazione degli effetti causati dall’operazione di testing piuttosto che dell’impatto provocato dalla possibile vulnerabilità. Questo approccio porta spesso ai cosiddetti falsi positivi, per cui l’analisi dei report dell’attività di fuzzing richiede sempre una attenta valutazione, dove l’esperienza maturata nella sicurezza, può fare la differenza nello scoprire con esattezza le vulnerabilità più nascoste.
Bisogna inoltre sottolineare che il test di fuzzing è spesso distruttivo, quindi si consiglia di utilizzarlo sempre su sistemi di test pre-produzione, mai su quelli “live” oppure online.