[Collaborazione] AIPreproc, modulo di AI per Snort

Stato
Discussione chiusa ad ulteriori risposte.

imported_BlackLight

Utente Silver
16 Agosto 2007
211
8
1
98
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:

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.
 
Il tutto comincia a prendere forma, ed è pronta una prima versione iper-beta che pare funzionare correttamente. Il tutto è scaricabile dalla pagina GitHub del progetto:
http://github.com/BlackLight/Snort_AIPreproc
via git clone/pull o scaricando direttamente il tar.gz. Per l'installazione

Codice:
./configure
make
make install

Al ./configure può essere necessario specificare la directory dove è installato Snort se non è in /usr via --prefix, e se si vuole il supporto per i log salvati su database MySQL (--with-mysql) o si vuole rimuovere il supporto per libgraphviz (--without-graphviz), abilitato di default, per la generazione automatica dei grafici di correlazione fra gli alert come immagini .png.

All'interno del pacchetto è contenuto anche un README che spiega nel dettaglio come configurare e usare al meglio il modulo di Snort. Quello che fa, per ora, è clusterizzare gli alert in base a gerarchie fornite dall'utente in base alla topologia/uso della rete (subnet IP, range di porte particolari, alert simili...), con lo scopo di raggruppare in questo modo anche i falsi positivi e rendere più agevole la lettura dei log, mandando l'output su un file di log chiamato di default clustered_alerts. Gli alert clusterizzati vengono quindi esaminati a posteriori, e sulla base di regole di correlazione (per ora fornite staticamente dall'utente in formato XML, o fornite direttamente dallo sviluppatore nel mio caso, o da terze parti), contenenti per ogni alert le sue pre-condizioni e post-condizioni, genera un grafo di correlazione fra gli alert che mostra il "percorso" di un attaccante in un attacco multi-step. Questo modello consta già di un piccolo motore di reasoning che fa inferenza sulle condizioni fornite nella knowledge base (per ora sostituendo a runtime macro predefinite, come +ANY_ADDR+ e +ANY_PORT+, per ottimizzare il matching fra gli alert, o espandendo a runtime subnet IP all'interno dei modelli delle condizioni degli alert). Il passo successivo sarà implementare un modello di reasoning e auto-apprendimento che attraverso reti bayesiane e/o modelli di Markov impari a correlare due alert in funzione di "quanto frequentemente" questi compaiono insieme. Fatta la correlazione fra gli alert (calcolata come rapporto fra la cardinalità dell'intersezione (post-cond(A), pre-cond(B)) e la cardinalità dell'unione (post-cond(A), pre-cond(B))), gli alert A e B si dicono "correlati", o meglio si dice che "l'alert A determina l'alert B", se e solo se il loro coefficiente di correlazione è maggiore o uguale di μ + kσ, dove μ è il coefficiente di correlazione medio, σ è la deviazione standard del coefficiente di correlazione, e k è un parametro fornito nella configurazione del modulo che esprime "quanto sensibile" deve essere il modello statistico (valori di k molto vicini allo 0, o minori di 0, rendono le correlazioni fra gli alert molto più comuni, valori via via più alti rendono le correlazioni via via più rare). A proposito, lancio l'appello: questo modello statistico parte dal presupposto che la deviazione standard del coefficiente di correlazione sia grosso modo costante. Purtroppo non è così nella maggior parte dei casi reali, quindi sto pensando a un algoritmo che calcoli k in modo dinamico in funzione degli alert ricevuti, se qualcuno ha idee si faccia avanti.

Nella directory corr_rules sono forniti alcuni file XML "giocattolo" che descrivono le pre-condizioni e le post-condizioni per alcuni alert comuni (per ora, ad esempio, nmap ping, TCP portscan, shellcode, e richiesta di mount remoto via RPC). Sulla base di questi si possono scrivere senza grossi problemi nuovi file XML che descrivono altri alert, la sintassi è molto semplice. Questo è ad esempio il file XML che descrive l'hyperalert di un TCP portscan (Snort ID 122.1.0):

Codice:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hyperalert PUBLIC "-//blacklighth//DTD HYPERALERT SNORT MODEL//EN" "http://0x00.ath.cx/hyperalert.dtd">

<hyperalert>
        <snort-id>122.1.0</snort-id>
        <desc>(portscan) TCP Portscan</desc>

        <pre>HostExists(+DST_ADDR+)</pre>
        <post>HasService(+DST_ADDR+, +ANY_PORT+)</post>
</hyperalert>

Sulla base di questi hyperalert è possibile già riconoscere, ad esempio, un semplice scenario d'attacco del tipo ping + portscan + iniezione di uno shellcode da remoto + mount remoto di una partizione NFS (immagine automaticamente generata dal modulo):

correlatedalertsresized.png


Ogni collaborazione al progetto è, ovviamente, più che benvenuta.
 
I lavori vanno avanti. Il modulo ora è stato aggiornato con un bel po' di roba foga fra cui

  • Supporto per correlazione bayesiana "intelligente". Ovvero, tutti gli alert ricevuti vengono serializzati su un file binario di history, parsato a intervalli regolari in una hash table, dal quale si possono ricavare informazioni su quanto due alert sono correlati in funzione del numero di volte in cui capitano insieme. Fondamentalmente gli alert vengono esaminati a coppie e la loro correlazione viene calcolata come

    KynC


    dove

    1aW1p.png


    con l'insieme di alert correlati definito come sottoinsieme del prodotto cartesiano delle coppie:

    CinGj.png


    con d definita come distanza temporale massima per considerare 2 alert correlati (1 secondo, 1 minuto, 1 settimana...) ed f è una funzione definita come

    s7YQ1.png


    con k che mi definisce quanto deve essere "larga" la gaussiana e configurabile dall'utente, calcolato in particolare come

    Njrwe.png


    e ovviamente la probabilità di B è calcolata come

    4sHj8.png


    Con questo metodo è possibile calcolare quanto due alert sono correlati fra loro in un attacco multi-step in funzione di come e quando vengono "triggerati" dal sistema (ovviamente ci saranno ampie oscillazioni inizialmente, e altrettante correlazioni sballate, ma man mano che il traffico in arrivo al sistema comincia ad assumere una "forma" precisa anche i coefficienti così calcolati si stabilizzano).
  • Supporto per l'output di flussi di traffico associati ad alert, cluster di alert e correlazioni su database (supporto per ora sia per MySQL che PostgreSQL). In questo modo, ad esempio, per vedere le informazioni sui singoli cluster di alert e gli alert in essi contenuti basterà un

    Codice:
    select a.alert_id, c.*
    from ca_clustered_alerts c join ca_alerts a
    on c.cluster_id=a.cluster_id
    group by c.cluster_id
  • Interfaccia web, basata su un web server di base scritto from scratch per l'occasione e runnato come thread parallelo (codice in webserv.c) con supporto CGI. Attraverso l'interfaccia web è possibile esplorare, attraverso un insieme di script AJAX e script esterni CGI in Perl, i cluster di alert ricevuti dal sistema, analizzare gli alert contenuti all'interno di ogni cluster (e salvare i singoli flussi di pacchetti matchati, se disponibili, in formato .pcap attraverso una CGI Perl esterna, pronti per essere analizzati da tcpdump, tcpsmash, Wireshark o snort stesso), visualizzare gli alert correlati (che appariranno come qualcosa del genere)

    WHxqe.png


    personalizzare l'interfaccia semplicemente trascinando a piacere i nodi del grafico come si vuole (grazie a JavaScript + SVG e le librerie Raphael JS e Dracula), e soprattutto modificare eventuali "scazzature" del modulo, avendo la possibilità di correlare a mano degli alert che il modulo non ha correlato o specificare che due alert correlati in realtà non lo sono. Il tutto è fatto a partire da un JSON contenente le inforrmazioni esportato dal modulo in C, parsato da AJAX, visualizzato graficamente attraverso Raphael+Dracula, e le cui informazioni rimbalzano alle CGI Perl responsabili rispettivamente dell'esportazione dei flussi in .pcap e della personalizzazione delle correlazioni (gestite attraverso file XML redirezionati al modulo una volta riempiti).

Come al solito, stesso link su GitHub per avere i sorgenti, e ogni collaborazione è più che accetta.
 
E' un po' che non aggiorno sull'attuale stato delle cose da queste parti...

Il core del modulo è praticamente terminato e funzionante, e usa diversi indici per correlare automaticamente gli alert, fra cui

[*] knowledge base con modelli di hyperalert
[*] rete pseudo-bayesiana
[*] rete neurale (Self-Organizing Map)
[*] correlazioni automatiche proposte dall'utente attraverso l'interfaccia web

Questi diversi indici danno un indice di correlazione complessivo calcolato come media pesata, dove i pesi sono prefissati (ad esempio la correlazione basata su hyperalert, essendo basata su un modello considerato sempre valido, ha peso unitario) o dinamici, in funzione della "conoscenza" acquisita dal software e dal livello di addestramento.

E' ora possibile anche aggiungere nuovi indici di correlazione, come moduli software al di sopra del modulo. I moduli possono essere

[*] librerie dinamiche scritte in C (file .so), si veda l'esempio in corr_modules/
[*] se il modulo è compilato passando a ./configure l'opzione --with-python, script Python piazzati sempre nella directory corr_modules/ e automaticamente caricati a runtime dal software.

Tutto ciò che un modulo deve avere, che sia scritto in C o in Python, sono due funzioni:

[*] AI_corr_index(), che dati due alert ritorna il loro indice di correlazione come valore numerico reale;
[*] AI_corr_index_weight(), che ritorna il peso di quell'indice, in funzione eventualmente di altri parametri, nel calcolo della media pesata.

L'interfaccia Python prevede l'uso di un modulo, 'snortai', che può essere compilato e installato spostandosi nella directory pymodule/ e andando di

Codice:
$ python setup.py build
$ [sudo] python setup.py install

A questo punto è possibile a un modulo o a un qualsiasi script esterno (purché Snort in quel momento sia in esecuzione, e anche il modulo SnortAI con il suo web server sia in esecuzione) accedere alla lista degli alert acquisiti fino a quel momento dall'IDS attraverso un codice del tipo

Codice:
#!/usr/bin/python

import snortai

alerts = snortai.alerts()

for alert in alerts:
	print alert.gid, alert.sid, alert.rev, alert.description, alert.classification

A questo punto si cercano

[*] Tester volenterosi che possano testare il mio codice in diversi contesti;
[*] Developers altrettanto volenterosi che possano darmi una mano a sviluppare dei porting anche per Perl e Ruby, in modo simile a quello che ho fatto per Python
 
Ammetto di non aver perso molto tempo a leggere il codice, ma non ho capito in cosa consiste l'interfaccia python, quindi dimmi dove e se sbaglio.
In pratica hai fatto in modo che i moduli possano essere scritti in python oltre che in c.
Il porting ad esempio in perl consiste nello scrivere un modulo (di perl) che interagisca col core del modulo (di snort), in modo che uno script esterno possa controllare lo stato degli alert.
 
Fondamentalmente mi servono due cose (che sono le due cose che per ora faccio in Python):

[*] Un modulo che faccia una richiesta HTTP, di default a http://localhost:7654/alerts.cgi?method=(xml|json) e, dato l'XML o il JSON in uscita che rappresenta lo stato attuale degli alert, lo parsi e lo converta in un vettore di oggetti visibile dal codice Perl o Ruby. In pratica, un modulo per Perl o Ruby, importabile in uno script, che peschi lo stato attuale degli alert del modulo, come faccio per ora in Python con un

Codice:
#!/usr/bin/python

import snortai

alerts = snortai.alerts()

for alert in snortai:
  # Gestisci gli alert

Vedi il file pymodule/snortai_module.c per vedere come ho implementato il tutto per Python. Mi serve qualcosa di simile anche in Perl e Ruby.

[*] Una funzione, o eventualmente più funzioni, C, da collocare nel file modules.c, che dato uno script Perl o Ruby contenente due funzioni specifiche (AI_corr_index e AI_corr_index_weight), richiami dal codice C queste funzioni passandogli come parametro due alert e ritornando rispettivamente il valore di correlazione fra questi due alert e il peso di quell'indice (non è richiesto scrivere queste due funzioni, che saranno specifiche per un modulo per SnortAI, basta fare in modo che il codice C le richiami passandogli i parametri opportuni e sia in grado di pescare i loro valori di ritorno).
 
Stato
Discussione chiusa ad ulteriori risposte.