Tracciamento di eventi a livello di sistema operativo e

Transcript

Tracciamento di eventi a livello di sistema operativo e
Università degli studi di Genova
Facoltà di Scienze Matematiche, Fisiche e Naturali
Corso di Laurea in Informatica
Anno accademico 2008/2009
Prova finale
Tracciamento di eventi a livello di
sistema operativo e applicazioni
alla sicurezza
Candidato:
Emanuele Rocca
Relatore:
Prof. Giuseppe Ciaccio
9 ottobre 2009
Indice
1 Introduzione
3
1.1
Tracing dinamico a livello di sistema operativo . . . . . . . .
3
1.2
Applicazioni alla sicurezza . . . . . . . . . . . . . . . . . . . .
4
2 SystemTap
5
2.1
Architettura . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
2.2
Il linguaggio . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
2.2.1
Tipi di script . . . . . . . . . . . . . . . . . . . . . . .
7
2.2.2
Definizione di nuove sonde . . . . . . . . . . . . . . . .
7
2.2.3
Definizione di funzioni . . . . . . . . . . . . . . . . . .
8
2.2.4
Statistiche . . . . . . . . . . . . . . . . . . . . . . . . .
8
Affidabilità e sicurezza . . . . . . . . . . . . . . . . . . . . . .
9
2.3.1
Memoria, puntatori NULL e divisione per zero . . . .
9
2.3.2
Cicli infiniti e ricorsione . . . . . . . . . . . . . . . . .
9
2.3.3
Carico eccessivo
9
2.3
. . . . . . . . . . . . . . . . . . . . .
3 Tapset per i portscan
10
3.1
Scansioni di tipo NULL, FIN e XMAS . . . . . . . . . . . . .
10
3.2
Scansioni TCP Connect . . . . . . . . . . . . . . . . . . . . .
11
3.2.1
Funzioni ausiliarie . . . . . . . . . . . . . . . . . . . .
11
3.2.2
Tenere traccia dei socket . . . . . . . . . . . . . . . . .
12
3.2.3
Individuazione dei socket senza traffico . . . . . . . . .
12
4 Anomaly Based Intrusion Detection Systems
14
4.1
Analisi dei payload . . . . . . . . . . . . . . . . . . . . . . . .
14
4.2
Analisi delle sequenze di chiamate di sistema . . . . . . . . .
14
1
5 Implementazione di un Anomaly Based IDS
5.1
5.2
5.3
15
Generazione sequenze di chiamate di sistema . . . . . . . . .
15
5.1.1
Scelta della lunghezza . . . . . . . . . . . . . . . . . .
15
5.1.2
Generare le sequenze con SystemTap . . . . . . . . . .
16
Il database dei comportamenti normali . . . . . . . . . . . . .
18
5.2.1
Generazione sequenze uniche . . . . . . . . . . . . . .
18
5.2.2
Serializzazione . . . . . . . . . . . . . . . . . . . . . .
20
Individuazione di comportamenti anomali . . . . . . . . . . .
21
5.3.1
22
Confronto algoritmi per il calcolo della distanza . . . .
6 Valutazione del sistema proposto
23
6.1
Impatto sulle performance del sistema operativo . . . . . . .
23
6.2
Riconoscimento di comportamenti anomali . . . . . . . . . . .
24
6.2.1
Individuazione di rootkit
. . . . . . . . . . . . . . . .
25
6.2.2
Scansioni con Nessus . . . . . . . . . . . . . . . . . . .
25
6.2.3
Falsi positivi . . . . . . . . . . . . . . . . . . . . . . .
26
7 Conclusioni
27
2
1
Introduzione
I sistemi di tracciamento dinamico del funzionamento dei sistemi operativi
consentono di analizzare in maniera approfondita il comportamento di un
sistema in esecuzione. Tali sistemi di analisi vengono usati principalmente
da sviluppatori software e sistemisti, ma si rivelano utili anche per facilitare
il debugging agli sviluppatori dei sistemi operativi stessi. Sono inoltre molto
interessanti dal punto di vista didattico, permettendo di studiare e osservare
“dal vivo” il funzionamento e l’interazione dei vari sottosistemi di un kernel.
In questa prova finale si cercherà di mostrare come la flessibilità di strumenti di questo tipo consenta in maniera relativamente semplice di identificare possibili tentativi di intrusione, sia tramite sonde sviluppate al fine di
individurare particolari tipi di portscan, sia mediante analisi delle anomalie.
La storia dei sistemi di tracing dinamico è assolutamente recente e di conseguenza molto breve: tra i primi software di rilievo, infatti, ricordiamo
DTrace di Sun Microsystems, presentato per la prima volta alla USENIX
2004 Annual Technical Conference [1] e usato largamente da sistemisti e sviluppatori Unix su Sun Solaris e Mac OS X. Da subito DTrace ha dimostrato
di essere un software molto interessante, al punto di venire spesso citato tra
le caratteristiche principali di Sun Solaris.
SystemTap, un software analogo per il kernel Linux, è stato presentato nel
2005 all’Ottawa Linux Symposium [2] da Red Hat, IBM, e Intel.
La principale ragione per cui non è stato possibile effettuare il porting di
DTrace a Linux, ma si è scelto piuttosto di sviluppare un nuovo sistema, è
di carattere legale. Nonostante DTrace sia a tutti gli effetti software libero,
è stato rilasciato secondo i termini della licenza CDDL1 , incompatibile con
la licenza GNU GPL2 del kernel Linux.
1.1
Tracing dinamico a livello di sistema operativo
I tradizionali strumenti di analisi delle performance sono orientati allo sviluppatore, e dunque agli ambienti di sviluppo e testing, piuttosto che ai
sistemi in produzione. Spesso però le differenze di comportamento tra un
sistema in fase di test ed uno in produzione, vuoi per via di configurazioni
1
2
Common Development and Distribution License
General Public License
3
non perfettamente replicabili o per tipologie di utilizzo impreviste rendono
desiderabile l’uso di un sistema di analisi preciso e senza “effetti collaterali”,
che siano di tipo prestazionale o di affidabilità.
Per consentire l’analisi del comportamento di un sistema in produzione sono
necessari strumenti con le seguenti prerogative:
• Nessuna influenza sul sistema quando disabilitati
• Semplicità nell’estrazione ed elaborazione dei dati raccolti
• Massima sicurezza durante l’esecuzione
L’utilizzo di sonde inserite staticamente nel kernel, approccio usato per
esempio dal Linux Trace Toolkit, influenza il comportamento del sistema operativo anche quando il sistema di analisi non è in uso; di contro, la
possibilità di avvalersi di sonde caricabili dinamicamente consente di non
influire sul lavoro del sistema nel momento in cui non fosse necessario usare
lo strumento di analisi.
Per permettere di estrarre ed elaborare con semplicità i dati di interesse,
il sistema di tracing dinamico deve fornire un apposito linguaggio di scripting ad alto livello e mascherare in qualche modo i dettagli implementativi
del sistema operativo agli utenti, che non necessariamente sono esperti del
kernel.
Il sistema di tracing deve inoltre garantire un elevato grado di sicurezza:
l’utilizzo del sistema da parte di un utente non deve portare a malfunzionamenti, per esempio per via di cicli infiniti o divisioni per zero.
1.2
Applicazioni alla sicurezza
I sistemi atti a rilevare tentativi di intrusione, noti come Intrusion Detection
System, o IDS, possono essere di rete oppure host-based.
La possibilità di analizzare nel dettaglio il comportamento di un kernel ci
consente di sviluppare un prototipo di IDS host-based “ibrido”. Useremo
infatti un approccio basato sulla conoscenza del comportamento di alcuni
eventi malevoli, nella fattispecie dei portscan, ma proveremo anche a seguire
l’approccio basato sull’analisi delle anomalie, osservando le sequenze di chiamate di sistema eseguite dai vari programmi, come dettagliato nelle sezioni
3 e 4.2.
4
Figura 1: Architettura ad alto livello di SystemTap
2
SystemTap
L’uso di SystemTap è relativamente semplice, richiedendo all’utente solo di
imparare un linguaggio di scripting ad alto livello, trattato nel dettaglio
nella sezione 2.2.
Per poter lavorare con il sistema operativo, però, gli script debbono essere in qualche modo trasformati in moduli del kernel, che andranno caricati
e che dovranno interagire con lo spazio utente. L’architettura di SystemTap, spiegata nella sezione che segue, è fortemente influenzata da queste
considerazioni.
2.1
Architettura
SystemTap è formato da due componenti principali: un front-end chiamato
stap che si occupa di produrre un modulo del kernel a partire da uno script
SystemTap, ed un back-end chiamato staprun che carica il modulo, estrae
l’output dallo spazio kernel e infine rimuove il modulo dalla memoria.
5
Questo tipo di separazione tra la fase di compilazione e quella di esecuzione
consente di compilare gli script su una macchina di sviluppo con installate
le informazioni di debug e le librerie necessarie. Il modulo cosı̀ prodotto
può essere eseguito su una sistema di produzione, senza doverne cambiare
la configurazione o installare software addizionale.
L’organizzazione architetturale di SystemTap può essere vista come una
pipeline [5] organizzata nelle seguenti fasi:
• parse
• elaborate
• translate
• build
• load/run
• extract output/unload
Tra le componenti principali del front-end vi è il cosiddetto translator che
si occupa dei primi tre stadi: durante la fase di parse viene analizzato lo
script in input, elaborato durante la fase elaborate, e infine tradotto in un
programma C durante la terza fase, detta translate [5].
Il programma C cosı̀ prodotto viene compilato durante lo stadio di build, producendo un modulo del kernel e concludendo l’attività per quanto riguarda
il front-end.
Il back-end si occupa a questo punto di caricare il modulo del kernel nella
fase di load/run; nella fase detta extract output il back-end copia dallo spazio
kernel i dati da mandare in output su file temporanei, sino a quando l’utente
non decide di terminare l’esecuzione o non si verificano errori a run-time
quali per esempio il superamento del numero massimo di iterazioni consentite
o una divisione per zero3 . Sia in caso di errore che in caso di conclusione con
successo, l’ultima operazione compiuta dal back-end è quella di rimozione
(fase di unload ) del modulo dalla memoria.
3
Per maggiori dettagli sulle limitazioni imposte da SystemTap per evitare di incorrere
in problemi di affidabilità o di sicurezza si veda la sezione 2.3
6
2.2
Il linguaggio
Il linguaggio di scripting di SystemTap è fortemente tipato, procedurale,
non richiede di dichiarare le variabili ed è ispirato ad awk e dtrace [8].
I due costrutti principali sono sonde e funzioni. Le sonde consentono di
identificare il verificarsi di particolari eventi del kernel, quali per esempio la
chiamata di una determinata syscall o l’ingresso in una particolare funzione
del kernel. Mediante le funzioni è invece possibile scrivere codice modulare
e riusabile.
2.2.1
Tipi di script
Gli script SystemTap possono essere di due tipi: tapset e probe. I tapset
sono librerie di funzioni e alias di sonde che consentono di scrivere con più
facilità gli script probe, che sono script SystemTap prodotti dall’utente per
raggiungere un determinato scopo.
Per esempio, come si vedrà nei capitoli successivi, nello svolgere questa prova
finale è stato prodotto un tapset chiamato portscan.stp contenente varie funzioni e alias di sonde per individuare alcune categorie di portscan e un probe
script che si avvale di questo e molti altri tapset forniti con SystemTap4 per
raccogliere dati circa possibili intrusioni e comportamenti anomali.
2.2.2
Definizione di nuove sonde
Per definire una nuova sonda è necessario dichiararla come alias di una o
più sonde esistenti. In questo modo, al trigger della (o delle) sonde cui si
fa riferimento verrà invocata anche quella in via di definizione, che potrà
leggere dati, modificarli e renderli disponibili all’utente come meglio crede.
A titolo d’esempio, il seguente probe alias definisce la sonda udp.sendmsg,
che viene invocata al momento della chiamata di udp_sendmsg, funzione del
kernel responsabile dell’invio di un datagramma UDP.
/∗∗
∗ probe udp . sendmsg − Sending a UDP message
∗ @name : Name o f t h i s probe
∗ @sock : Network s o c k e t
4
Tra gli altri: io.stp per l’input/output, networking.stp per gli eventi relativi alla rete,
tcp.stp per l’analisi dei pacchetti TCP
7
∗ @ s i z e : Number o f b y t e s t o send
∗
∗ Context :
∗
The p r o c e s s which s e n d s a udp message
∗/
probe udp . sendmsg = k e r n e l . f u n c t i o n ( ” udp sendmsg ” ) {
name = ”udp . sendmsg ”
sock
= $sk
size
= $len
}
Tra i parametri con cui viene chiamata la funzione udp_sendmsg troviamo
il socket sk e il numero intero size, corrispondente al numero di byte da
inviare.
La semplice sonda udp.sendmsg esporta queste variabili rispettivamente col
nome di sock e size.
2.2.3
Definizione di funzioni
Le funzioni sono definibili mediante la parola chiave function; non è necessario specificare il tipo del valore di ritorno o dei parametri, come si può
notare dall’esempio seguente:
function fibonacci ( i )
{
i f ( i < 1) return 0
e l s e i f ( i < 2) return 1
e l s e r e t u r n f i b o n a c c i ( i −1) + f i b o n a c c i ( i −2)
}
2.2.4
Statistiche
Per uno strumento di analisi è molto importante rendere semplice la raccolta
di statistiche in una maniera che eviti il lock esclusivo delle variabili globali
atte a contenere i valori collezionati. Per questa ragione SystemTap fornisce
un operatore speciale per la raccolta dei valori e varie funzioni per elaborare
i dati di interesse.
L’operatore di aggregazione è <<< ed un esempio del suo utilizzo è il seguente:
probe k e r n e l . f u n c t i o n ( ” k m a l l o c ” ) {
k m a l l o c [ execname ( ) ] <<< $ s i z e
}
8
2.3
Affidabilità e sicurezza
Come già accennato nella sezione 1.1, l’affidabilità e la sicurezza sono tra
i requisiti fondamentali di un sistema di tracciamento dinamico a livello
kernel. SystemTap prevede vari controlli e limitazioni per evitare disastrosi
bug in kernel space.
2.3.1
Memoria, puntatori NULL e divisione per zero
Non è possibile esaurire la memoria a disposizione perché SystemTap non
consente allocazione dinamica. Gli array vengono allocati durante l’inizializzazione della sonda e ogni tentativo di allocazione troppo dispendioso non
consente alla sonda stessa di partire. Inoltre, il numero massimo di elementi
in un array è 2048, modificabile mediante il parametro MAXMAPENTRIES.
Per quanto concerne l’accesso a puntatori NULL e l’esecuzione di operazioni
non valide, in fase di compilazione le operazioni potenzialmente pericolose
vengono tradotte in codice C che controlla gli argomenti passati; in fase di
esecuzione le eventuali operazioni non valide provocano l’interruzione della
sonda e l’emissione di un messaggio di errore.
2.3.2
Cicli infiniti e ricorsione
SystemTap rende inoffensive le sequenze di chiamate ricorsive che non “interrompono” la ricorsione, o più in generale i cicli infiniti, mediante due
limiti configurabili: il numero massimo di chiamate ricorsive permesse è 10,
modificabile tramite il parametro MAXNESTING. Analogamente, MAXACTION
limita a 1000 il numero massimo di statement eseguibili per ogni “probe
hit”.
2.3.3
Carico eccessivo
Per evitare che le sonde possano girare troppo a lungo e dunque impegnare
eccessivamente il sistema è stato introdotto un limite al numero di cicli
spendibili su tutte le sonde per cpu5 durante un determinato numero di
cicli6 .
5
6
STP OVERLOAD THRESHOLD, valore predefinito 500000000
STP OVERLOAD INTERVAL, default a 1000000000
9
Nel caso di sonde che effettivamente necessitino di lunghe computazioni
è anche possibile disabilitare del tutto questo tipo di controlli, definiti di
overload processing, impostando il parametro STP_NO_OVERLOAD.
3
Tapset per i portscan
La comunità ha prodotto un buon numero di tapset orientati ognuno ad un
determinanto ambito d’interesse, in modo da rendere più semplice l’utilizzo
stesso di SystemTap. Non si può pretendere infatti che gli utenti, per quanto tecnicamente preparati, conoscano cosı̀ nel dettaglio il funzionamento del
kernel Linux da sapere quale funzione si occupa di un dato compito. I vari
tapset forniti con SystemTap, risparmiando all’utente i dettagli implementativi del kernel, permettono di associare ad un “concetto”, quale ad esempio
l’invio di un frame Ethernet, una serie di eventi del kernel. In questo modo,
in caso di riorganizzazione del codice del kernel, l’utente non dovrà aggiornare i propri script, visto che la comunità che lavora su SystemTap tiene
costantemente aggiornati i tapset a corredo.
Basandoci sul tapset che si occupa del protocollo TCP abbiamo prodotto
un tapset che fornisce due sonde atte a individuare scansioni NULL, FIX,
XMAS e TCP Connect. Queste tipologie di scansione sono un sottoinsieme
delle tecniche di portscan adottate dal famoso scanner di rete Nmap.
3.1
Scansioni di tipo NULL, FIN e XMAS
Per quanto concerne i primi tre tipi di portscan, l’individuazione tramite
SystemTap è banale: alla ricezione di un pacchetto TCP, mediante la sonda
tcp.receive, si controllano i flag impostati. Se nessuno dei flag TCP è impostato si tratta di una scansione NULL, se è settato il solo flag FIN si tratta
di una FIN scan, se sia FIN che PSH che URG sono impostati si tratta di
una scansione di tipo XMAS.
La variabile name conterrà il nome del tipo di scansione NULL/FIN/XMAS
eventualmente identificata, stringa vuota altrimenti.
/∗ NULL, FIN and XMAS s c a n s a r e e a s y t o c a t c h ∗/
probe p o r t s c a n . n u l l f i n x m a s = t c p . r e c e i v e {
name = ””
i f ( f i n && psh && urg ) name = ”XMAS” ;
10
if
( ! psh && ! urg && ! syn && ! r s t && ! ack )
name = f i n ? ”FIN” : ”NULL” ;
i f ( name == ” ” ) next ;
}
3.2
Scansioni TCP Connect
Riconoscere le scansioni di tipo TCP Connect non è immediato come nel
caso affrontato in precedenza: è infatti necessario individurare i socket TCP
che vengono chiusi senza che attraverso di essi siano effettivamente transitati
dei dati.
3.2.1
Funzioni ausiliarie
Come prima cosa ci servono alcune funzioni atte a leggere i dati presenti
nella struttura skb, resa disponibile dalla sonda tcp.receive, contenuta nel
tapset tcp.
La prima delle funzioni che definiamo calcola la lunghezza del payload di
un dato pacchetto, la seconda costruisce una stringa di identificazione del
socket cui il pacchetto afferisce.
function
t c p p a y l o a d l e n : l o n g ( skb ) {
tcphdr =
g e t s k b t c p h d r ( skb )
r e t u r n @cast ( skb , ” s k b u f f ”)−> l e n −
@cast ( tcphdr , ” t c p h d r”)−> d o f f ∗ 4
}
t c p s o c k s t r : s t r i n g ( skb ) {
function
tcphdr =
g e t s k b t c p h d r ( skb )
dport =
t c p s k b d p o r t ( tcphdr )
sport =
t c p s k b s p o r t ( tcphdr )
iphdr =
g e t s k b i p h d r ( skb )
saddr = ip ntop ( i p s k b s a d d r ( iphdr ) )
daddr = i p n t o p ( i p s k b d a d d r ( i p h d r ) )
r e t u r n s p r i n t f (”% s :%d %s :%d ” , saddr , s p o r t , daddr , d p o r t ) ;
}
11
3.2.2
Tenere traccia dei socket
Abbiamo bisogno di una struttura dati nella quale immagazzinare il numero
di byte di payload effettivamente transitati attraverso ogni socket. Decidiamo di utilizzare un array associativo e come chiave scegliamo la stringa di
identificazione costruita come illustrato nella sezione 3.2.1.
Aggiorniamo la nostra struttura alla ricezione di pacchetti TCP col flag SYN
impostato, prima fase del three-way handshake.
global sockets
probe k e r n e l . f u n c t i o n ( ” t c p v 4 s y n r e c v s o c k ” ) {
t c p p a y l o a d l e n ( $skb )
s o c k e t s [ t c p s o c k s t r ( $skb ) ] =
}
3.2.3
Individuazione dei socket senza traffico
Alla ricezione di un pacchetto TCP verifichiamo l’esistenza della corrispondente stringa di identificazione del socket.
Nel caso in cui la dimensione del payload del pacchetto sia maggiore di 0
cancelliamo il socket corrispondente dalla nostra struttura dati in modo da
ignorare questo e i successivi eventi relativi al socket che stiamo controllando.
Se il pacchetto sotto esame ha i flag ACK e RST impostati ci troviamo
di fronte ad una scansione di tipo TCP connect: il fatto che i due flag
siano impostati implica una richiesta di chiusura del socket e tutti gli eventuali pacchetti ricevuti in precedenza avevano dimensione di payload a 0,
altrimenti il socket sarebbe stato cancellato dalla struttura dati e gli eventi
successivi ignorati.
probe p o r t s c a n . t c p c o n n e c t = t c p . r e c e i v e {
sockstr =
t c p s o c k s t r ( $skb )
i f ( ! ( [ sockstr ] in sockets ))
next ;
s o c k e t s [ s o c k s t r ] +=
t c p p a y l o a d l e n ( $skb )
i f ( s o c k e t s [ s o c k s t r ] > 0) {
/∗ some data has a c t u a l l y been s e n t on t h i s s o c k e t ∗/
delete sockets [ sockstr ]
next ;
}
i f ( ack && r s t ) {
name = ”TCP Connect ”
12
delete sockets [ sockstr ]
}
else
next ;
}
13
4
Anomaly Based Intrusion Detection Systems
L’approccio fin qui adottato per l’identificazione di possibili scansioni in
corso è simile a quello seguito dai cosiddetti Signature Based Intrusion Detection Systems. Tale categoria di IDS confronta il comportamento a runtime del sistema con un database di comportamenti anomali noti: eventuali
corrispondenze vengono considerate come possibili attacchi.
Tra i punti deboli di questa categoria di IDS annoveriamo l’impossibilità di
individuare tutti gli attacchi la cui signature non è presente nel database, la
conseguente necessità di mantenere aggiornate le “definizioni” degli attacchi
noti e il rischio di non individuare il comportamento di tutti gli exploit
volutamente modificati per differire dalle definizioni.
Di contro, gli Anomaly Based Intrusion Detection Systems considerano come anomalo ogni comportamento che si discosta in qualche modo da quello
normale del sistema; tale categoria di IDS non necessita dunque di un database di pattern anomali, ma piuttosto di una definzione di “comportamento
normale”. Per quanto concerne gli IDS di rete il concetto di normalità può
essere definito basandosi sull’analisi dei payload dei pacchetti ricevuti, mentre per i cosiddetti Host-based Intrusion Detection System un metodo per
discernere tra comportamenti anomali e accettabili è l’analisi delle sequenze
di chiamate di sistema.
4.1
Analisi dei payload
La strada dell’analisi dei payload dei pacchetti ricevuti è stata seguita da
Wang e Stolfo [13]: in una prima fase di addestramento del sistema viene
analizzata la dimensione del payload dei pacchetti ricevuti sulle varie porte
e ne viene calcolata la deviazione standard. Questi dati vengono comparati
con le dimensioni dei payload dei pacchetti ricevuti a runtime e vengono
generati degli allarmi al superamento delle soglie di tolleranza impostate.
4.2
Analisi delle sequenze di chiamate di sistema
Il prototipo di Intrusion Detection System sviluppato nel corso di questa
prova finale si basa sull’analisi di brevi sequenze di chiamate di sistema,
approccio teorizzato da Forrest et al [10].
14
L’idea di fondo è di produrre una sorta di “sistema immunitario” per calcolatori e per farlo è necessario riuscire a distinguere i comportamenti normali
da quelli anomali. Il lavoro svolto da Hofmeyr et al [11] dimostra empiricamente la validità dell’uso di sequenze di syscall come discriminante tra
comportamenti legittimi e illegittimi di un programma in esecuzione.
L’approccio proposto consiste nel memorizzare in un database le sequenze uniche di chiamate di sistema per ogni programma in esecuzione sulla
macchina durante un periodo di utilizzo normale del calcolatore. Una volta costruito questo database sarà possibile confrontare le sequenze di syscall
eseguite dai vari processi con quelle ritenute normali, alla ricerca di eventuali
pattern anomali.
5
Implementazione di un Anomaly Based IDS
A livello macroscopico, i due problemi principali da affrontare nell’implementazione di un IDS basato sull’analisi delle sequenze di syscall sono la creazione di un database di comportamenti normali e l’analisi dei comportamenti
in fase di esecuzione per riscontrare eventuali anomalie.
Come prerequisito per affrontare entrambi i problemi abbiamo individuato
lo sviluppo di uno strumento che produca le sequenze di syscall di una certa
lunghezza eseguite dai vari programmi; nella nostra implementazione questo
strumento è stato realizzato mediante una sonda SystemTap, descritta nel
dettaglio nella sezione 5.1.2.
L’output di questa sonda viene analizzato da alcuni script Python che si
occupano di costruire e rendere persistente il database di sequenze uniche,
come spiegato nella sezione 5.2, nonché di scovare eventuali pattern anomali
a runtime (5.3).
5.1
5.1.1
Generazione sequenze di chiamate di sistema
Scelta della lunghezza
La scelta della lunghezza delle sequenze da analizzare è particolarmente delicata: come è ovvio siamo interessati ad avere sequenze il più corte possibile,
in modo da diminuire la dimensione del database e la computazione necessaria in fase di esecuzione per identificare i comportamenti anomali. D’altro
15
canto, evidentemente, vogliamo sequenze sufficientemente lunghe da permetterci di caratterizzare correttamente il comportamento dei programmi
in esame.
Hofmeyr et al [11] concludono che sequenze di dieci syscall sono un buon
compromesso tra questi requisiti contrastanti, mentre Warrender et al [12]
notano che è possibile ottenere risultati soddisfacenti con sequenze di lunghezza sei, migliorando sensibilmente le performance e diminuendo la dimensione del database rispetto all’analisi di sequenze di lunghezza dieci.
Nella nostra implementazione la scelta è lasciata all’utente tramite un parametro di configurazione, il cui valore predefinito è 6: garantendo questa
scelta all’utente si rende necessaria l’implementazione di controlli volti ad
evitare l’utilizzo di un database di sequenze con lunghezza diversa dal valore scelto per il parametro. Cambiandolo, infatti, si rende necessaria la
ricostruzione del database.
5.1.2
Generare le sequenze con SystemTap
Eccoci finalmente allo script SystemTap che si occupa di costruire tutte le sequenze di syscall di una data lunghezza divise per eseguibile. Lo presentiamo
nella sua interezza, descrivendo poi passo passo le operazioni svolte.
global values
global occurs
probe s y s c a l l . ∗ {
e x e c = execname ( )
i f ( e x e c == ” s t a p ” | | e x e c == ” s t a p i o ” )
next ;
v a l u e s [ exec , o c c u r s [ e x e c ] ] = name
o c c u r s [ e x e c ]++
i f ( o c c u r s [ e x e c ] == $1 ) {
p r i n t f (”% s %d ” , exec , u i d ( ) )
f o r ( i =0; i < $1 ; i ++) {
p r i n t f ( ” %s ” , v a l u e s [ exec , i ] )
v a l u e s [ exec , i ] = v a l u e s [ exec , i +1]
}
p r i n t f (”\n ” ) ;
o c c u r s [ e x e c ]−−
}
16
}
Lo script in questione è piuttosto breve ma ci permette di illustrare varie
caratteristiche di SystemTap.
Gli eventi che vogliamo individuare sono, banalmente, le invocazioni di chiamate di sistema. Il tapset syscalls.stp ci viene in aiuto, esponendo varie
sonde quali per esempio syscall.brk, syscall.chmod e cosı̀ via.
Attendiamo dunque l’esecuzione di una qualunque chiamata di sistema, assegnando alla variabile exec il nome dell’eseguibile e ignorando le syscall
relative agli eseguibili stap e stapio.
probe s y s c a l l . ∗ {
e x e c = execname ( )
i f ( e x e c == ” s t a p ” | | e x e c == ” s t a p i o ” )
next ;
A questo punto memorizziamo nell’array associativo values il nome della
chiamata corrente; gli indici di values sono nell’ordine: il nome dell’eseguibile
che ha effettuato la determinata syscall e la posizione nella sequenza della
stessa. La posizione corrente viene mantenuta, per eseguibile, nell’array
occurs.
v a l u e s [ exec , o c c u r s [ e x e c ] ] = name
o c c u r s [ e x e c ]++
Come si diceva, la lunghezza delle sequenze è un parametro configurabile,
che passiamo allo script nella variabile $1: raggiunta la lunghezza desiderata
scriviamo su standard output il nome dell’eseguibile, lo user id e la sequenza
identificata. Durante l’output della sequenza ci occupiamo inoltre di fare
scorrere verso sinistra la finestra.
i f ( o c c u r s [ e x e c ] == $1 ) {
p r i n t f (”% s %d ” , exec , u i d ( ) )
f o r ( i =0; i < $1 ; i ++) {
p r i n t f ( ” %s ” , v a l u e s [ exec , i ] )
v a l u e s [ exec , i ] = v a l u e s [ exec , i +1]
}
p r i n t f (”\n ” ) ;
o c c u r s [ e x e c ]−−
}
L’output ottenuto è di questo tipo:
17
gnome−t e r m i n a l 1000 g e t t i m e o f d a y p o l l g e t t i m e o f d a y g e t t i m e o f d a y w r i t e p o l l
gnome−t e r m i n a l 1000 p o l l g e t t i m e o f d a y g e t t i m e o f d a y w r i t e p o l l p o l l
Nell’esempio, la lunghezza della sequenze è stata impostata a 6 e l’eseguibile
gnome-terminal ha invocato nell’ordine queste syscall:
• gettimeofday
• poll
• gettimeofday
• gettimeofday
• write
• poll
• poll
5.2
Il database dei comportamenti normali
Per produrre il database dei comportamenti normali partiamo dunque dal
copioso output generato dalla sonda SystemTap di cui al punto 5.1.2, lo
analizziamo individuando le sequenze uniche e serializziamo i dati raccolti
mediante lo script Python che andiamo a descrivere.
5.2.1
Generazione sequenze uniche
Lo script builddb.py si avvale di due moduli per assolvere il suo compito: il
primo di questi, reader.py, genera una struttura dati contenente le sequenze uniche divise per eseguibile. La parte fondamentale di questa struttura
è il dizionario executables, che contiene i dati veri e propri divisi per eseguibile; per esempio, si può accedere alle sequenze generate da firefox con:
executables[’firefox’].
Considerata la mole di dati da analizzare è importante assicurarsi di utilizzare un metodo efficiente per generare l’insieme di sequenze uniche: per questa
ragione abbiamo pensato di implementare una classe generica che effettua
la lettura dell’input, lasciando a classi più specifiche l’implementazione del
18
metodo che si occupa di verificare se una data sequenza è già presente nel
database, cosı̀ come l’inserimento di una sequenza nuova.
Il metodo go() analizza ogni linea in input, chiamando il metodo addseq(eseguibile, sequenza) che garantisce l’inserimento nel dizionario executables descritto poc’anzi solo in caso di sequenza sconosciuta.
knownseq(eseguibile, sequenza), risulterà utile in fase di esecuzione, come
descritto nella sezione 5.3.
class SyscallDataReader ( object ) :
[ ... ]
def go ( s e l f ) :
while True :
# Not u s i n g r e a d l i n e s ( ) t o a l l o w u n b u f f e r e d i n p u t
i n p u t l i n e = s e l f . input . r e a d l i n e ()
i f not i n p u t l i n e :
s e l f . e n d i n g = time . time ( )
break
execname , uid , c a l l s = l i n e 2 d a t a ( i n p u t l i n e )
s e l f . addseq ( execname , c a l l s )
def addseq ( s e l f , execname , c a l l s ) :
r a i s e NotImplementedError
def knownseq ( s e l f , execname , c a l l s ) :
r a i s e NotImplementedError
Le varie specializzazioni si differenziano a seconda del tipo di struttura dati
usata per rappresentare i valori del dizionario executables.
L’implementazione mostratasi più efficiente si avvale della struttura dati set,
inclusa tra le strutture dati di base di Python.
class SetSyscallDataReader ( SyscallDataReader ) :
def addseq ( s e l f , execname , c a l l s ) :
i f execname not in s e l f . e x e c u t a b l e s :
s e l f . e x e c u t a b l e s [ execname ] = s e t ( [ ] )
s e l f . e x e c u t a b l e s [ execname ] . add ( c a l l s )
def knownseq ( s e l f , execname , c a l l s ) :
return c a l l s in s e l f . e x e c u t a b l e s [ execname ]
19
Come si può notare, l’implementazione di un “reader” è questione di poche
righe, consentendo di sperimentare facilmente vari algoritmi e strutture dati.
A titolo d’esempio, la seguente implementazione usa la struttura dati list:
class ListSyscallDataReader ( SyscallDataReader ) :
def addseq ( s e l f , execname , c a l l s ) :
i f execname not in s e l f . e x e c u t a b l e s :
s e l f . e x e c u t a b l e s [ execname ] = [ ]
i f not s e l f . knownseq ( execname , c a l l s ) :
s e l f . e x e c u t a b l e s [ execname ] . append ( c a l l s )
def knownseq ( s e l f , execname , c a l l s ) :
return c a l l s in s e l f . e x e c u t a b l e s [ execname ]
5.2.2
Serializzazione
dbaccess.py è il secondo modulo utilizzato dallo script per la costruzione
del database, e fornisce le funzioni utili a salvare su file e successivamente
caricare la struttura dati contenente le sequenze uniche raccolte.
import o s
import s h u t i l
import c P i c k l e
import c o n f i g
def d b e x i s t s ( ) :
return o s . path . i s f i l e ( c o n f i g .FILENAME)
def g e t d a t a ( ) :
dbf = open ( c o n f i g . FILENAME, ’ r ’ )
r e a d e r = c P i c k l e . l o a d s ( dbf . r e a d ( ) )
dbf . c l o s e ( )
return r e a d e r
def putdata ( data ) :
if dbexists ( ) :
backup = c o n f i g .FILENAME + ” . o l d ”
print ” C r e a t i n g backup f i l e ” , backup , ” b e f o r e s a v i n g d a t a b a s e . . . ” ,
s h u t i l . copy ( c o n f i g . FILENAME, backup )
print ” done . ”
# can ’ t p i c k l e f i l e o b j e c t s
data . i n p u t = None
20
dbf = open ( c o n f i g . FILENAME,
c P i c k l e . dump( data , dbf )
dbf . c l o s e ( )
5.3
’wb ’ )
Individuazione di comportamenti anomali
Lo script Python responsabile dell’individuazione dei comportamenti anomali è l’ultima componente del nostro sistema.
Il suo funzionamento è concettualmente molto semplice: per ogni sequenza in input ne calcola la distanza di Hamming minima rispetto a tutte le
sequenze presenti nel database. Se la distanza minima supera il valore configurabile ALLOWED_MISMATCHES, di default impostato a 2, la sequenza viene
considerata anomala.
Di seguito riportiamo la funzione atta a calcolare la distanza minima di
una sequenza rispetto a quelle note: per ogni sequenza nota ne calcoliamo
la distanza rispetto a quella in esame, fermandoci se il valore trovato è
inferiore o uguale al numero di differenze concesse. In tal caso, infatti, ci
troviamo di fronte a una sequenza legittima.
def m i n d i s t a n c e ( s e q u e n c e , known seqs , d i s t a n c e=d i s t a n c e x r a n g e ) :
minimum = c o n f i g .SEQUENCE LENGTHS
f o r known in known seqs :
v a l = d i s t a n c e ( known , s e q u e n c e )
i f v a l <= c o n f i g .ALLOWED MISMATCHES:
return v a l
i f v a l < minimum :
minimum = v a l
return minimum
La funzione distance(), usata per calcolare la distanza di Hamming tra due
sequenze, viene passata come parametro in modo da poterne sperimentare
facilmente varie implementazioni e valutarne l’efficienza come descritto nella
sezione che segue.
21
5.3.1
Confronto algoritmi per il calcolo della distanza
Evidentemente, l’operazione eseguita più di frequente a runtime è il calcolo
della distanza tra due sequenze; ne segue che la scelta di un buon algoritmo
è particolarmente importante.
Siamo partiti da una versione molto “idiomatica” dell’algoritmo, ma inefficiente. Il codice che segue itera sugli elementi usando la funzione zip(),
che date due sequenze costruisce la lista delle coppie di eguale posizione.
Costruisce quindi mediante una list comprehenshion, da cui il nome dell’algoritmo, una lista di booleani che assumeranno valore True in caso di
diversità della coppia di elementi e False in caso contrario. A questo punto,
la funzione sum() somma gli elementi della lista e considera False uguale a
0 e True uguale a 1, ritornando infine la distanza di Hamming.
def d i s t a n c e c o m p r e h e n s i o n ( s1 , s 2 ) :
return sum ( [ ch1 != ch2 f o r ch1 , ch2 in z i p ( s1 , s 2 ) ] )
Per provare a migliorare i tempi di esecuzione abbiamo evitato di costruire una lista, iterando su una sequenza mediante la funzione enumerate e
contando quante volte gli elementi dell’altra differiscono:
def d i s t a n c e e n u m e r a t e ( s1 , s 2 ) :
mismatches = 0
f o r idx , elem in enumerate ( s 1 ) :
i f elem != s 2 [ i d x ] :
mismatches += 1
return mismatches
Come ultima inefficienza eliminabile notiamo che la lunghezza della sequenza
è a noi nota a priori. Non è dunque necessario usare la funzione enumerate(),
che itera su una sequenza ritornando a ogni iterazione indice e valore, ma
ci basta andare da 0 ad N, dove N è la lunghezza delle sequenze, tramite la
funzione xrange.
def d i s t a n c e x r a n g e ( s1 , s 2 ) :
mismatches = 0
f o r i d x in xrange ( c o n f i g .SEQUENCE LENGTHS ) :
i f s 1 [ i d x ] != s 2 [ i d x ] :
mismatches += 1
return mismatches
22
Come benchmark abbiamo scelto di calcolare 100 volte la distanza tra una
sequenza del tutto “sbagliata”, perché composta solo da syscall inesistenti,
e le sequenze prodotte dagli eseguibili gnome-screensaver e firefox in 4 ore di
raccolta dati. Scegliendo una sequenza composta da sole syscall inesistenti
valutiamo il comportamento degli algoritmi nel caso peggiore, visto che sarà
necessario calcolare la distanza rispetto a tutte le sequenze note.
Il benchmark è stato ripetuto 3 volte e di seguito riportiamo il minore dei
tempi impiegati nel corso delle esecuzioni.
algoritmo
comprehension
enumerate
xrange
gnome-screensaver (771)
1.330 s
1.061 s
0.99 s
firefox (15558)
28.9 s
23.3 s
21.3 s
Concludiamo dunque che, anche nel caso peggiore, la versione dell’algoritmo
che utilizza la funzione xrange è da preferirsi.
6
6.1
Valutazione del sistema proposto
Impatto sulle performance del sistema operativo
Ogni processo può eseguire un numero considerevole di chiamate di sistema al secondo, che vanno organizzate in sequenze di una data lunghezza.
L’elaborazione di una mole di dati di questo tipo porta in pochi secondi al
superamento delle soglie descritte nel paragrafo 2.3.3.
Proviamo a compilare lo script presentato nella sezione 5.1.2 e ad eseguirlo:
$ s t a p −p 4 a l l −s e q u e n c e s . s t p −m t e s t a l l s e q u e n c e s . ko 6
$ time s t a p r u n t e s t a l l s e q u e n c e s . ko > /tmp/ s e q u e n c e s
ERROR: probe o v er he ad e x c e e d e d t h r e s h o l d
WARNING: Number o f e r r o r s : 1 , s k i p p e d p r o b e s : 0
real
4m31 . 7 8 9 s
user
0m0. 0 2 8 s
s y s 0m1. 3 3 2 s
Lo script ha catturato 1671907 sequenze di 6 chiamate di sistema consecutive
in circa 4 minuti e mezzo prima di venire fermato dalla logica di controllo
dell’overhead. Decisamente troppo poco per riuscire a costruire un database
utilizzabile.
23
Compilando invece il modulo con -DSTP_NO_OVERLOAD è possibile disabilitare i controlli sull’overhead, consentendo al nostro script di procedere nell’esecuzione. Ovviamente, però, le operazioni svolte continueranno a essere
parecchio impegnative per il sistema: per evitare di renderlo inutilizzabile è
sufficiente impostare la priorità di scheduling di staprun a 19, in modo da
garantire ai processi utente di lavorare senza intoppi.
Si veda, ad esempio, come si è proceduto per lo script che si occupa del
controllo a runtime:
#! / b i n / sh
nice −n 19 s t a p r u n a l l s e q u e n c e s . ko | nice −n 19 python r u n t i m e c h e c k . py
Cosı̀ facendo è possibile costruire un database di dimensioni arbitrarie e
verificare la presenza di comportamenti anomali senza per questo rendere
inutilizzabile la macchina in esame.
Ad esempio, in 4 ore siamo stati in grado di costruire un database contenente
i dati di 103 eseguibili per un totale di 69974 sequenze uniche.
Di seguito riportiamo il numero di sequenze uniche identificate per i dieci
eseguibili più “attivi”:
eseguibile
rhythmbox
firefox
transmission
Xorg
evince
bash
pulseaudio
vim
gnome-terminal
gnome-panel
6.2
sequenze uniche
15622
15558
6987
5368
3306
2067
1745
1425
1327
1069
Riconoscimento di comportamenti anomali
Analizziamo, finalmente, il funzionamento del nostro sistema, sia per quanto concerne l’individuazione di attacchi e comportamenti di dubbia natura
che dal punto di vista dei falsi positivi, cioè dei comportamenti legittimi
erroneamente ritenuti anomali.
24
6.2.1
Individuazione di rootkit
Un rootkit è una collezione di programmi che, tra le altre cose, vengono
utilizzati da un attaccante che ha ottenuto accesso al sistema per nascondere
l’avvenuta intrusione agli amministratori.
Abbiamo installato la versione dell’eseguibile netstat presente nel rootkit
ark versione 1.0.1 su una macchina protetta dal nostro IDS. Questa versione
di netstat è modificata in modo da non mostrare le connessioni in corso che
rispettano determinati criteri scelti a piacere dall’attaccante. Come criterio
abbiamo scelto di nascondere tutti i server in ascolto sull’indirizzo 127.0.0.1.
Ecco l’output prodotto dall’eseguibile netstat originale:
ema@mars : ˜ / Desktop / ark − 1 . 0 . 1 $ sudo n e t s t a t . o l d −napt | g r e p 127
tcp
0
0 127.0.0.1:631
0.0.0.0:∗
LISTEN
4294/ cupsd
Il server TCP cups è dunque in ascolto sulla porta 631, indirizzo 127.0.0.1
Cercando di ottenere la stessa informazione con l’eseguibile netstat fornito
con il rootkit ark non otteniamo invece nessun output, proprio come ci
aspettavamo:
ema@mars : ˜ / Desktop / ark − 1 . 0 . 1 $ sudo n e t s t a t −napt | g r e p 127
ema@mars : ˜ / Desktop / ark − 1 . 0 . 1 $
Il nostro IDS identifica il comportamento di netstat come potenzialmente
malizioso, scovando ben quattro sequenze del tutto anomale (sei syscall su
sei):
6 netstat ( ’ rt sigprocmask ’ , ’ fork ’ , ’ r t s i g a c t i o n ’ ,
’ r t s i g a c t i o n ’ , ’ r t s i g p r o c m a s k ’ , ’ wait4 ’ )
6 netstat ( ’ fork ’ , ’ r t s i g a c t i o n ’ , ’ r t s i g a c t i o n ’ ,
’ r t s i g p r o c m a s k ’ , ’ wait4 ’ , ’ r t s i g a c t i o n ’ )
6 netstat ( ’ r t s i g a c t i o n ’ , ’ r t s i g a c t i o n ’ , ’ rt sigprocmask ’ ,
’ wait4 ’ , ’ r t s i g a c t i o n ’ , ’ r t s i g a c t i o n ’ )
6 n e t s t a t ( ’ r t s i g a c t i o n ’ , ’ r t s i g p r o c m a s k ’ , ’ wait4 ’ ,
’ r t s i g a c t i o n ’ , ’ r t s i g a c t i o n ’ , ’ rt sigprocmask ’ )
6.2.2
Scansioni con Nessus
Nessus è uno strumento per scoprire vulnerabilità su sistemi remoti, caratterizzato da un gran numero di plugin che consentono di abilitare controlli
25
di vario genere. Alcuni di questi plugin cercano di sfruttare vulnerabilità
note di servizi quali OpenSSH e Samba in modo a volte piuttosto invasivo;
Nessus è dunque un ottimo banco di prova per il nostro IDS.
Il nostro IDS è stato allenato ad un uso molto basilare dei servizi disponibili
(Cups, Samba e OpenSSH). Via Cups abbiamo stampato un paio di pagine
di prova, Samba è stato usato per la condivisione di uno share Windows e
OpenSSH per una mezza dozzina di accessi da remoto, alcuni a buon fine e
altri sbagliando password.
Abbiamo provato a effettuare una scansione Nessus da remoto, usando tutti
i plugin forniti con l’installazione di default dello scanner.
In tabella sono riassunti i dati relativi agli alert prodotti dal nostro IDS;
per ogni eseguibile riportiamo il numero di sequenze uniche presenti nel
database, il numero di sequenze anomale identificate durante la scansione
(con ripetizioni) ed il numero massimo di mismatch.
servizio
sshd
cupsd
smbd
6.2.3
sequenze uniche
1173
1100
864
sequenze anomale
468
10
215
numero massimo differenze
5
4
5
Falsi positivi
È ovviamente possibile che alcuni comportamenti legittimi vengano erroneamente ritenuti anomali. Per quanto durante la fase di creazione del database
ci si sforzi di riprodurre quanti più casi d’uso comune del sistema, può capitare che alcuni eseguibili effettuino delle operazioni particolari a runtime,
per esempio per qualche cambiamento nell’ambiente operativo della macchina analizzata. Supponiamo il caso di una rete casalinga a cui si colleghi una
nuova macchina, non presente durante la fase di creazione del database: il
tentativo da parte di quest’ultima di scoprire la presenza di una stampante di rete potrebbe essere visto dal demone di stampa del calcolatore sotto
controllo come anomalo.
Per portare un esempio ancor più concreto, in fase di test del sistema abbiamo modificato uno script usato dalla macchina aggiungendo l’uso del
comando sort. Dopo pochi minuti di analisi, la macchina (chiaramente non
sotto attacco) ha iniziato a produrre i seguenti alert:
4 s o r t ( ’ read ’ ,
’ read ’ ,
’ read ’ ,
’ read ’ ,
26
’ read ’ ,
’ read ’ )
4
4
4
4
sort
sort
sort
sort
( ’ read
( ’ read
( ’ read
( ’ read
’
’
’
’
,
,
,
,
’ read
’ read
’ read
’ read
’
’
’
’
,
,
,
,
’ read
’ read
’ read
’ read
’
’
’
’
,
,
,
,
’ read
’ read
’ read
’ read
’
’
’
’
,
,
,
,
’ read
’ read
’ read
’ read
’
’
’
’
,
,
,
,
’ read
’ read
’ read
’ read
’)
’)
’)
’)
Il comportamento suddetto è perfettamente accettabile; abbiamo dunque
fatto girare lo script di creazione del db, il quale in caso di db già esistente
si limita ad aggiungere le nuove sequenze piuttosto che ripartire da zero, controllando in seguito tramite l’interprete Python la presenza della sequenza
ritenuta anomala.
ema@mars : ˜ / dev / systemtap−i d s −poc$ python
iPython 2 . 6 . 2 ( r e l e a s e 2 6 −maint , Apr 19 2 0 0 9 , 0 1 : 5 6 : 4 1 )
[GCC 4 . 3 . 3 ] on l i n u x 2
Type ” h e l p ” , ” c o p y r i g h t ” , ” c r e d i t s ” o r ” l i c e n s e ” f o r more i n f o r m a t i o n .
>>> import d b a c c e s s
>>> data = d b a c c e s s . g e t d a t a ( )
>>> p r i n t ( ’ read ’ , ’ read ’ , ’ read ’ , ’ read ’ , ’ read ’ , ’ read ’ ) \
i n data . e x e c u t a b l e s [ ’ s o r t ’ ]
True
>>>
La sequenza è stata aggiunta al database, scongiurando cosı̀ il verificarsi del
falso positivo causato dal “nuovo” utilizzo del comando sort.
7
Conclusioni
SystemTap è un noto sistema per il tracciamento dinamico del funzionamento del kernel Linux; questo genere di strumenti consente in modo relativamente semplice di analizzare nel dettaglio il comportamento di un sistema
operativo in produzione. In questa prova finale abbiamo dimostrato come
sia possibile scrivere degli script SystemTap per individuare determinati tipi
di comportamenti potenzialmente ostili come ad esempio dei portscan.
Abbiamo inoltre sviluppato un Anomaly Based Intrusion Detection
System usando l’analisi di brevi sequenze di chiamate di sistema per individuare i comportamenti potenzialmente anomali. Il sistema sviluppato,
composto principalmente da uno script SystemTap e una mezza dozzina di
script Python, si è rivelato in grado di riconoscere tentativi di intrusione da
remoto generati tramite lo scanner di sicurezza Nessus, nonché la presenza
di rootkit locali, nella fattispecie ark.
27
Il sistema è stato rilasciato come software libero secondo i termini della
Apache License, versione 2.0 ed è esaminabile online all’indirizzo seguente:
http://bitbucket.org/ema/systemtap-ids-poc/src/. Per utilizzarlo è sufficiente scaricarne i sorgenti, disponibili all’indirizzo http://bitbucket.org/ema/systemtapids-poc/downloads/ e seguire il file README.
28
Riferimenti bibliografici
[1] Bryan M. Cantrill et al. Dynamic Instrumentation of Production
Systems Sun Microsystems Inc, 2004.
[2] Vara Prasad et al. Locating System Problems Using Dynamic
Instrumentation IBM Corp, Red Hat Inc, Intel Corporation, 2005.
[3] Elena Zannoni, Linux Tips and Tricks for Developers Oracle
Corporation, 2007.
[4] Jonathan Corbet, On DTrace envy Linux Weekly News, August 2007.
[5] Frank Ch. Eigler, Vara Prasad et al. Architecture of SystemTap: a
Linux trace/probe tool Red Hat Inc, IBM Corp, July 2005.
[6] The SystemTap Community, stap manual page Red Hat Inc
[7] Bart Jacob, Paul Larson et al. SystemTap: Instrumenting the Linux
Kernel for Analyzing Performance and Functional Problems IBM Corp,
2009.
[8] The SystemTap Community, SystemTap Language Reference Red Hat
Inc, IBM Corp, Intel Corporation, 2009.
[9] Mark Wielaard, A SystemTap update Linux Weekly News, January
2009.
[10] Stephanie Forrest, Steven A. Hofmeyr, Anil Somayaji, Thomas A.
Longstaff A Sense of Self for Unix Processes University of New Mexico,
CERT Coordination Center, 1996.
[11] Steven A. Hofmeyr, Stephanie Forrest, Anil Somayaji Intrusion Detection using Sequences of System Calls University of New Mexico,
1997
[12] Christina Warrender, Stephanie Forrest, Barak Pearlmutter Detecting
Intrusions Using System Calls: Alternative Data Models University of
New Mexico, 1999.
[13] Ke Wang, Salvatore J. Stolfo Anomalous Payload-based Network
Intrusion Detection Columbia University.
29