La storia.
Giugno 2010, ultimi miei giorni in Danimarca, comincio a guardarmi intorno per la tesi per la mia laurea specialistica in ingegneria informatica. Non trovando disponibilità a Copenhagen mi rivolgo al mio vecchio docente di reti e sicurezza di Modena, con cui cominciamo a parlare dell'idea di integrare all'interno di un IDS (Intrusion Detection System) come Snort algoritmi di AI (intelligenza artificiale), in modo da rendere il rilevamento degli attacchi più intelligente, diminuire il "rumore di fondo" dell'intrusion detection e il tasso di falsi positivi, rendere più agevole e "umana" la lettura di possibili minacce per la sicurezza dai log (ed eventualmente integrare meglio le regole di iptables nel caso in cui si voglia mettere a valle un sistema di sicurezza attivo e quindi trasformare Snort in un Intrusion Prevention Systems), clusterizzare gli alert in macrogruppi in modo da rendere più agevole la lettura (ad esempio invece di 1000 alert del tipo "pacchetto TCP frammentato da parte della macchina della rete X verso una macchina della rete interna" avere un unico alert), e prevedere con buona approssimazione le potenziali mosse future di un attaccante nello scenario di un attacco multi-step, considerato come un grafo molto ramificato dove ogni nodo è un pezzo dell'attacco.
È un progetto estremamente ambizioso, che si rifà un po' al defunto progetto SnortAI, ma anche molto complesso, sia da un punto di vista algoritmico, sia della dimensione e della gestione. È un progetto che sta crescendo giorno dopo giorno finora solo sotto le mie mani prendendo mille strade che all'inizio neanche prevedevamo. E come è facile intuire da solo mi comincia a risultare molto difficile gestire un progetto del genere (siamo già nell'ordine delle migliaia di righe di codice). Quindi rivolgo qui l'appello a potenziali sviluppatori, tester, o anche semplicemente a chi ha delle idee che possono risultare interessanti a questi fini (è un progetto che finora al mondo non esiste, è altamente sperimentale e mi sto muovendo anch'io per tentativi e idee da concretizzare pian piano), c'è bisogno di ogni tipo di figura in questo progetto e del lavoro di tutti. Per questo ho deciso di pubblicare i sorgenti su cui sto lavorando su GitHub, http://github.com/BlackLight/Snort_AIPreproc . Chiunque può esaminarli da lì (consiglio di fare spesso dei clone o dei pull, dato che aggiorno il repository praticamente ogni ora lavorandoci su attivamente), contribuire, fare dei fork, ecc.
Cosa c'è finora.
Snort_AIPreproc è un modulo per Snort che agisce a livello di preprocessore (quindi prima della trafila di matching di regole e flussi vera e propria). In realtà questo è vero solo in parte, perché pur essendo piazzato a livello di preprocessore in Snort parsa in parallelo anche gli alert log a valle, cercando di integrare quante più informazioni possibile.
Finora quello che ho fatto è
- corpo principale del modulo (spp_ai.c), che inizializza il tutto, parsa la configurazione del modulo stesso direttamente da snort.conf e avvia i thread richiesti in funzione delle preferenze dell'utente (ad esempio l'utente può scegliere di usare o meno il modulo per fare clustering intelligente degli alert, o se usare o meno meccanismi di timeout sui flussi di traffico)
- ricezione dei pacchetti in arrivo sull'interfaccia di rete e sistemazione degli stessi all'interno di una grande hash table (per cui uso la mini-libreria uthash, già integrata nei sorgenti) avente come chiave la coppia <src_ip, dst_port>, in cui ogni elemento è una lista doppiamente concatenata contenente i singoli pacchetti provenienti da un host e diretti verso una certa porta, ordinati in base al SEQ number
- parsing del file di alert di Snort per prendere conoscenza dei flussi che il sistema ha rilevato come sospetti, associare a ogni alert un flusso salvato nella hash table dei flussi in modo da poter fare un'analisi più approfondita di quel flusso sulla base delle informazioni fornite dall'IDS, marcare quel flusso come "sorvegliato speciale" (quindi non soggetto a cancellazioni dovute ai timeout), e gestire tutti gli alert all'interno del log come lista concatenata in ordine temporale
- clustering degli alert. L'utente, dalla configurazione di Snort, può fornire certe sottoclassi all'interno dei domini degli attributi degli alert e in funzione di come vuole filtrare il traffico e dell'architettura della propria rete (ad esempio può dire che la sottorete privata si trova a 192.168.1.0/24, che i server in DMZ si trovano a 10.8.0.0/24, che gli interessa raggruppare il traffico verso porte non privilegiate in cluster unici mentre vuole maggiore precisione sul traffico verso porte privilegiate, ecc.). In funzione di queste informazioni (parametri di clustering) il modulo crea diverse gerarchie (finora prevedo gerarchie su indirizzi e porte, ma accetto ogni tipo di suggerimenti, sarebbe interessante ad esempio fare gerarchie di clustering anche di tipo temporale, ad esempio per isolare meglio il traffico che arriva all'inizio o alla fine del mese, durante i giorni lavorativi o nel weekend, nelle ore lavorative o di notte, e così via), che vengono poi usate per creare, attraverso un algoritmo di data mining (proposto da Klaus Julisch nel 2002 nel paper "Clustering intrusion detection alarms to support root cause analysis, ma anche su quest'articolo sto considerando diverse prospettive di miglioramento, ad esempio una funzione che mi permetta di stabilire dinamicamente la dimensione minima dei cluster in funzione delle variabili ambientali della rete invece che prenderla staticamente), diversi "macro-raggruppamenti" per gli alert, più facili da gestire e da interpretare, ad esempio "nel weekend, di notte, durante i backup delle macchine, ricevo da diverse macchine nella sottorete X verso macchine della sottorete Y sulle porte Z diversi pacchetti frammentati, è consigliabile una configurazione migliore della rete". Questo tipo di ragionamento sembra fantascienza, ma se l'utente fornisce parametri di clustering che rispecchiano effettivamente l'ambiente dove l'IDS va a infilarsi ho testato che effettivamente è facile poter estrarre informazioni del genere
Questo è quello che c'è finora. Attualmente sto lavorando proprio sull'algoritmo di clustering. Quello di cui ho bisogno sono idee (cosa fareste con questo sistema, con quello che ci consente di fare finora, ipotesi di crescita, caratteristiche da implementare a monte o a valle), scenari d'uso e tuning della configurazione (ogni quanto eseguire il thread che fa il cleanup della hash table? ogni quanto eseguire il thread che parsa l'alert log? ogni quanto eseguire il thread che clusterizza gli alert? che parametri e che sottoclassi sarebbe consigliabile usare per un clustering più intelligente?), qualche anima pia che controlli eventuali bug nel codice (gestire un codice con tante migliaia di righe di codice e tanto uso di memoria dinamica per creare strutture complesse non è semplice, il memory leak è sempre dietro l'angolo e ancora di più lo è l'errore logico, il mancato controllo su un puntatore non inizializzato, la chiamata ricorsiva che può piantarsi), qualcuno che mi testi il codice e riporti pareri e opinioni, e se possibile qualcuno che mi aiuti anche con la stesura.
Il codice come avete modo di vedere è ancora abbastanza disordinato in alcuni punti. Più che altro troverete in giro diverse fprintf su file di log temporanei, che uso ovviamente per fare debug del codice stesso mentre lo scrivo, ovviamente quelle si possono rimuovere senza alcun problema. Inoltre per usarlo, almeno per ora, vanno scaricati i sorgenti di Snort, piazzati da qualche parte, e creata una nuova directory in SNORTSRC/dynamic-examples (chiamata ad esempio ai-preprocessor) con dentro i file del progetto. Va anche modificato il Makefile in modo che contenga come PREPROC_PATH il percorso effettivo al path contenente i moduli per il preprocessore di Snort (ad esempio /usr/lib/snort/snort_dynamicpreprocessor), in modo che il make copi direttamente lì i file compilati del preprocessore. Una volta fatte queste accortenze, per compilare il tutto e copiare i file compilati al giusto posto, per ora, basta eseguire lo script ./build.sh (con privilegi di root se il PREPROC_PATH è in una directory privilegiata).
Inoltre il modulo va anche configurato, la configurazione va piazzata direttamente in snort.conf. Un esempio di configurazione può essere il seguente:
Grazie a tutte le anime pie che mi daranno una mano.
Giugno 2010, ultimi miei giorni in Danimarca, comincio a guardarmi intorno per la tesi per la mia laurea specialistica in ingegneria informatica. Non trovando disponibilità a Copenhagen mi rivolgo al mio vecchio docente di reti e sicurezza di Modena, con cui cominciamo a parlare dell'idea di integrare all'interno di un IDS (Intrusion Detection System) come Snort algoritmi di AI (intelligenza artificiale), in modo da rendere il rilevamento degli attacchi più intelligente, diminuire il "rumore di fondo" dell'intrusion detection e il tasso di falsi positivi, rendere più agevole e "umana" la lettura di possibili minacce per la sicurezza dai log (ed eventualmente integrare meglio le regole di iptables nel caso in cui si voglia mettere a valle un sistema di sicurezza attivo e quindi trasformare Snort in un Intrusion Prevention Systems), clusterizzare gli alert in macrogruppi in modo da rendere più agevole la lettura (ad esempio invece di 1000 alert del tipo "pacchetto TCP frammentato da parte della macchina della rete X verso una macchina della rete interna" avere un unico alert), e prevedere con buona approssimazione le potenziali mosse future di un attaccante nello scenario di un attacco multi-step, considerato come un grafo molto ramificato dove ogni nodo è un pezzo dell'attacco.
È un progetto estremamente ambizioso, che si rifà un po' al defunto progetto SnortAI, ma anche molto complesso, sia da un punto di vista algoritmico, sia della dimensione e della gestione. È un progetto che sta crescendo giorno dopo giorno finora solo sotto le mie mani prendendo mille strade che all'inizio neanche prevedevamo. E come è facile intuire da solo mi comincia a risultare molto difficile gestire un progetto del genere (siamo già nell'ordine delle migliaia di righe di codice). Quindi rivolgo qui l'appello a potenziali sviluppatori, tester, o anche semplicemente a chi ha delle idee che possono risultare interessanti a questi fini (è un progetto che finora al mondo non esiste, è altamente sperimentale e mi sto muovendo anch'io per tentativi e idee da concretizzare pian piano), c'è bisogno di ogni tipo di figura in questo progetto e del lavoro di tutti. Per questo ho deciso di pubblicare i sorgenti su cui sto lavorando su GitHub, http://github.com/BlackLight/Snort_AIPreproc . Chiunque può esaminarli da lì (consiglio di fare spesso dei clone o dei pull, dato che aggiorno il repository praticamente ogni ora lavorandoci su attivamente), contribuire, fare dei fork, ecc.
Cosa c'è finora.
Snort_AIPreproc è un modulo per Snort che agisce a livello di preprocessore (quindi prima della trafila di matching di regole e flussi vera e propria). In realtà questo è vero solo in parte, perché pur essendo piazzato a livello di preprocessore in Snort parsa in parallelo anche gli alert log a valle, cercando di integrare quante più informazioni possibile.
Finora quello che ho fatto è
- corpo principale del modulo (spp_ai.c), che inizializza il tutto, parsa la configurazione del modulo stesso direttamente da snort.conf e avvia i thread richiesti in funzione delle preferenze dell'utente (ad esempio l'utente può scegliere di usare o meno il modulo per fare clustering intelligente degli alert, o se usare o meno meccanismi di timeout sui flussi di traffico)
- ricezione dei pacchetti in arrivo sull'interfaccia di rete e sistemazione degli stessi all'interno di una grande hash table (per cui uso la mini-libreria uthash, già integrata nei sorgenti) avente come chiave la coppia <src_ip, dst_port>, in cui ogni elemento è una lista doppiamente concatenata contenente i singoli pacchetti provenienti da un host e diretti verso una certa porta, ordinati in base al SEQ number
- parsing del file di alert di Snort per prendere conoscenza dei flussi che il sistema ha rilevato come sospetti, associare a ogni alert un flusso salvato nella hash table dei flussi in modo da poter fare un'analisi più approfondita di quel flusso sulla base delle informazioni fornite dall'IDS, marcare quel flusso come "sorvegliato speciale" (quindi non soggetto a cancellazioni dovute ai timeout), e gestire tutti gli alert all'interno del log come lista concatenata in ordine temporale
- clustering degli alert. L'utente, dalla configurazione di Snort, può fornire certe sottoclassi all'interno dei domini degli attributi degli alert e in funzione di come vuole filtrare il traffico e dell'architettura della propria rete (ad esempio può dire che la sottorete privata si trova a 192.168.1.0/24, che i server in DMZ si trovano a 10.8.0.0/24, che gli interessa raggruppare il traffico verso porte non privilegiate in cluster unici mentre vuole maggiore precisione sul traffico verso porte privilegiate, ecc.). In funzione di queste informazioni (parametri di clustering) il modulo crea diverse gerarchie (finora prevedo gerarchie su indirizzi e porte, ma accetto ogni tipo di suggerimenti, sarebbe interessante ad esempio fare gerarchie di clustering anche di tipo temporale, ad esempio per isolare meglio il traffico che arriva all'inizio o alla fine del mese, durante i giorni lavorativi o nel weekend, nelle ore lavorative o di notte, e così via), che vengono poi usate per creare, attraverso un algoritmo di data mining (proposto da Klaus Julisch nel 2002 nel paper "Clustering intrusion detection alarms to support root cause analysis, ma anche su quest'articolo sto considerando diverse prospettive di miglioramento, ad esempio una funzione che mi permetta di stabilire dinamicamente la dimensione minima dei cluster in funzione delle variabili ambientali della rete invece che prenderla staticamente), diversi "macro-raggruppamenti" per gli alert, più facili da gestire e da interpretare, ad esempio "nel weekend, di notte, durante i backup delle macchine, ricevo da diverse macchine nella sottorete X verso macchine della sottorete Y sulle porte Z diversi pacchetti frammentati, è consigliabile una configurazione migliore della rete". Questo tipo di ragionamento sembra fantascienza, ma se l'utente fornisce parametri di clustering che rispecchiano effettivamente l'ambiente dove l'IDS va a infilarsi ho testato che effettivamente è facile poter estrarre informazioni del genere
Questo è quello che c'è finora. Attualmente sto lavorando proprio sull'algoritmo di clustering. Quello di cui ho bisogno sono idee (cosa fareste con questo sistema, con quello che ci consente di fare finora, ipotesi di crescita, caratteristiche da implementare a monte o a valle), scenari d'uso e tuning della configurazione (ogni quanto eseguire il thread che fa il cleanup della hash table? ogni quanto eseguire il thread che parsa l'alert log? ogni quanto eseguire il thread che clusterizza gli alert? che parametri e che sottoclassi sarebbe consigliabile usare per un clustering più intelligente?), qualche anima pia che controlli eventuali bug nel codice (gestire un codice con tante migliaia di righe di codice e tanto uso di memoria dinamica per creare strutture complesse non è semplice, il memory leak è sempre dietro l'angolo e ancora di più lo è l'errore logico, il mancato controllo su un puntatore non inizializzato, la chiamata ricorsiva che può piantarsi), qualcuno che mi testi il codice e riporti pareri e opinioni, e se possibile qualcuno che mi aiuti anche con la stesura.
Il codice come avete modo di vedere è ancora abbastanza disordinato in alcuni punti. Più che altro troverete in giro diverse fprintf su file di log temporanei, che uso ovviamente per fare debug del codice stesso mentre lo scrivo, ovviamente quelle si possono rimuovere senza alcun problema. Inoltre per usarlo, almeno per ora, vanno scaricati i sorgenti di Snort, piazzati da qualche parte, e creata una nuova directory in SNORTSRC/dynamic-examples (chiamata ad esempio ai-preprocessor) con dentro i file del progetto. Va anche modificato il Makefile in modo che contenga come PREPROC_PATH il percorso effettivo al path contenente i moduli per il preprocessore di Snort (ad esempio /usr/lib/snort/snort_dynamicpreprocessor), in modo che il make copi direttamente lì i file compilati del preprocessore. Una volta fatte queste accortenze, per compilare il tutto e copiare i file compilati al giusto posto, per ora, basta eseguire lo script ./build.sh (con privilegi di root se il PREPROC_PATH è in una directory privilegiata).
Inoltre il modulo va anche configurato, la configurazione va piazzata direttamente in snort.conf. Un esempio di configurazione può essere il seguente:
Codice:
# ---> MY AI PREPROCESSOR
preprocessor ai: hashtable_cleanup_interval 30 \
tcp_stream_expire_interval 50 \
alertfile "/home/blacklight/local/snort/lib/snort_dynamicpreprocessor/log/alert" \
alert_clustering_interval 600 \
clusterfile "/home/blacklight/local/snort/lib/snort_dynamicpreprocessor/log/clustered_alerts" \
cluster ( class="dst_port", name="1-1023", range="1-1023" ) \
cluster ( class="dst_port", name="1-100", range="1-100" ) \
cluster ( class="dst_port", name="1-80", range="1-80" ) \
cluster ( class="dst_port", name="81-100", range="81-100" ) \
cluster ( class="dst_port", name="1024-65535", range="1024-65535" ) \
cluster ( class="dst_port", name="150-170", range="150-170" ) \
cluster ( class="dst_port", name="700-710", range="700-710" ) \
cluster ( class="dst_port", name="1024-65535", range="1024-65535" ) \
cluster ( class="src_addr", name="127.0.0.1/24", range="127.0.0.1/24" ) \
cluster ( class="src_addr", name="127.0.0.1/16", range="127.0.0.1/16" ) \
cluster ( class="src_addr", name="127.0.0.1/8", range="127.0.0.1/8" ) \
cluster ( class="src_addr", name="192.168.1.0/24", range="192.168.1.0/24" ) \
cluster ( class="src_addr", name="192.168.1.0/16", range="192.168.1.0/16" ) \
cluster ( class="src_addr", name="192.168.1.0/8", range="192.168.1.0/8" ) \
cluster ( class="src_addr", name="192.168.1.1", range="192.168.1.1" ) \
cluster ( class="src_addr", name="10.8.0.0/24", range="10.8.0.0/24" ) \
cluster ( class="src_addr", name="10.8.0.0/16", range="10.8.0.0/16" ) \
cluster ( class="src_addr", name="10.8.0.0/8", range="10.8.0.0/8" )
Grazie a tutte le anime pie che mi daranno una mano.