Tecniche per la rilevazione e correzione di errori nell'elaborazione automati...
Tecniche di Test-driven development in ambito sicurezza informatica e rilevazione vulnerabilità
1. UNIVERSITÀ DEGLI STUDI DI TRIESTE
FACOLTÀ DI INGEGNERIA
Corso di laurea magistrale in Ingegneria Informatica
Tecniche di Test-driven development
in ambito sicurezza informatica
e rilevazione vulnerabilità
Laureando Relatore
Federico Cecutti chiar.mo prof. Alberto Bartoli
Correlatore
Enrico Milanese
anno accademico 2011/2012
3. Indice
Abstract.................................................................................................................................................7
PARTE PRIMA – Descrizione generale del lavoro
1 Introduzione......................................................................................................................................8
1.1 Contesto...............................................................................................................................8
1.2 Obiettivi e vincoli progettuali..............................................................................................8
1.3 Fasi di lavoro e risultati........................................................................................................9
1.4 Articolazione della tesi.........................................................................................................9
2 Analisi e progettazione...................................................................................................................11
2.1 Scelta dell'ambiente di testing............................................................................................11
2.2 Test di integrazione............................................................................................................12
2.2.1 Contesto e obiettivi.....................................................................................................12
2.2.2 Classi di test................................................................................................................13
2.2.3 Oggetti mock..............................................................................................................14
2.2.4 Lato server..................................................................................................................14
2.2.5 Schema consuntivo.....................................................................................................17
2.3 Test su moduli singoli........................................................................................................18
2.3.1 Contesto......................................................................................................................18
2.3.1.1 Analisi di banner e pagine web (banners e webapplications)............................18
2.3.1.2 Analisi di numeri di versione (versions_regexp)................................................20
2.3.2 Tipi di test...................................................................................................................22
2.3.2.1 Test su singoli sistemi di banners (categoria 1)..................................................22
2.3.2.2 Test su singoli sistemi noti di versions_regexp (categoria 2).............................23
2.3.2.3 Test di coerenza interna sui file di banners e webapplications (categoria 3).....25
2.3.2.3.1 Test di correttezza sintattica........................................................................25
2.3.2.3.2 Test di coerenza tra espressione regolare e commenti................................26
2.3.2.3.3 Test di coerenza tra espressione regolare ed elementi <info>
con type=”re”..............................................................................................26
2.3.2.3.4 Test di coerenza tra espressione regolare, commenti ed
elementi <info> con type=”re”....................................................................26
2.3.2.3.5 Test per ripetizioni......................................................................................26
2.3.2.3.6 Test sugli elementi <info> sospetti..............................................................27
2.3.2.3.7 Riepilogo dei test della categoria 3.............................................................28
2.3.2.4 Test di coerenza interna sui file di versions_regexp (categoria 4)......................28
2.3.3 Organizzazione dei test...............................................................................................29
2.3.3.1 Test su singoli sistemi di banners (categoria 1)..................................................29
2.3.3.2 Parsing XML......................................................................................................30
2.3.3.3 Test di coerenza interna XML su banners e webapplications (categoria 3).......33
2.3.3.4 Test su versions_regexp (categorie 2 e 4)...........................................................35
2.3.3.5 Schema consuntivo per le categorie 2, 3 e 4.......................................................36
3
4. 3 Realizzazione..................................................................................................................................37
3.1 Implementazione................................................................................................................37
3.1.1 Codifica test-driven....................................................................................................37
3.1.1.1 Logica di controllo..............................................................................................37
3.1.1.2 Strategia di sviluppo dei test...............................................................................37
3.1.1.3 Schema assertivo................................................................................................38
3.1.2 Test per errori potenziali.............................................................................................39
3.1.3 Organizzazione dei sorgenti.......................................................................................40
3.1.4 Esecutore dei test........................................................................................................40
3.2 Utilizzo...............................................................................................................................41
PARTE SECONDA – Generazione di espressioni regolari
4 Analisi.............................................................................................................................................43
4.1 Descrizione del problema...................................................................................................43
4.2 Caratteristiche delle espressioni regolari dei file XML.....................................................44
4.3 Definizioni.........................................................................................................................46
4.4 Definizione dei tipi di soluzione........................................................................................47
4.4.1 Soluzione generica inaccettabile................................................................................48
4.4.2 Soluzione con parole comuni – Common word solution (cws)..................................48
4.4.3 Soluzione con token frequenti da cws – Frequent token solution from cws (ftscw)....48
4.4.4 Soluzione con pattern – Pattern solution (ps).............................................................48
4.4.5 Soluzione con token frequenti da ps – Frequent token solution from ps (ftsp)...........48
4.4.6 Soluzione con pattern complessi di interesse – Complex pattern solution (cps).......49
4.5 Prime considerazioni sulla genericità delle espressioni regolari........................................49
5 Progettazione..................................................................................................................................52
5.1 Workflow............................................................................................................................52
5.2 Algoritmo di Hunt-McIlroy................................................................................................54
5.2.1 Introduzione................................................................................................................54
5.2.1.1 Regola dei suffissi corrispondenti......................................................................55
5.2.1.2 Regola dei suffissi non corrispondenti...............................................................55
5.2.1.3 Formalizzazione..................................................................................................56
5.2.1.4 Tabella.................................................................................................................56
5.2.1.5 Tabella con traceback..........................................................................................58
5.2.1.6 Numero di sequenze arbitrario............................................................................59
5.2.2 Utilizzo.......................................................................................................................60
5.3 Problema di corrispondenza...............................................................................................61
5.3.1 Tabelle di corrispondenza...........................................................................................61
5.3.2 Modifica a run-time della greediness degli operatori di iterazione............................64
5.3.3 Ricerca strutturale delle corrispondenze....................................................................67
5.3.3.1 Prima regola........................................................................................................68
5.3.3.2 Seconda regola....................................................................................................70
5.3.3.3 Terza regola.........................................................................................................70
5.3.3.4 Common element dei sottoproblemi...................................................................71
5.3.3.5 Criterio di invalidazione.....................................................................................72
5.3.3.6 Criterio di arresto................................................................................................72
5.3.3.7 Assunzioni di corrispondenza e applicabilità delle regole..................................72
5.3.3.8 Osservazioni complementari sulla terza regola..................................................73
4
5. 5.3.3.8.1 Allineamento di coppia e numero di soluzioni possibili.............................73
5.3.3.8.2 Scelta del common element........................................................................74
5.3.3.8.3 Sottoproblemi generati dalla terza regola...................................................74
5.3.3.9 Algoritmo di ricerca strutturale delle corrispondenze........................................75
5.4 Espressione regolare per il singolo pattern........................................................................79
5.4.1 Introduzione dei token frequenti................................................................................79
5.4.2 Riduzione degli icp a pattern generici........................................................................80
5.4.3 Eliminazione forzata dell'operatore di opzionalità.....................................................81
6 Implementazione............................................................................................................................83
6.1 Struttura della libreria e unità di test..................................................................................83
6.2 Algoritmo di Hunt-McIlroy................................................................................................84
6.3 Algoritmo di corrispondenza..............................................................................................86
6.3.1 Matcher.......................................................................................................................86
6.3.2 Terza regola................................................................................................................88
6.4 Strutture dati per le corrispondenze...................................................................................92
6.4.1 Modello a strutturazione fissa....................................................................................92
6.4.2 Modello a strutturazione variabile..............................................................................94
6.5 Da strutture di corrispondenza a soluzioni.........................................................................97
6.5.1 Espressione regolare per ricavare una tupla di stringhe...........................................103
6.6 Soluzioni con token frequenti..........................................................................................104
6.6.1 Espressione regolare per ricavare i pattern...............................................................105
7 Utilizzo e diagnostica...................................................................................................................107
7.1 Esempio di utilizzo...........................................................................................................107
7.1.1 Input della generazione............................................................................................107
7.1.2 File risultato della generazione.................................................................................107
7.1.3 Rappresentazione dei risultati..................................................................................109
7.1.4 Albero delle soluzioni e scelta del risultato migliore...............................................110
7.1.5 Scelta della cws.........................................................................................................111
7.1.6 Confronto tra cws e ftscw...........................................................................................111
7.1.7 Confronto tra ps........................................................................................................111
7.1.8 Confronto tra ps e ftsp...............................................................................................113
7.1.9 Confronto tra ftscw e ftsp............................................................................................113
7.2 Politiche di logging..........................................................................................................114
7.3 Codice di diagnostica.......................................................................................................115
PARTE TERZA – Conclusioni e bibliografia
8 Conclusioni...................................................................................................................................117
8.1 Obiettivi...........................................................................................................................117
8.2 Valutazione delle prestazioni............................................................................................117
8.2.1 Valore dei test realizzati............................................................................................117
8.2.2 Valutazione del generatore di espressioni regolari...................................................118
8.2.2.1 Cenni al problema di misura di genericità........................................................120
8.3 Sviluppi futuri..................................................................................................................121
8.4 Aspetti quantitativi...........................................................................................................122
8.5 Conclusioni personali.......................................................................................................123
5
6. 9 Bibliografia...................................................................................................................................124
9.1 Test-driven development..................................................................................................124
9.2 Vulnerabilità.....................................................................................................................124
9.3 Espressioni regolari..........................................................................................................124
9.4 Algoritmo di Hunt-McIlroy e miglioramenti...................................................................124
9.5 Python..............................................................................................................................125
9.6 Framework di test.............................................................................................................125
9.7 Strumenti utilizzati...........................................................................................................125
9.8 Strumenti complementari.................................................................................................125
6
7. Abstract
Una delle tecniche di difesa dagli attacchi informatici è l'analisi delle vulnerabilità, che riguardano
essenzialmente due ambiti: i software e i protocolli.
Le vulnerabilità dei software si possono individuare esaminando degli aspetti specifici dei nodi
interessati; ad esempio, per i server, la prima risposta dopo lo stabilirsi della connessione, per i
server web alcuni contenuti di pagina, e in generale il numero di versione di programmi e sistemi
operativi.
Le vulnerabilità dei protocolli si possono scoprire inviando delle richieste particolari verso i nodi, e
analizzando le risposte.
L'insieme di conoscenza degli input da inviare e delle risposte attese, così come dei pattern da
ricercare, costituisce una base dati che viene ampliata nel tempo, man mano che vengono trovate
nuove minacce.
La prima parte del lavoro di tesi consiste nello sviluppo di test per la validazione dei nuovi
inserimenti in questa base dati.
Un supporto fondamentale dell'analisi delle vulnerabilià è costituito dalle espressioni regolari che
individuano determinate strutture all'interno dei flussi di dati. La generazione automatica di tali
espressioni regolari a partire da esempi costituisce la seconda parte della tesi.
Gli strumenti software e i test sviluppati nel corso del lavoro sono attualmente impiegati
dall'azienda committente.
7
8. PARTE PRIMA
Descrizione generale del lavoro
1 Introduzione
La presente tesi illustra il lavoro svolto presso Emaze Networks S.p.A.
1.1 Contesto
L'azienda realizza il software ipLegion, che comprende un componente detto Scout per l'analisi e il
monitoraggio delle vulnerabilità informatiche, e aveva la necessità di integrare i test già in uso con
delle nuove implementazioni.
La base di conoscenza per l'analisi è costituita da dati e logiche dedicate alle diverse vulnerabilità, e
viene ampliata man mano che se ne scoprono di nuove. Su tale base il sistema è in grado di asserire
che un dato endpoint manifesta una certa vulnerabilità.
In prima approssimazione, l'operazione avviene interrogando l'endpoint secondo una modalità che
può spaziare dal semplice utilizzo di un protocollo all'invio di particolari richieste, e analizzando le
sue risposte.
Alcune vulnerabilità sono presenti soltanto se si riscontrano determinate risposte, altre possono
essere presenti se l'endpoint risponde, o non risponde, secondo un certo schema.
La correttezza di questi schemi costituisce un aspetto cruciale del sistema di analisi di vulnerabilità,
perché un'eventuale imprecisione potrebbe non far scoprire una vulnerabilità o, al contrario,
generare dei falsi positivi.
1.2 Obiettivi e vincoli progettuali
L'obiettivo del lavoro è realizzare dei test che permettano di trovare in modo automatico delle
eventuali imprecisioni nella base di conoscenza.
I test prendono in esame alcune tipologie di errori, ritenuti particolarmente difficili da rilevare con
un'analisi diretta di un operatore umano.
Lo sviluppo dei test e del codice correlato deve seguire l'approccio del test-driven development,
conformemente alla linea di sviluppo aziendale.
Una volta consolidati i test, questi costituiranno lo strumento per il test-driven development di ogni
futuro aggiornamento della base dati del sistema. Ogni singolo test garantirà l'assenza di un certo
tipo di errore dalla nuova versione.
8
9. I test possono essere classificati in due tipologie: test su un modulo singolo, in cui si esamina
soltanto un certo aspetto del sistema di analisi di vulnerabilità, indipendentemente dal resto, e test di
integrazione, in cui si esamina l'interazione tra due endpoint.
I test più complessi hanno richiesto la progettazione e lo sviluppo di librerie software specifiche, tra
le quali ha assunto particolare rilievo quella per la generazione automatica di espressioni regolari
che catturassero le caratteristiche strutturali di un campione di stringhe noto.
Questo modulo è destinato ad essere utilizzato direttamente da un operatore umano, a supporto della
messa a punto di nuove espressioni regolari nella base di conoscenza del sistema. Permetterà inoltre
lo sviluppo di ulteriori test su modulo singolo per la validazione delle espressioni regolari presenti.
Il codice dei test e delle librerie è stato sviluppato in Python 2.4.1, lo stesso linguaggio e versione
del sistema di analisi di vulnerabilità. Si è utilizzato l'IDE PyCharm.
1.3 Fasi di lavoro e risultati
Il lavoro si è articolato in una fase preliminare di studio del linguaggio di programmazione e del
sistema di analisi di vulnerabilità, seguita dalla scelta del framework di gestione dei test.
Quindi si è trattata la progettazione e la realizzazione dei test; infine ci si è occupati del generatore
di espressioni regolari.
Il risultato del lavoro è stato il rilascio di versioni corrette dei file di configurazione interessati dai
test e di librerie di utilità generale disponibili per nuovi test futuri.
1.4 Articolazione della tesi
La presente tesi si articola in una prima parte riguardante i test e una seconda dedicata al modulo di
generazione di espressioni regolari.
Questo modulo ha costituito una parte cospicua del lavoro di tesi e, per il fatto che è interessato da
problematiche diverse dalla quelle specifiche riguardanti i test – anche se rientra nello stesso
contesto progettuale – si è ritenuto di considerarlo in una sezione separata.
Di seguito, un breve riepilogo dei capitoli:
1. Si esaminano il contesto, gli obiettivi e i vincoli progettuali, le fasi di svoglimento del lavoro
e i suoi risultati e la struttura della tesi
2. Si illustrano le scelte per l'individuazione del framework di test e si analizzano e progettano
i test da sviluppare, suddividendoli in test di intgrazione e test su moduli singoli
3. Si descrivono gli aspetti rilevanti dell'implementazione dei test e si fornisce un esempio di
esecuzione
9
10. 4. Si analizza il problema della generazione delle espressioni regolari, individuando le
caratteristiche rilevanti delle espressioni regolari già esistenti e definendo di conseguenza le
tipologie di soluzione
5. Si presenta la progettazione del workflow per l'algoritmo di generazione, approfondendo
l'algoritmo di Hunt-McIlroy, il problema di corrispondenza e l'espressione regolare da
associare al singolo pattern
6. Si illustrano le modalità di sviluppo test-driven, i punti salienti dell'implementazione degli
algoritmi di Hunt-McIlroy e di corrispondenza, le strutture dati utilizzate per la descrizione
delle corrispondenze e l'articolazione del codice per ottenere le soluzioni con pattern e con
token frequenti
7. Si presentano degli esempi di utilizzo del generatore di espressioni regolari e si discute il
codice aggiuntivo sviluppato per la diagnostica del modulo
8. Si delineano le conclusioni del lavoro complessivo, valutandolo sulla base degli obiettivi del
progetto
9. Si elencano i riferimenti bibliografici
10
11. 2 Analisi e progettazione
2.1 Scelta dell'ambiente di testing
Il concetto di test-driven development è così radicato in Python che la stessa Python Standard
Library include due framework per scrivere delle unità di test: il più semplice doctest e unittest,
che danno il nome ai rispettivi package.
Scegliere di fondare i propri test su uno di questi due framework ha indubbiamente i grandi
vantaggi delle soluzioni standard; in particolare:
• ragionevole certezza dell'assenza di bug nel framework
• garanzia di supporto in versioni future di Python
• abbondanza di documentazione disponibile
Nella scelta del framework giocano un ruolo essenziale la semplicità e la sinteticità nell'utilizzo. Il
sorgente del test deve rendere il più possibile evidente la sua finalità e le sue caratteristiche
specifiche.
doctest, sviluppato nell'intento di massimizzare la semplicità di utilizzo, si dimostra non essere uno
strumento adeguato per questo progetto, perché presenta le problematiche seguenti:
• utilizzo prolisso, per la necessità di riprodurre una sessione interattiva
• non è immediatamente distinguibile la parte di test, perché inclusa in una docstring
• bassa modularità, perché la parte di test si trova nello stesso file del codice da testare
unittest richiede invece la scrittura di un vero codice di test: una soluzione forse meno immediata
ma molto più sintetica, che offre la possibilità di includere questo sorgente in un modulo separato.
Utilizzando unittest, la creazione di una campagna di test è associata alla creazione di script
supplementari preposti alla ricerca dei test, alla loro aggregazione ed esecuzione.
Esistono vari framework1 che estendono unittest automatizzando queste funzionalità.
Spesso è possibile restringere l'insieme dei test da eseguire al singolo package, file, classe o
metodo, senza dover ricorrere ad alcuno script aggiuntivo.
Questa funzionalità è particolarmente interessante, perché in caso di aggiornamenti limitati alla base
di conoscenza del sistema permette di effettuare una validazione rapida eseguendo soltanto i test per
le parti modificate.
Si sceglie di usare nose, per la sua ampia diffusione e disponibilità di documentazione e perché non
aggiunge a questi requisiti altre funzionalità non di interesse.
1 Si veda ad esempio http://packages.python.org/testing/
11
12. 2.2 Test di integrazione
2.2.1 Contesto e obiettivi
L'obiettivo dei test di integrazione da sviluppare è quello di verificare la corretta individuazione di
alcune vulnerabilità legate all'interazione tra l'endpoint da analizzare e un server.
Il sistema di analisi memorizza, per ogni vulnerabilità individuata, le informazioni rilevanti. Tra
queste vi sono:
• codice identificativo aziendale univoco
• metodo di rilevazione
• numero di porta lato server
• protocollo di trasporto
• servizio
• flag per indicare se l'interazione utilizza SSL
• una o più risposte rilevanti dell'endpoint
• nome del software interessato
La scelta effettiva di quali informazioni memorizzare dipende dalla vulnerabilità specifica.
I test riguardano alcuni casi in cui è noto il valore di queste informazioni: si vuole verificare che i
valori rilevati dal sistema di analisi di vulnerabilità corrispondano a quelli noti.
Le vulnerabilità oggetto dei test riguardano:
• sistemi Check Point (vulnerabilità 10720) e sistemi non Check Point, su cui si testa l'assenza
della vulnerabilità
• sistemi NetBus2 (vulnerabilità 17993)
• sistemi multimediali conformi al protocollo H.323 (vulnerabilità 10099): la stessa
vulnerabilità è testata in riferimento a varie implementazioni (server Tandberg, server
Aethra) e loro servizi (Tandberg Alerting, Aethra Alerting e Aethra Connect) e si testa la sua
assenza su un server non H.323
Per comunicare con il client da analizzare è necessario predisporre un mock locale che simuli il
comportamento lato server all'origine della vulnerabilità.
Il lato client deve utilizzare la funzione del sistema di analisi di vulnerabilità gatherfunc, che lo
inizializza e lo collega al server sulla base di queste caratteristiche:
12
13. • numero di porta lato server
• protocollo di trasporto
• servizio
• indirizzo IP del server
• oggetto Session
L'oggetto Session è un descrittore dell'analisi di sicurezza del nodo corrente, di cui si tiene conto
durante l'analisi di vulnerabilità.
Nel sistema di analisi di vulnerabilità ogni protocollo noto o situazione di interesse ha un proprio
modulo – nella directory modules – che contiene un'implementazione specifica di gatherfunc.
Una volta invocata gatherfunc, si può ottenere la lista delle vulnerabilità rilevate invocando
getIdentifiedVulnerabilities sull'oggetto Session.
Ogni vulnerabilità è un oggetto Vulnerability da cui si possono ricavare le caratteristiche descritte.
2.2.2 Classi di test
Poiché ogni classe di test è in corrispondenza uno a uno con le variabili da passare a gatherfunc,
queste caratteristiche possono essere incluse come attributi di classe nelle classi di test.
Al contrario, le caratteristiche lato server dipendono dal particolare simulatore utilizzato; la stessa
logica di test potrebbe avvalersi in futuro di simulatori diversi.
Ogni classe di test avrà pertanto queste caratteristiche:
• attributi di classe port, transport, service, host_ip e session
• metodo setUp per avviare il simulatore lato server
• metodo di test, in cui si utilizzano gatherfunc e getIdentifiedVulnerabilities
• metodo tearDown per la finalizzazione lato server
I metodi setUp e tearDown implementano quelli definiti in unittest.TestCase2.
Dato che ogni protocollo ha una propria implementazione di gatherfunc, l'importazione di questa
funzione dipende dal protocollo coinvolto nel test.
Si sceglie di raggruppare in uno stesso file le classi di test relative a uno stesso protocollo.
2 Si tratta più propriamente di un override. Poiché il metodo di unittest.TestCase ha come implementazione la sola
istruzione pass, da un punto di vista logico si può pensare come un'implementazione di un interfaccia. In Python non
esistono delle vere e proprie interfacce: si tratta comunque di classi, anche se con i metodi contenenti solo pass.
13
14. 2.2.3 Oggetti mock
I test devono utilizzare degli oggetti mock che si sostituiscano agli oggetti Session e Vulnerability
utilizzati normalmente dal sistema di analisi di vulnerabilità.
I mock devono essere utilizzabili da gatherfunc nello stesso modo degli oggetti standard, ma
limitarsi a modellare gli aspetti dell'oggetto effettivamente di rilievo nei test.
La funzione gatherfunc utilizza un oggetto Session che descrive l'analisi di sicurezza in corso su un
certo endpoint. In particolare ne invoca alcuni metodi.
L'oggetto mock Session deve tenere traccia di:
• indirizzo IP dell'endpoint oggetto dell'analisi, di default 'localhost'
• parametro sd (utile per test futuri, qui non utilizzato), di default None
• una lista di oggetti Vulnerability corrispondenti alle vulnerabilità rilevate
e deve implementare i seguenti metodi con queste funzionalità:
• getScoutdict: restituisce il parametro sd (utile per test futuri)
• getVirtualHost: restituisce l'indirizzo IP dell'endpoint
• getIdentifiedVulnerabilities: restituisce la lista di oggetti Vulnerability
• getLogger: restituisce il logger denominato scout3
• reportVulnerability: inserisce un oggetto Vulnerability nella lista; le caratteristiche sono
passate come parametri; il metodo deve verificare che i tipi degli argomenti siano corretti
Per Vulnerability è sufficiente definire una classe con un attributo per ciascuna informazione che
caratterizza la vulnerabilità.
2.2.4 Lato server
La Python Standard Library include un modulo per facilitare la codifica di server, SocketServer.
Tutti i test da sviluppare riguardano interazioni sincrone basate su TCP, perciò ha interesse
utilizzare la classe SocketServer.TCPServer.
La classe inclusa nella libreria standard non fornisce garanzie sulle tempistiche in cui un indirizzo
lato server possa essere riutilizzato. Dopo la finalizzazione, il socket potrebbe restare in uno stato di
TIME_WAIT anche per qualche secondo, e restituire un errore alla richiesta di connessione al proprio
indirizzo4.
Una classe ReuseTCPSocketServer deve estendere SocketServer.TCPServer ridefinendo server_bind.
3 Il sistema di logging standard di Python (modulo logging della Python Standard Library) utilizza una struttura
gerarchica di logger identificati da un nome.
4 Si veda http://docs.python.org/library/socket.html, in particolare la parte su socket.SO_REUSEADDR.
14
15. La politica di interazione con il client viene definita in un metodo handle di una classe specifica per
ciascun simulatore lato server, che implementa il metodo handle di
SocketServer.BaseRequestHandler.
Per rendere rilevabili le vulnerabilità oggetto dei test, i simulatori devono effettuare le seguenti
interazioni con i client:
• Checkpoint: il server deve inviare al client un banner5 cpResp0 specifico e chiudere la
connessione
• H.323: se il server riceve una certa request H323Req0 deve inviare una response
H323Resp0,i, che dipende dal caso specifico di implementazione del protocollo (l'indice i è
diverso nei casi Tandberg Alerting, Aethra Alerting e Aethra Connect)
• Netbus2: se il server riceve una certa request nbReq0 deve inviare una certa response
nbResp0 e chiudere la connessione
L'attivazione del simulatore lato server da parte di una classe di test deve comportare l'inizio di un
nuovo flusso parallelo di esecuzione, che possa comunicare con il client mentre quest'ultimo è in
esecuzione.
Una classe MultiThreadTCPServer corrisponde al mock lato server. La classe deve contenere un
metodo spawnServerThread che avvia il servizio in un thread separato.
Il metodo deve effettuare il bind del server a un indirizzo IP e un numero di porta specificati e
avviare il thread di servizio specificando il gestore di richieste desiderato. MultiThreadTCPServer
deve essere istanziato passando un parametro serverClass per specificare il gestore di richieste.
MultiThreadTCPServer deve infine contenere un metodo per la finalizzazione del server.
MultiThreadTCPServer delega l'operazione di bind al metodo server_bind di ReuseTCPSocketServer,
un override del metodo di SocketServer.TCPServer in cui si imposta l'opzione del socket che ne
permette il riutilizzo anche se in stato di TIME_WAIT.
Al momento della creazione di un'istanza di MultiThreadTCPServer deve poter essere specificato un
attributo opzionale banner, per indicare una stringa che il server invia al client una volta aperta la
connessione.
In tutti i test in cui si verifica l'assenza di una certa vulnerabilità è sufficiente ricorrere a un request
handler lato server molto semplice, che si limiti a inviare un banner al client su una connessione
aperta e la chiuda. Si tratta in effetti di un mock di un server qualsiasi che non dia luogo alla
situazione della vulnerabilità da testare.
Questa logica è inclusa in una classe FakeRequestHandler.
5 In questo contesto con banner si intende il primo messaggio che il server invia al client una volta aperta la
connessione e terminati eventuali handshake preliminari.
15
16. Questo tipo di interazione con il client è la stessa da utilizzare nel caso del simulatore Checkpoint:
FakeRequestHandler può essere utilizzato anche in questo caso, specificando il banner cpResp0.
Figura 1: UML class diagram per i simulatori lato
server
Figura 2: UML class diagram con le classi dei test da
sviluppare
16
17. 2.2.5 Schema consuntivo
Si propone di seguito un UML class diagram che evidenzia le interazioni tra le classi interessate dal
test per i sistemi NetBus2.
La classe di test Netbus2Test utilizza la funzione gatherfunc per la predisposizione del lato client e
per la sua connessione al lato server. gatherfunc si serve dell'oggetto mock Session per registrare le
vulnerabilità trovate.
Lato server viene creato un oggetto MultiThreadTCPServer, a cui viene passato il callable
Netbus2Simulator. All'invocazione del metodo spawnServerThread, l'oggetto istanzia
ReuseTCPSocketServer passando il simulatore come request handler, e lo attiva in un thread
separato. Ad ogni oggetto MultiThreadTCPServer corrisponde un oggetto ReuseTCPSocketServer e
viceversa.
Gli altri test presentano un'interazione analoga tra le classi, e si differenziano per la classe di test, il
modulo del sistema di analisi con l'implementazione di gatherfunc e il request handler lato server,
che hanno comunque strutture analoghe. Alcuni test utilizzano il mock FakeRequestHandler come
request handler, che viene incluso nello schema riassuntivo come caso di rilievo.
Figura 3: UML class diagram riepilogativo con le classi coinvolte nel test per i sistemi NetBus2
17
18. 2.3 Test su moduli singoli
2.3.1 Contesto
Nella maggior parte dei casi una vulnerabilità interessa soltanto una certa tipologia di nodo, con
specifiche caratteristiche software o hardware.
Nell'analisi delle vulnerabilità che potrebbero interessare un endpoint remoto è fondamentale
ricavare il maggior numero di informazioni possibili riguardo all'endpoint.
2.3.1.1 Analisi di banner e pagine web (banners e webapplications)
Il sistema di analisi di vulnerabilità comprende un modulo che riconosce le caratteristiche
dell'endpoint prendendo in esame una qualche stringa che questo ha generato.
I test da sviluppare riguardano in particolare due directory – banners e webapplications – contenenti
dei file XML, uno per ogni contesto applicativo, che definiscono i legami tra questa stringa e le
caratteristiche dell'endpoint.
La directory banners è utilizzata nell'analisi delle prime risposte fornite dai server (dette banner), la
directory webapplications nell'analisi del <body> delle pagine web trasmesse.
Ogni file XML comprende degli elementi <software> con degli elementi <info> al loro interno:
ogni elemento <software> corrisponde a un insieme di caratteristiche dell'endpoint da individuare e
ogni elemento <info> corrisponde a una singola caratteristica.
Ogni elemento <software> ha un attributo name, suo identificativo, e un attributo re, che specifica
un'espressione regolare. Se la stringa da analizzare è conforme 6 all'espressione regolare, il sistema
di analisi riconosce nell'endpoint le caratteristiche riportate nell'elemento <software>
corrispondente.
La notazione è leggermente diversa nei file XML della directory webapplications, in cui si utilizza
un elemento <re> interno a <software> anziché un attributo di <software>.
A una stringa nota durante l'analisi di vulnerabilità sono applicate tutte le espressioni regolari
contenute negli elementi <software> del file XML afferente all'analisi in corso.
La stringa può essere conforme a nessuna, una o più espressioni regolari: ogni match individua un
insieme di certe caratteristiche dell'endpoint.
Ogni caratteristica può essere individuata in uno di questi due modi:
6 Per conformità si intende che la stringa deve generare un match con l'espressione regolare.
Più precisamente, detta s la stringa, R l'espressione regolare e L(R) il linguaggio individuato da R , deve essere
s 2 L(R)
18
19. • essere nota staticamente: la sola conformità della stringa all'espressione regolare rende
evidente un certo insieme di caratteristiche
• essere ricavata a run-time dalla stringa: l'espressione regolare comprende un gruppo
capturing che individua una porzione di stringa con il valore della caratteristica
L'espressione regolare contiene pertanto un gruppo capturing intorno ad ogni caratteristica
individuata nel secondo modo, e ogni gruppo capturing corrisponde a un elemento <info>.
Gli elementi <info> hanno i seguenti attributi:
• key: l'identificativo della caratteristica
• type: la modalità di valorizzazione della caratteristica
◦ type=”str” per una caratteristica valorizzabile staticamente
◦ type=”re” per una caratteristica valorizzabile con la sottostringa corrispondente a un
gruppo capturing dell'espressione regolare
• value: il valore della caratteristica
◦ se type vale ”str” il valore della caratteristica è riportato letteralmente
◦ se type vale ”re” si specifica il numero del gruppo7 dell'espressione regolare da cui
ricavare tale valore
Al termine di questa fase dell'analisi di vulnerabilità le caratteristiche dell'endpoint che sono state
individuate vengono memorizzate in un dizionario8, con una chiave per ogni matching che la stringa
ha provocato sulle espressioni regolari racchiuse negli elementi <software> del file XML.
La chiave è il valore dell'attributo name dell'elemento <software>. Il valore corrispondente alla
chiave è a sua volta un dizionario, avente per chiavi gli attributi key degli elementi <info> e per
valori le informazioni corrispondenti.
Una singola caratteristica può avere uno oppure vari valori possibili, tra i quali non si è in grado di
scegliere a questo punto dell'analisi di vulnerabilità.
In questo caso il relativo valore contenuto nel dizionario è una lista comprensiva di tutte le
possibilità.
Esempio
Si riportano due elementi <software> estratti dai file XML di configurazione.
Sono evidenziati in rosso grassetto i gruppi capturing delle espressioni regolari e i corrispondenti
riferimenti negli elementi <info> ai numeri dei gruppi.
7 Seguendo lo standard di Python, i numeri dei gruppi iniziano da 1 e conteggiano unicamente i gruppi capturing. Il
gruppo 0 individua l'intera espressione regolare.
8 Inteso come struttura dati di Python.
19
20. <!-- WebLogic Server Administration Console</title> Welcome to WebLogic Server Version:
FakeServer3</p> -->
<software name="Weblogic" eid="17599">
<re><![CDATA[WebLogic Server Administration Console</title>.*WebLogic Server Version:
([w.]+)</p>]]></re>
<info key="version" type="re" value="1"/>
<info key="console" type="str" value="detected"/>
</software>
<!-- <title>XenServer 5.6.0</title> -->
<software name="Citrix XenServer">
<re><![CDATA[<title>XenServer ([w.]+)</title>]]></re>
<info key="version" type="re" value="1"/>
</software>
Se il file XML fosse composto da questi soli due elementi e la stringa banner fosse
'<title>XenServer 2.3</title>'
il dizionario generato sarebbe
{ 'Citrix XenServer': {version: '2.3'} }
Per ogni elemento <software> deve essere riportato nel file XML almeno un commento con un
esempio effettivo di possibile stringa attivatrice. Nei file XML contenuti in banners questa stringa è
un banner, nei file in webapplications è una porzione del <body> di una pagina web.
Il commento può essere inserito in un punto qualsiasi del file.
Solitamente viene posto immediatamente al di sopra dell'elemento <software> corrispondente,
oppure viene posizionato un blocco di commenti prima del relativo blocco di elementi <software>.
In ogni caso, si tratta soltanto di consuetudini di comodo che possono essere disattese senza
compromettere il corretto funzionamento del software di analisi di vulnerabilità.
2.3.1.2 Analisi di numeri di versione (versions_regexp)
Spesso le vulnerabilità riguardano soltanto alcune versioni di un certo software.
Il sistema di analisi comprende un modulo che a seconda del numero di versione di un software
20
21. noto ne indica la vulnerabilità consultando una base di conoscenza XML.
I file XML sono inclusi in una directory chiamata versions_regexp.
Ogni file XML comprende degli elementi <vulnerability> che hanno come discendenti degli
elementi <regexp>. Ogni elemento <vulnerability> ha un attributo id il cui valore identifica
univocamente la vulnerabilità; ogni elemento <regexp> ha un attributo value il cui valore è
un'espressione regolare.
Se il numero di versione che viene esaminato a run-time è conforme a una delle espressioni regolari
contenute in un elemento <vulnerability>, il sistema dichiara che quel numero di versione è affetto
dalla vulnerabilità avente come identificativo l'attributo id di quell'elemento <vulnerability>.
Tutte le espressioni regolari di questi file devono essere obbligatoriamente terminate da un
ancoraggio per la fine della stringa. In altre parole, l'espressione regolare deve sempre individuare il
termine del numero di versione. L'inizio del numero di versione può invece non essere compreso
nell'espressione regolare.9
Esempio
<vulnerability id="18272">
<regexp value="7.0.[0-6].*$"/>
<regexp value="7.0$"/>
<regexp value="6.0.[1-2]?[0-9]$"/>
<regexp value="6.0.30$"/>
<regexp value="6.0$"/>
</vulnerability>
<vulnerability id="18395">
<regexp value="7.0.1[0-1]$"/>
<regexp value="7.0.[0-9]$"/>
<regexp value="7.0$"/>
</vulnerability>
9 Ciò può essere utile per trattare casi del tipo “numero di versione che termina con .2”, in cui cioè non si specifica la
parte iniziale del numero di versione.
21
22. 2.3.2 Tipi di test
I test da sviluppare possono essere suddivisi in due tipologie:
• test su singoli sistemi noti
• test di coerenza interna ai file XML
I primi devono verificare che per alcune configurazioni specifiche, corrispondenti a determinati
elementi di singoli file XML, il risultato dell'elaborazione da parte del sistema di analisi delle
vulnerabilità sia quello atteso.
I secondi hanno lo scopo di accertare che i file XML non contengano degli errori interni, che ne
compromettano la correttezza semantica.
È opportuno distinguere i test che analizzano i file XML contenuti nelle directory banners e
webapplications da quelli contenuti in versions_regexp, perché la struttura e il valore semantico dei
file sono molto diversi nei due casi.
Risultano quindi queste quattro tipologie di test:
banners e webapplications versions_regexp
Su singoli sistemi test di categoria 1 test di categoria 2
Di coerenza interna ai file XML test di categoria 3 test di categoria 4
Si prende ora in esame ciascuno di questi casi per formalizzarne i requisiti.
2.3.2.1 Test su singoli sistemi di banners (categoria 1)
I test da sviluppare riguardano alcuni banner specifici di singoli server SMTP, SNMP e Telnet, e
interessano pertanto solo i corrispondenti tre file contenuti in banners.
Ciascun test deve richiamare la funzione del sistema di analisi di vulnerabilità
scoutUtils.BannerParser, che prende in input un banner e un file XML e restituisce il dizionario
consuntivo, e verificare che per un particolare banner il risultato sia quello atteso.
Il test deve verificare che nel dizionario siano presenti tutte le chiavi attese e che ogni chiave abbia
il valore previsto. Nel caso in cui questo valore sia una lista, è sufficiente verificare che ogni valore
della lista attesa sia presente nella lista risultante.
In questo modo si prova la correttezza di alcuni elementi <software> basandosi su
scoutUtils.BannerParser; essendo una funzione esterna, questa funzione ha già delle proprie unità di
test che ne avvalorano la correttezza intrinseca.
Questo approccio trascura il caso altamente improbabile che un errore su un elemento <software>
22
23. possa essere compensato da un ulteriore errore sulla funzione conducendo a un risultato corretto.
2.3.2.2 Test su singoli sistemi noti di versions_regexp (categoria 2)
Si vuole predisporre un test per verificare che le vulnerabilità associate alle versioni del web server
Apache Tomcat siano quelle attese. Il test riguarda pertanto un unico file: tomcat.xml.
Per ogni vulnerabilità nel file, il test deve verificare che per un certo insieme di numeri di versione
la vulnerabilità venga rilevata e per un altro insieme no.
Esso deve utilizzare la funzione del sistema di analisi di vulnerabilità
software_version_postscan.isVulnerableSoftwareVersionRegexpList, che prende in input un numero
di versione e la lista di espressioni regolari corrispondenti a una certa vulnerabilità e ne indica
l'esistenza restituendo True o False.
Gli insiemi di numeri di versione oggetto delle asserzioni devono essere determinati a partire dalle
espressioni regolari del file XML, in modo che siano rappresentativi di tutti i casi possibili.
Il file tomcat.xml ha la seguente struttura (sono state omesse le parti non rilevanti in questo
contesto):
<vulnerability id="18272">
<regexp value="7.0.[0-6].*$"/>
<regexp value="7.0$"/>
<regexp value="6.0.[1-2]?[0-9]$"/>
<regexp value="6.0.30$"/>
<regexp value="6.0$"/>
</vulnerability>
<vulnerability id="18395">
<regexp value="7.0.1[0-1]$"/>
<regexp value="7.0.[0-9]$"/>
<regexp value="7.0$"/>
</vulnerability>
Quasi tutte le espressioni regolari individuano un unico numero di versione o ne specificano un
numero finito, imponendo dei range a cui devono appartenere alcune cifre in posizioni fisse. È
riconoscibile un pattern '.*' in una sola delle espressioni regolari e una parte opzionale in un'altra.
I range sono stati evidenziati in verde e i costrutti '.*' e l'indicatore di opzionalità in rosso.
23
24. Per ogni espressione regolare che individua un unico numero di versione, si include nel test
un'asserzione che verifichi la corrispondente vulnerabilità.
Per ogni range si verifica che siano vulnerabili le versioni corrispondenti agli estremi, ma non la
versione successiva all'estremo superiore se questo è minore di 9, né quella precedente all'estremo
inferiore se questo è maggiore di 0.
Per ogni parte opzionale e costrutto '.*' viene fatta almeno un'asserzione positiva che utilizzi
l'elemento e una che non lo utilizzi.
Per la vulnerabilità 18272 si fanno delle asserzioni sui seguenti numeri di versione:
• 7.0.0, 7.0.6.4: asserzioni positive dagli estremi del range; una utilizza il '.*'
• 7.0.7, 7.0.7.4: asserzioni negative dall'estremo superiore del range; una utilizza il '.*'
• 7.0: asserzione positiva per l'espressione regolare che individua un unico numero di versione
• 6.0.10, 6.0.29, 6.0.0, 6.0.9: asserzioni positive per gli estremi; due utilizzano il '?'
• 6.0.0.0, 6.0.3.0: asserzioni negative dagli estremi del range opzionale
• 6.0.30, 6.0: asserzioni positive dalle espressioni regolari per il singolo numero di versione
Per la vulnerabilità 18395 si fanno delle asserzioni sui seguenti numeri di versione:
• 7.0.10, 7.0.11: asserzioni positive per gli estremi del range
• 7.0.12: asserzione negativa dagli estremi del range
• 7.0.0, 7.0.9: asserzioni positive per gli estremi del range (che non ha corrispondenti
asserzioni negative)
• 7.0: asserzione positiva per l'espressione regolare per il singolo numero di versione
A questa base di asserzioni possono essere aggiunte a piacere asserzioni positive o negative per altri
numeri di versione senza compromettere il valore del test.
In questo modo si prova la correttezza delle espressioni regolari, demandando alla funzione del
sistema di analisi di vulnerabilità la verifica della conformità tra il numero di versione e una delle
espressioni regolari che interessano una certa vulnerabilità. Anche in questo caso, la correttezza
intrinseca della funzione esterna è avvalorata da un'unità di test già disponibile e valgono
considerazioni analoghe a quelle del caso precedente.
Il test valida le espressioni regolari del file XML a fronte di esempi. Ciò è particolarmente utile nel
mantenimento del file: ogni modifica futura alle espressioni regolari dovrà essere accompagnata da
una modifica o un'estensione di questo test. Il test segnalerà eventuali modifiche accidentali.
24
25. 2.3.2.3 Test di coerenza interna sui file di banners e webapplications (categoria 3)
I file XML di banners e webapplications sono elenchi di elementi <software> accompagnati dai
relativi commenti. Ad esempio:
<!-- <title>XenServer 5.6.0</title> -->
<software name="Citrix XenServer">
<re><![CDATA[<title>XenServer ([w.]+)</title>]]></re>
<info key="version" type="re" value="1"/>
</software>
Ognuno di questi blocchi contiene una e una sola espressione regolare, uno o più elementi <info> ed
è associato a uno o più commenti con esempi di banner conformi all'espressione regolare. Per
motivi di praticità d'uso, i commenti sono talvolta raggruppati in una sorta di indice, lontano dai
rispettivi elementi <software>. Il file può comprendere ulteriori commenti con testo arbitrario.
Si vuole verificare che:
• non vi siano state alterazioni accidentali dei commenti o delle espressioni regolari, come
l'immissione di caratteri, la cancellazione o la modifica
• non compaiano ripetizioni di porzioni del file XML, e che uno stesso elemento <software>
non compaia più di una volta
Non è possibile creare test che soddisfino completamente tali richieste; è possibile approssimare la
soluzione in modo soddisfacente con dei test che rendano queste caratteristiche ideali altamente
probabili.
Tra tutti i test, illustrati di seguito, quelli fondamentali verificano, per ogni file XML ed elemento
<software>, la correttezza sintattica dell'espressione regolare, e la coerenza tra espressione regolare,
commenti ed elementi <info> con type=”re”.
2.3.2.3.1 Test di correttezza sintattica
Un test sulla correttezza sintattica del file XML non è necessario, perché gli IDE utilizzati ne
effettuano già una validazione continua, e un eventuale errore verrebbe segnalato immediatamente
all'operatore.
Deve essere invece inclusa in un test la validazione sintattica delle espressioni regolari. Un
eventuale errore verrebbe segnalato soltanto nella fase successiva di analisi di vulnerabilità, che può
essere eseguita anche molto tempo dopo l'aggiornamento della base dati, e comunque in modo
indipendente.
25
26. 2.3.2.3.2 Test di coerenza tra espressione regolare e commenti
Nell'introduzione di nuovi elementi <software> nel file XML, è possibile che l'operatore dimentichi
di inserire i commenti corrispondenti, o che non aggiorni correttamente un commento a fronte di
una modifica dell'espressione regolare, provocando un disallineamento che non è segnalato neanche
durante l'analisi di vulnerabilità.
Considerando le dimensioni del file e la possibile distanza tra l'elemento <software> e il relativo
commento, la corrispondenza può non essere evidente, ed è possibile incorrere in errori umani.
Per provare la coerenza tra espressioni regolari e commenti, è necessario predisporre un test che
verifichi, per ogni file XML, che non vi siano espressioni regolari prive di commenti
corrispondenti.
2.3.2.3.3 Test di coerenza tra espressione regolare ed elementi <info> con type=”re”
Per la coerenza tra espressioni regolari ed elementi <info>, si prevede un test che verifichi l'assenza
di gruppi capturing privi del corrispondente elemento <info> con type=”re”.
2.3.2.3.4 Test di coerenza tra espressione regolare, commenti ed elementi <info> con type=”re”
Per verificare che ogni elemento <info> con type=”re” corrisponda a un gruppo capturing si utilizza
un approccio diverso.
Come confermato dal test descritto precedentemente, in ciascun file ogni espressione regolare ha un
commento corrispondente; quindi il test è costruibile passando ogni commento alla funzione del
sistema di analisi di vulnerabilità preposta all'esame dei banner.
La funzione ricerca, nel file specificato, dei match con le espressioni regolari degli elementi
<software> e aggiorna di conseguenza il dizionario consuntivo dell'analisi di vulnerabilità.
Il test fallisce se si verifica un'eccezione, individuando in questo modo i disallineamenti tra i gruppi
capturing dell'espressione regolare e gli elementi <info> con type=”re” per i commenti del file.
2.3.2.3.5 Test per ripetizioni
In alcuni casi, la ripetizione di una porzione di file XML causa un errore di sintassi, o relativa alle
regole XML o a quelle della base di conoscenza del sistema.
Entrambi questi casi non sono interessanti, per le ragioni già discusse: il primo è rilevato
immediatamente dal validatore a bordo dell'IDE, il secondo è difficilmente producibile da un
operatore, e viene comunque segnalato nell'analisi di vulnerabilità.
Rimane aperta la possibilità di ripetizioni di elementi <info> all'interno di elementi <software>, o di
elementi <software> all'interno del file XML. Nei file di webapplications, la ripetizione di elementi
26
27. <re>10 all'interno di <software> provocherebbe invece una trasgressione della sintassi standard, e
rientra nella casistica precedente.
La ripetizione di elementi <info> non produce errori e non è necessario predisporre dei test che ne
verifichino l'assenza.
Al contrario, è fondamentale garantire che ogni elemento <software> sia unico all'interno del file,
se non altro per ragioni di manutenibilità. Se vi fossero due ripetizioni di uno stesso elemento,
l'operatore potrebbe modificare una delle due copie senza modificare la seconda, di cui potrebbe
perfino ignorarne l'esistenza.
Poiché ogni elemento <software> è in corrispondenza uno a uno con l'espressione regolare al suo
interno, è sufficiente testare l'unicità di ogni espressione regolare all'interno di ciascun file.
2.3.2.3.6 Test sugli elementi <info> sospetti
Un caso di alterazione particolarmente insidioso per la sua difficoltà di rilevazione manuale è quello
in cui l'attributo type=”re” di un elemento <info> sia stato erroneamente scambiato con type=”str”.
Si definisce sospetto un elemento <info> per cui è plausibile che sia avvenuto tale scambio. Si
dichiara sospetto un elemento <info> per cui sussistono le condizioni seguenti:
• type vale ”str”
• value è un numero naturale piccolo
• l'elemento non è incluso in una lista di <info> da escludere a priori
Gli elementi <software> della base di conoscenza del sistema hanno espressioni regolari che
tipicamente comprendono due o tre elementi <info>, talvolta quattro e solo raramente un numero
maggiore.
Poiché in questo tipo di casi di errore il value si riferisce a un gruppo di un'espressione regolare, è
ragionevole aspettarsi che si tratti di un numero naturale piccolo. In prima approssimazione, una sua
limitazione superiore può essere costituita dal numero di gruppi capturing dell'espressione regolare.
È conveniente introdurre una certa tolleranza a questa valutazione, per escludere quei rari casi di
aggiornamento errato in cui si riduce il numero di gruppi dell'espressione regolare senza eliminare
gli elementi <info> corrispondenti.
Questo tipo di errore non verrebbe rilevato dal sistema di analisi di vulnerabilità, perché type=”str”
induce il sistema a considerare il numero come il valore letterale di una caratteristica.
Si ritiene ragionevole fissare questa tolleranza a 10, nella convinzione che è pressoché impossibile
che l'operatore elimini ben dieci gruppi capturing dall'espressione regolare senza notare dieci
elementi <info> di troppo.
Prendendo in considerazione quello che nella pratica è molto frequente, un elemento <info> che
abbia una delle seguenti caratteristiche non è mai considerato sospetto:
10 Si utilizzano elementi <re> soltanto nei file di webapplications. In quelli di banners l'espressione regolare è contenuta
nell'attributo re di <software>.
27
28. • key=”sp”
• key=”operating-system.release” e value=”7” in un elemento <software> dove vi sia un
elemento <info key=”operating-system.type” type=”str” value=”Windows”/> e non vi siano
altri elementi <info> con la stessa key
Queste due regole escludono dai casi sospetti i numeri di service pack (qui indicato con “sp”) e la
release 7 di Windows.
Si predispone un test che, per ogni value di un elemento <info> sospetto, verifica che tale value
compaia nell'espressione regolare dell'elemento <software> in cui è contenuto l'elemento <info>.
In caso di fallimento, questo test segnala una situazione solo potenzialmente errata.
2.3.2.3.7 Riepilogo dei test della categoria 3
Ognuno dei seguenti test si sviluppa in una versione per banners e una per webapplications; tutti i
test prendono in esame singolarmente ogni file XML della directory:
• ogni espressione regolare deve essere sintatticamente corretta
• ogni espressione regolare deve essere conforme ad almeno un commento
• ogni gruppo capturing di ciascuna espressione regolare deve avere un corrispondente11
elemento <info> con type=”re”
• ogni elemento <info> con type=”re” deve corrispondere ad un gruppo capturing
nell'espressione regolare
• ogni espressione regolare deve essere unica
• ogni elemento <info> sospetto deve avere un value che compare nell'espressione regolare
corrispondente
2.3.2.4 Test di coerenza interna sui file di versions_regexp (categoria 4)
L'obiettivo di questi test è quello di rendere improbabili eventuali errori umani nell'immissione delle
espressioni regolari utilizzate per filtrare i numeri di versione.
Innanzitutto è opportuno predisporre due test per verificare rispettivamente la correttezza sintattica
delle espressioni regolari e la conformità rispetto alla regola di terminazione con l'ancoraggio per la
fine della stringa.
11 Con “corrispondente” si intende che deve trovarsi nello stesso elemento <software>.
28
29. I numeri di versione contengono spesso dei punti, che nell'espressione regolare vengono individuati
da '.'.
Se dovesse mancare l'escaping del carattere speciale '.', l'espressione regolare presenterebbe un
errore che il sistema di analisi di vulnerabilità non potrebbe individuare automaticamente. Infatti
l'espressione regolare risulterebbe:
• sintatticamente corretta
• più generale di quella corretta
rendendo l'errore non individuabile né dal test precedente né da alcun test puntuale sui matching
attesi.
È opportuno testare esplicitamente la presenza del carattere di escape prima di ogni punto contenuto
in un'espressione regolare. Vista la frequenza dello schema '.*' nella base di conoscenza, il test deve
permettere che non vi sia un backslash prima di esso.
Il test rileva un anomalia anche nel caso di utilizzo corretto del punto non preceduto da backslash,
comunque altamente improbabile in questo contesto. Nel suo output, il test deve rendere evidente
che i casi individuati sono errori soltanto potenziali.
Riassumendo, i test da realizzare devono verificare:
• correttezza sintattica delle espressioni regolari
• presenza nelle espressioni regolari dell'ancoraggio per il termine della stringa
• presenza nelle espressioni regolari dell'escaping per ogni '.', a meno che questo non preceda
un '*'
2.3.3 Organizzazione dei test
L'aspetto più complesso dei test descritti è il parsing XML. I test della categoria 1 possono
demandarlo alla funzione scoutUtils.BannerParser, per gli altri serve un'implementazione specifica.
I test della categoria 1 possono essere progettati a parte.
2.3.3.1 Test su singoli sistemi di banners (categoria 1)
Ricordando che il risultato dell'analisi di vulnerabilità è un dizionario con chiavi corrispondenti a
stringhe singole o a liste, è opportuno svincolare la logica di test da questa distinzione.
Si sceglie quindi di definire BaseTestCaseForBanners con un metodo assert_inStrOrList, una
versione specializzata di unittest.TestCase. Tutte le classi contenenti i test ereditano da
BaseTestCaseForBanners.
29
30. Figura 4: UML class diagram per i test della categoria 1
2.3.3.2 Parsing XML
Per il parsing dell'XML si sceglie di usare un approccio di tipo SAX 12, per il quale è disponibile un
modulo della Python Standard Library.
Si tratta di implementare l'interfaccia13 xml.sax.ContentHandler, e in particolare le callback
richiamate all'individuazione di un tag di inzio di un elemento, di fine di un elemento e di caratteri
interni agli elementi.
Una prima implementazione dell'interfaccia è costituita da BaseReValidatorXmlHandler, che correda
il parser di un attributo lista per salvare le espressioni regolari trovate.
Viene implementato il metodo __init__, per l'inizializzazione della lista, e reset, per ripristinare la
situazione iniziale in cui la lista è vuota.
Le specializzazioni successive sono invece distinte tra classi per banners e webapplications e classi
per versions_regexp, perché nei due casi serve tenere traccia di informazioni diverse.
Nel primo caso i dati da salvare sono i seguenti:
12 Simple API for XML.
13 Non esistendo delle vere e proprie interfacce in Python, si tratta in realtà di una classe i cui metodi contengono
esclusivamente l'istruzione pass.
Visto che la distinzione tra classe e interfaccia riguarda più l'ereditarietà che non l'entità in sé, nei diagrammi UML
delle classi si preferisce utilizzare comunque la freccia tratteggiata verso la classe base, ma omettendo l'esplicito
prefisso <<interface>> al nome della classe base.
30
31. • numero di gruppi capturing di ogni espressione regolare
• espressioni regolari che compaiono più di una volta nel file
• valori sospetti dell'attributo value di <info>, associati all'espressione regolare corrispondente
Dovranno perciò essere ridefiniti i metodi __init__ e reset per inizializzare e reinizializzare le
relative strutture dati.
Deve essere introdotta un'ulteriore specializzazione per distinguere il parsing dei file in banners da
quelli in webapplications, perché nei due contesti le espressioni regolari sono specificate in
posizioni diverse dell'XML.
Nei file di banners l'espressione regolare è riportata come valore di un attributo re, in quelli di
webapplications è il contenuto di un elemento <re>.
La specializzazione per i file di banners può finalmente effettuare il parsing.
Si aggiungono a questo livello degli attributi per salvare lo stato del parser, cioè delle variabili
riferite all'ultima espressione regolare rilevata, che alla ricezione del tag di chiusura </software>
devono essere inserite nelle strutture dati consuntive definite in RegexTestReValidatorXmlHandler.
La specializzazione per i file di webapplications può essere definita in modo analogo, ma il fatto
che le espressioni regolari non si trovino all'interno di tag rende necessaria l'implementazione del
metodo characters14 e l'introduzione di variabili aggiuntive:
• flag per indicare se è in corso il parsing di un'espressione regolare
• buffer per l'espressione regolare
Il flag viene inizializzato prima che il parser inizi l'analisi del file XML a un valore False. Ciò
richiede l'override dei metodo __init__ e reset.
Poiché il flag è definito durante tutto il parsing, il metodo characters può bufferizzare l'espressione
regolare solo nel caso in cui il valore del flag è True.
Nel caso dei numeri di versione, la sola informazione da salvare sono le espressioni regolari
associate a ciascuna vulnerabilità.
La classe VersionREValidatorXmlHandler deve comprendere un dizionario di espressioni regolari –
dello stesso livello degli attributi di RegexTestREValidatorXmlHandler – e alcune variabili utili nel
corso del parsing, analoghe a quelle di BannerREValidatorXmlHandler e
WebappREValidatorXmlHandler.
In particolare servono:
• un flag per indicare se il parser sta analizzando l'interno di un elemento <vulnerability>
14 Callback invocata alla ricezione di caratteri di dati, ossia di stringhe contenute all'interno di elementi XML.
31
32. • un buffer per l'identificativo della vulnerabilità corrente
• un buffer per le espressioni regolari associate alla vulnerabilità corrente
I metodi di parsing da implementare sono startElement ed endElement; non serve implementare
characters perché in questo tipo di file XML tutti i dati utili sono contenuti negli attributi all'interno
dei tag.
Il buffer per le espressioni regolari associate alla vulnerabilità corrente deve essere inizializzato
prima che inizi il parsing, in modo che alla lettura di </vulnerability> questo sia effettivamente
pronto per l'uso.
Figura 5: UML class diagram per le classi di parsing XML
32
33. 2.3.3.3 Test di coerenza interna XML su banners e webapplications (categoria 3)
I test di coerenza sui file XML si basano sui dati trovati nei file dai parser.
È conveniente che il parsing XML sia svolto prima di ogni test, e che i dati rilevanti vengano inclusi
come attributi di classe nelle classi di test.
Si sceglie di inserire il codice che attiva il parsing nel metodo setUpClass, ereditato da
unittest.TestCase, che viene eseguito alla creazione del relativo oggetto.
Poiché i test riguardano tutti i file delle due directory, in questa fase deve essere effettuato il parsing
di ciascun file.
Si sceglie di utilizzare un parser per banners e uno per webapplications; ognuno deve analizzare
ciascun file della propria directory.
Per ciascun file della directory setUpClass deve:
• effettuare il parsing del file
• bufferizzare le informazioni estratte dal parser salvandole negli attributi di RegexTest
• invocare il reset del parser
La classe deve comprendere varie strutture dati, una per ogni informazione di interesse, in cui sono
compresi tutti i dati derivanti dal parsing.
Ogni struttura dati deve organizzare queste informazioni raggruppandole a seconda del file di
provenienza.
Si sceglie di utilizzare due strutture dati separate per le informazioni dei file di banners e di
webapplications, perché si riferiscono a due contesti operativi diversi.
Analogamente, si sceglie di rendere specifici per l'una o l'altra directory anche i metodi di test. In
questo modo un test per una certa directory contiene asserzioni soltanto su una struttura dati riferita
a quella stessa directory, e la suddivisione tra i moduli banners e webapplications del sistema di
analisi di vulnerabilità si riflette anche sulle sue unità di test.
Si predispone un attributo di classe logger a cui indirizzare i messaggi da visualizzare al termine dei
test che devono segnalare degli errori potenziali15.
15 La politica di default di nose è quella di mostrare i messaggi dello standard output ( sys.stdout) solo in caso di
fallimento o errore di un test. Per messaggi da visualizzare sempre non è opportuno usare lo standard output.
33
34. Figura 6: UML class diagram per i test di categoria 3
34
35. 2.3.3.4 Test su versions_regexp (categorie 2 e 4)
Nel caso dei test su versions_regexp anche i test su singoli moduli utilizzano le informazioni
contenute nei file XML.
Il parsing viene richiamato, in modo del tutto analogo al caso precedente, da una classe base
TestCaseForVersions estesa da VersionRegexpTest, che comprende i test di coerenza interna ai file
XML (categoria 4), e da TomcatVersionsTest, che comprende il test su moduli singoli (categoria 2).
La logica che verifica che un numero di versione sia vulnerabile o meno non dipende dal modulo
singolo oggetto dei test di categoria 2, quindi si sceglie di inserirla come metodo della classe base.
Se in estensioni future del sistema si volesse predisporre un'unità di test di categoria 2 su un sistema
diverso da Apache Tomcat, la relativa classe dovrebbe estendere la classe base.
Figura 7: UML class diagram per i test delle categorie 2 e 4
35
36. 2.3.3.5 Schema consuntivo per le categorie 2, 3 e 4
I test delle categorie 2, 3 e 4 condividono l'infrastruttura delle classi preposte al parsing XML,
mentre i test della categoria 1 si basano su un sistema di classi indipendente.
Lo schema seguente riepiloga le classi coinvolte nei test delle categorie 2, 3 e 4. In particolare:
• la parte sinistra è preposta ai test di categoria 3
• la parte centrale è preposta al parsing XML
• la parte destra è preposta ai test delle categorie 2 ( TomcatVersionsTest) e 4
(VersionRegexpTest)
Figura 8: UML class diagram riassuntivo per i test delle categorie 2, 3 e 4
36
37. 3 Realizzazione
3.1 Implementazione
3.1.1 Codifica test-driven
3.1.1.1 Logica di controllo
La metodologia di sviluppo test-driven si basa sulla messa a punto di una logica di controllo – le
unità di test – come prima fase dell'implementazione, prima ancora di procedere alla scrittura del
codice.
Come strategia di sviluppo, il sorgente deve essere implementato verificandone continuamente la
correttezza mediante il codice di controllo. Dopo ogni nuovo inserimento nel sorgente viene
eseguito il codice di test, in modo da avere un riscontro continuo sulla correttezza
dell'implementazione.
Il principio di base è quello di individuare eventuali errori il prima possibile, evidenziandone la
causa.
Il sorgente da sviluppare è costituito dalle unità di test, da affiancare a risorse preesistenti, costituite
dai file di configurazione.
I file di configurazione possono essere utilizzati come logica di controllo per i test da sviluppare,
rendendo non strettamente necessario scrivere delle unità di test per l'implementazione dei test.
Con questa modalità di sviluppo i test vengono validati dai file di configurazione, che possono a
loro volta contenere errori: si può procedere in questo modo solo nei casi in cui gli errori nei file di
configurazione sono indipendenti da quelli dei test da sviluppare. In altre parole, i test devono
individuare gli errori dei file di configurazione e i file di configurazione quelli dei test.
Ciò è vero nei casi in cui il sorgente dei test è particolarmente semplice, e questa proprietà risulta
banalmente vera. Per ogni aspetto più complesso dei test è necessario predisporre un'unità di test di
secondo livello specifica. Questo sarà l'approccio tipico nell'implementazione del generatore di
espressioni regolari.
3.1.1.2 Strategia di sviluppo dei test
Conformemente alla logica di controllo descritta, si adottano le regole di codifica seguenti.
Il primo test viene effettuato con una prima stesura dell'unità di test, in cui si riportano gli import
necessari al file e uno stub della classe di test, che estende unittest.TestCase e viene implementata
con la sola istruzione pass. In questo modo si possono rilevare eventuali errori causati dagli import.
Si procede quindi con l'introduzione di un metodo di test, implementato con l'istruzione:
self.fail('UNIMPLEMENTED')
Il relativo test accerta che nose rilevi correttamente il test e ne segnali l'insuccesso. A questo punto
37
38. si rileva un eventuale errore nel nome del test16.
È utile mantenere questa riga come ultima istruzione per tutta la fase di implementazione del
metodo di test, e procedere inserendo il codice prima.
Siccome nose segnala il primo punto di fallimento di un test e ne interrompe l'esecuzione, tutti gli
eventuali errori imputabili al codice di nuova introduzione hanno la precedenza sul fallimento
causato dall'istruzione finale.
In questo modo si ha la certezza che nessun test di cui non è stata terminata l'implementazione
possa essere dimenticato dal programmatore: fino al momento di rimozione dell'istruzione di
fallimento il test continuerà ad essere segnalato da nose come fallimentare.
Sia nell'unità di test che nel sorgente del software è conveniente utilizzare l'istruzione pass come
segnaposto per istruzioni non implementate, in modo da rendere evidente il punto in cui proseguire
con l'implementazione.
Per annotare queste parti con indicazioni su come proseguire, o come modificare il sorgente, si
ricorre a commenti che inizino con todo, quasi una parola chiave nello sviluppo test-driven.
Molti IDE, tra cui PyCharm, evidenziano questi commenti provvisori in un modo particolare e
permettono di creare delle liste delle azioni ancora da fare.
È preferibile che un'unità di test effettui delle verifiche eterogenee sulla logica da testare, cercando
di spaziare su tutto il suo dominio.
Se il dominio presenta dei casi limite, è utile introdurre dei test per la loro verifica.
3.1.1.3 Schema assertivo
Ogni metodo di test è fondato su una o più asserzioni, che costituiscono le istruzioni di verifica di
correttezza.
unittest.TestCase mette a disposizione vari metodi per effettuare asserzioni, tra cui si sceglie di
utilizzarne un numero molto ristretto, in modo da rendere più evidente possibile la finalità
dell'asserzione:
• assertTrue: il metodo utilizzato più comunemente
• assertFalse: utilizzato quando l'aspetto più importante da testare è la falsità dell'argomento
• fail: utilizzato durante la fase di sviluppo dei test
• assertRaises: utilizzato per segnalare che un'istruzione solleva un'eccezione specificata
16 nose impone che sorgenti, package, directory di test siano conformi all'espressione regolare '(?:^|[b_.-])[Tt]est)', detta
testMatch. In questi file vengono individuati solo i test con nome conforme a testMatch e inclusi in classi che
ereditano da unittest.TestCase con nome conforme a testMatch.
Si veda https://nose.readthedocs.org/en/latest/usage.html .
38
39. Si preferisce scrivere esplicitamente il test di uguaglianza con l'operatore == anziché adottare il
metodo assertEquals.
Dato che in caso di test fallimentare o errato nose riporta il codice della riga di errore e un eventuale
messaggio, si aggiunge un messaggio di spiegazione se la riga di asserzione non rende evidente la
natura dell'errore.
3.1.2 Test per errori potenziali
Se il test per i valori <info> sospetti o quello di coerenza interna dei file XML di versions_regexp
rilevano una situazione anomala, non si può avere la certezza che si tratti di un errore. L'output deve
essere un messaggio che segnali il caso potenzialmente errato.
Anziché ricorrere ad asserzioni, si utilizzano dei messaggi nel log di nose nose.result, mappato da
un attributo logger della classe di test.
Per i valori sospetti, ad esempio, un messaggio di livello logging.WARNING viene generato se
l'elemento <info> sospetto ha un value che non compare nell'espressione regolare corrispondente e
un messaggio di livello logging.INFO riassume il numero di casi totali trovati.
Il comportamento di default di nose prevede di includere nell'output i messaggi indirizzati ai logger
gestiti dal framework.
Utilizzare un'asserzione sarebbe inopportuno anche perché i test si interromperebbero al primo caso
anomalo incontrato, già da solo sufficiente per dichiarare scorretta la configurazione in corso di test.
Dato che ogni caso anomalo per questi test potrebbe non essere un errore, è sensato ricercarli tutti.
Figura 9: Test per i valori <info> sospetti
39
40. 3.1.3 Organizzazione dei sorgenti
I file sviluppati nel corso del progetto si collocano nella directory testunits del sistema di analisi
delle vulnerabilità.
L'organizzazione della gerarchia interna a questa directory è stata definita tenendo conto delle
possibili estensioni future:
• tests: per i test, le loro librerie e le estensioni future
• extlib: per le librerie necessarie all'esecuzione dei test (nose)
• oldtests: per vecchi test
La directory tests si articola in:
• integration_tests: per i sorgenti dei test di integrazione
• unit_tests: per i sorgenti dei test su singolo modulo
• lib: per librerie utilizzate dai test
• mocks: per oggetti mock utilizzati dai test
• resources: per risorse aggiuntive di scenari specifici (request handler dei simulatori lato
server)
I test all'interno di unit_tests sono raggruppati a seconda del modulo del sistema di analisi di
vulnerabilità a cui si riferiscono. All'interno di unit_tests vi sono le due directory:
• bannersAndWebapps: per i sorgenti dei test sui file XML di banners e webapplications
• versions: per i sorgenti dei test sui file XML di versions_regexp
3.1.4 Esecutore dei test
All'interno della directory testunits/tests si predispone uno script in Python che effettua la ricerca e
il lancio dei test nelle subdirectory.
Allo script possono essere passati gli argomenti standard di nose17, con i quali si possono ad
esempio specificare dei criteri di filtraggio per eseguire soltanto un sottoinsieme dei test presenti.
17 Si veda http://nose.readthedocs.org/en/latest/usage.html.
40
41. 3.2 Utilizzo
Per l'esecuzione dei test si invoca dalla directory testunits/tests del sistema di analisi delle
vulnerabilità l'interprete Python, passando come argomento lo script esecutore main.py.
Di default, lo script avvia la ricerca e l'esecuzione di tutti i test presenti nella directory; utilizzando
dei parametri aggiuntivi, secondo le specifiche di nose, è possibile specificare quali test eseguire.
Nel suo output, nose genera un carattere per ogni test eseguito che ne riassume l'esito:
• '.' per un test con esito positivo
• 'F' per un test con esito negativo (asserzione disattesa)
• 'E' per un test con errori
Per i test con esito negativo o con errori viene generata una sezione con l'indicazione della riga che
ha fatto fallire il test, il suo codice e il tipo di errore generato ed eventuali messaggi correlati.
Si includono i messaggi indirizzati ai log gestiti da nose.
Infine nose mostra delle righe aggiuntive, con il numero di test eseguiti, il tempo di esecuzione
complessivo e l'esito della campagna di test, con il numero di test fallimentari o con errori se questi
sono presenti.
Esempi
Per eseguire i test di verifica dei file XML contenuti in banners e webapplications si procede in
questo modo:
In questo esempio ci si serve della variabile python, valorizzata con il percorso dell'interprete.
A main.py è passata come argomento la directory tests/bannersAndWebapps, in cui ricercare ed
eseguire i test.
Seguono alcuni caratteri punto, uno per ogni test eseguito, e il messaggio indirizzato al log
nose.result, che indica che non ci sono banner sospetti.
Dopo altri due punti e una riga di separazione sono riportati i dati consuntivi della campagna di test;
41
42. il messaggio finale 'OK' segnala che tutti i test della campagna hanno avuto esito positivo.
In questo secondo esempio si eseguono i test contenuti nell'unità di test test_transformPatterns, per
la funzione transformPatterns.
In questo caso la campagna di test è lanciata con il parametro -v (verbose). Per ogni test nose genera
un output più prolisso, che comprende nome e percorso del test, o stringa di documentazione, ed
esito.
Si mostra infine un esempio più significativo, non incluso nel progetto, con dei test fallimentari:
42
43. PARTE SECONDA
Generazione di espressioni regolari
4 Analisi
4.1 Descrizione del problema
La seconda parte di questa tesi prende spunto da un problema riscontrato dopo aver messo a punto i
test su moduli singoli per i file XML di banners e webapplications.
I test hanno evidenziato che le espressioni regolari associate ai blocchi <software>, il punto cruciale
nell'analisi degli endpoint remoti, spesso presentavano delle imprecisioni dovute ad errori umani.
Molti errori tipici sono stati individuati dai test sviluppati nella prima parte; tuttavia nessuna
campagna di test può garantire la totale assenza di errori umani, perché molti possibili errori non
dipendono soltanto dalla coerenza con gli esempi di banner (i commenti del file XML) o con gli
elementi <info>.
Si sviluppa uno strumento di generazione automatica che prenda in input degli esempi di banner18 e
fornisca delle possibili espressioni regolari conformi a tutti i banner di esempio. In altre parole, ogni
espressione regolare generata deve individuare un linguaggio in cui devono essere contenute tutte le
stringhe banner di input.
Lo strumento vuole essere innanzitutto un ausilio all'operatore umano, del quale si richiede
comunque un intervento finale di scelta dell'espressione regolare più adatta, o di aggiunta di
caratteristiche note che non risultino evidenti dagli esempi di input. Inoltre, in espansioni future
potrà essere utilizzato per costruire nuovi test.
Come obiettivo generale, le espressioni regolari in output devono riflettere il più possibile le
caratteristiche delle stringhe banner.
Questa linea guida deve essere precisata nel corso della fase di analisi, in cui si definiscono le
caratteristiche esatte che devono avere le espressioni regolari soluzioni del problema, anche in
relazione allo scenario applicativo.
18 Per praticità di trattazione, si includono nel termine generico “banner” anche le stringhe riportate nei file di
webapplications, parti del <body> di pagine web.
43
44. 4.2 Caratteristiche delle espressioni regolari dei file XML
Osservando le espressioni regolari riportate nei file XML di banners e webapplications si nota che
nella quasi totalità dei casi esse sono composte da una struttura scheletro di parole riportate
letteralmente, entro la quale si inseriscono delle parti variabili, come parti opzionali, insiemi di
caratteri validi, parti ripetute.
Un'espressione regolare con questa struttura individua delle stringhe in cui sono presenti certamente
delle sottostringhe comuni, che compaiono in un ordine fisso.
Si nota inoltre che le parti variabili non compaiono quasi mai all'interno di parole: tipicamente è
presente un carattere separatore tra una parola e una parte variabile.
Spesso le parole comprendono dei caratteri numerici (ad es. IMAP4, POP3).
Spesso le espressioni regolari comprendono tra le parti variabili delle character class 19, in molti casi
incluse in dei character set20 insieme ad altre character class o caratteri fissi.
In molti casi le character class sono 'w' o 'd', a cui spesso si affiancano il carattere fisso punto,
talvolta anche il trattino, l'underscore o lo spazio, singolarmente o congiuntamente.
In particolare è frequente il character set '[d.]' in corrispondenza ai numeri di versione.
Spesso sono presenti delle parti generiche indicate con '.*'.
Le espressioni regolari non sono quasi mai ancorate all'inizio o alla fine della stringa.
Ovviamente, le espressioni regolari includono dei gruppi capturing intorno alle strutture di interesse
corrispondenti agli elementi <info>, perlopiù intorno a parti variabili.
Questi costrutti conferiscono a porzioni di espressione regolare un valore semantico; dal punto di
vista della corrispondenza tra espressione regolare e stringhe banner di esempio sono del tutto
irrilevanti.
19 Una character class è un costrutto delle espressioni regolari che individua nelle stringhe una certa tipologia
predefinita di carattere. Ad esempio: 'w' individua un carattere alfanumerico, 'd' un carattere numerico.
20 Un character set è un costrutto delle espressioni regolari che individua nelle stringhe una certa tipologia di carattere
definita esplicitamente. Nella definizione si possono usare le character class. Ad esempio '[ws.]' individua un
carattere che è un alfanumerico o un carattere di spaziatura o un punto.
44
45. Esempio
<!-- OK Domino IMAP4 Server Release 7.0.2 ready Wed, 11 Jul 2007 14:03:48 +0200x0d -->
<!-- OK Domino IMAP4 Server Release 7.0.2FP1 ready Fri, 9 Jan 2009 13:32:07 +0100 -->
<!-- OK Domino IMAP4 Server Release 7.0.2FP3 ready Tue, 28 Jul 2009 16:08:50 +0200 -->
<software name="Lotus Domino" re="Domino IMAP4 Server Release ([w.]+) ready">
<info key="version" type="re" value="1"/>
</software>
<!-- <h1>BEA WebLogic Server 9.2 -->
<software name="Weblogic">
<re>074h1076BEA WebLogic Server ([d.]+)</re>
<info key="version" type="re" value="1"/>
<info key="cpe_version" type="re" value="1"/>
</software>
<!-- hostname POP3 server (Post.Office v3.6.3 release 105 with ZPOP version 1.0 ID# -->
<!-- hostname POP3 server (Post.Office v3.5.3 release 223 with ZPOP version 1.0 ID# -->
<software name="PostOffice" re="([w.-_]+) POP3 server (Post.Office v([w.]+)
release">
<info key="hostname" type="re" value="1"/>
<info key="version" type="re" value="2"/>
<info key="operating-system.type" type="str" value="MacOSX"/>
</software>
<!-- Microsoft Windows CE Version 4.10 (Build 908) -->
<!-- Microsoft Windows CE Version 5.0 (Build 1400) -->
<software name="Microsoft SNMP" re="Microsoft Windows CE Version ([d.]+)">
<info key="operating-system.type" type="str" value="Windows"/>
<info key="operating-system.version" type="str" value="CE"/>
<info key="version" type="re" value="1"/>
</software>
<!-- <title>Apache Tomcat/7.0.22</title> -->
<!-- Apache Tomcat/5.0.30 -->
<software name="Tomcat">
<re>Tomcat/([d.]+)</re>
<info key="version" type="re" value="1"/>
<info key="cpe_version" type="re" value="1"/>
</software>
<!-- >Tomcat Server Administration< -->
<software name="Tomcat" eid="10711">
<re>076Tomcat Server Administration074</re>
<info key="None" type="str" value="Tomcat"/>
<info key="console" type="str" value="detected"/>
</software>
45
46. 4.3 Definizioni
Si elencano una serie di definizioni in riferimento all'insieme delle stringhe banner di esempio, che
costituiscono l'input del generatore di espressioni regolari.
In ogni stringa è individuabile uno scheletro di sottostringhe comuni a tutte le stringhe dell'insieme,
poste sempre nello stesso ordine. Si definiscono:
• token (t): sottostringa, significativa secondo un determinato criterio
• common token (ct): una sottostringa presente in tutte le stringhe
• inter-common token (ict o interCT): la sottostringa delimitata da due ct o dai limiti della
stringa
Vista la centralità delle parole nel contesto applicativo, si definiscono:
• word token (wt): la più grande sottostringa composta da caratteri alfanumerici consecutivi
• common word token (cwt): wt che siano anche ct
Si definiscono:
• pattern: porzione di espressione regolare di significato compiuto
• common pattern (cp): un pattern presente in tutte le stringhe
• inter-common pattern (icp): la sottosequenza di pattern delimitata da due cp o dai limiti della
sequenza di pattern
Per generalizzare le definizioni riguardanti i token e i pattern si definiscono:
• common element (ce): un elemento presente in tutte le stringhe (ct o cp)
• inter-common element (ice): la sottostringa o sottosequenza di pattern delimitata da due ce
(due ct o due cp) o dai limiti della stringa o della sequenza
Inoltre si utilizza il suffisso Seq per indicare una sequenza degli elementi precedentemente definiti
(ad es. cpSeq per una sequenza di common pattern).
Si definiscono poi quattro tipologie di pattern, schemi di base ispirati alle character class frequenti.
Ogni stringa è conforme a una sequenza di pattern di questi tipi fondamentali:
• S (spaziature): porzione di stringa conforme a 's+'
• D (cifre): porzione di stringa conforme a 'd+'
• W (parola): porzione di stringa conforme a 'w+'
• P (punteggiatura): porzione di stringa conforme a '[^sw]+'
46
47. Si definisce patternizzazione l'operazione in cui si individua la sequenza di pattern corrispondente a
una stringa.
Poiché ogni pattern di tipo D è anche di tipo W, questa operazione deve ricercare i pattern di tipo D
prima di quelli di tipo W.
Dal risultato è facilmente costruibile un'espressione regolare composta da pattern, conforme alla
stringa originaria.
Esempio
Stringa: 'Welcome to Pure-FTPd 1.0.14'
Sequenza di tipi di pattern: WSWSWPWSDPDPD
Espressione regolare: '^w+s+w+s+w+[^sw]+w+s+d+[^sw]+d+[^sw]+d+$'
Come convenzione di comodo si immaginerà che ogni stringa dell'insieme di banner sia disposta su
una propria riga; si definiscono allora i concetti di:
• elaborazione orizzontale: interessa una singola stringa e considera tutti i suoi element
• elaborazione verticale: interessa la corrispondenza di un singolo element in tutte le stringhe
4.4 Definizione dei tipi di soluzione
L'output della generazione deve essere un insieme di tipologie fisse di espressioni regolari, perlopiù
a livelli successivi di specificità.
Tutti i tipi di soluzioni devono considerare le stringhe banner fornite in input nella loro interezza, e
generare delle espressioni regolari con gli ancoraggi per individuare l'inizio e la fine delle stringhe.
Un'espressione regolare E priva di ancoraggi è logicamente equivalente a un'espressione regolare
Ea, ottenuta inserendo in E gli ancoraggi e separandoli con '.*'.
47