universit`a degli studi di milano progettazione e

annuncio pubblicitario
UNIVERSITÀ DEGLI STUDI DI MILANO
FACOLTÀ DI SCIENZE MATEMATICHE, FISICHE E NATURALI
CORSO DI LAUREA TRIENNALE IN INFORMATICA
PROGETTAZIONE E SVILUPPO DI UN SOFTWARE
PER L’ANALISI POST MORTEM DI UN HOST
WINDOWS
Relatore:
Prof. Danilo BRUSCHI
Correlatore:
Dott. Lorenzo CAVALLARO
Secondo Correlatore:
Dott. Andrea LANZI
Elaborato Finale di:
Lorenzo VALERIO
Matricola 626250
Anno Accademico 2004–05
i
Ai miei genitori,
che hanno reso possibile tutto ciò.
ii
Ringraziamenti
Ringrazio il Professor Danilo Bruschi per avermi dato la possibilità di svolgere questa
tesi.
Un ringraziamento più che doveroso va ai miei due fantastici correlatori: Lorenzo
Cavallaro e Andrea Lanzi che mi hanno seguito per tutta la durata del progetto, e mi
hanno insegnato un sacco di cose che non conoscevo, grazie davvero.
Ringrazio i miei zii per avermi dato quell’aiuto senza il quale non avrei mai potuto
raggiungere questa tappa della mia vita.
Inoltre voglio dire grazie a tutti i ragazzi del laboratorio LASER e in particolare a
Lorenzo Martignoni per tutti i piccoli consigli e la bella accoglienza che ho ricevuto.
Indice
1
2
Malicious software
3
1.1
Tipologie di malware conosciute . . . . . . . . . . . . . . . . . . . .
3
1.1.1
Virus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
1.1.2
Worm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
1.1.3
Trojan horse e Spyware . . . . . . . . . . . . . . . . . . . . .
5
1.1.4
Backdoor . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
1.1.5
Logicbomb e Timebomb . . . . . . . . . . . . . . . . . . . .
6
1.2
Cenni storici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
1.3
Statistiche di incidenti informatici . . . . . . . . . . . . . . . . . . .
14
I Rootkit
16
2.1
Definizione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
2.2
Fasi di un attacco . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
2.2.1
Information Gathering . . . . . . . . . . . . . . . . . . . . .
17
2.2.2
Exploitation . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
2.2.3
Metastasi . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
Il sistema Windows . . . . . . . . . . . . . . . . . . . . . . . . . . .
22
2.3.1
Cenni sull’architettura di Windows . . . . . . . . . . . . . .
22
2.3.2
User mode VS Kernel mode . . . . . . . . . . . . . . . . . .
29
2.3.3
Hooking e hiding in Windows . . . . . . . . . . . . . . . . .
30
2.3.4
Un esempio di win32 user land rootkit: NTIllusion . . . . . .
40
2.3
iii
INDICE
iv
3
Static and Dynamic Rootkit Detection
50
3.1
HFF: Hidden File Finder . . . . . . . . . . . . . . . . . . . . . . . .
50
3.1.1
Parte di un progetto piú grande . . . . . . . . . . . . . . . . .
50
3.1.2
L’idea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
3.1.3
Sviluppo e implementazione . . . . . . . . . . . . . . . . . .
54
3.1.4
Conclusioni e sviluppi futuri . . . . . . . . . . . . . . . . . .
64
Bibliografia
66
Introduzione
L’elaborato finale tratterà l’analisi post-mortem di una macchina Windows, in particolare verrà sviluppato uno strumento per poter analizzare il comportamento dell’ultima
fase di un attacco informatico, la “metastasi”.
Durante un attacco informatico possiamo individuare tre fasi principali:
1. Infomation Gathering
2. Exploitation
3. Metastasi
• consolidation
• continuation
Le prime due fasi verranno trattate all’interno dell’elaborato, per ora basti sapere che,
la fase di consolidation della metastasi mira a cancellare, per quanto possibile, le fasi
dell’attacco e ad installare un eventuale backdoor; mentre la fase della continuation ha
come obiettivo la propagazione dell’attacco verso host limitrofi.
La tesi sarà quindi incentrata sull’analisi della fase di consolidation, si tenterà di
sviluppare una tecnica per poter rilevare un’eventuale compromissione del sistema.
Il tipo di malicious code sul quale è stata focalizzata l’attenzione è il Trojan horse, che
da un punto di vista più generale, viene identificato come rootkit. Un rootkit è descritto
dalla seguente architettura:
1
INDICE
2
1. File nascosti, contenenti informazioni di vario genere, dalla raccolta di dati sensibili del sistema (ad esempio password), a file contenenti chiavi crittografiche
sfruttabili, per esempio, dalle backdoor installate.
2. File modificati, fra questi troviamo tutti i file che in qualche modo possono
fornire informazioni sul sistema e quindi rilevare un’eventuale compromissione
(ad esempio dir, netstat etc.)
3. File di backdoor, sono tutti quei programmi che permettono all’attaccante di
“tornare” in modo più agevole sul sistema compromesso.
In una prima fase, che nella fattispecie combacia con il software da me sviluppato,
l’analisi verte sulla ricerca dei file nascosti; il procedimento utilizzato è il seguente:
• Una prima lettura dei dati su disco viene fatta utilizzando le API fornite dal
sistema, le quali, si presume siano state compromesse. Successivamente viene
fatta una seconda lettura dei dati su disco utilizzando un sistema fidato, che nel
qual caso si tratta di un sistema Windows live costruito in modo tale da contenere
il software per effettuare tale analisi.
• Una volta compiuta la prima fase di acquisizione delle informazioni, avviene un
confronto tra l’output ottenuto usando le API untrusted e quello ottenuto usando
le API trusted.
L’elaborato finale è strutturato nel seguente modo:
Nel capitolo 1 si potranno trovare delle informazioni generali riguardanti i vari tipi di
malicious code, alcuni cenni storici riguardanti la nascita e lo sviluppo dei virus e una
piccola sezione sulla situazione attuale riguardo la diffusione delle varie tipologie di
codici maligni.
Nel capitolo 2 verranno trattati nello specifico i rootkit, che sono l’argomento portante
di questo elaborato finale, le fasi di un attacco informatico accennate brevemente in
questa introduzione e infine alcuni cenni sull’architettura del sistema Windows.
Nel terzo e ultimo capitolo verrà presentato l’intero progetto di analisi e più nello
specifico la parte di progettazione e sviluppo riguardante il software sviluppato per
effettuare la ricerca dei file nascosti.
Capitolo
1
Malicious software
1.1
Tipologie di malware conosciute
Il termine Malicious software (malware) indica un qualsiasi software creato con lo
scopo di recare danni più o meno estesi al computer sul quale viene seguito. Qui di
seguito verranno presentate le tipologie di malware più conosciute con lo scopo di
fornire una panoramica generale sull’argomento.
• Virus
• Worm
• Trojan e Spyware
• Backdoor
• Logicbomb e Timebomb
1.1.1
Virus
Questo è il tipo più comune di malware; la motivazione per la quale è noto come
virus, è dovuta all’analogia con un virus biologico: la propagazione avviene attraverso
l’interazione con una parte contaminata. I virus sono parti di software auto-replicanti
che, analogalmente ad un virus biologico, si attaccano ad un altro programma, o nel
3
Capitolo 1.
Malicious software
4
caso di un “macro-virus”, ad un altro file. Il virus si attiva solo quando il programma
o file viene eseguito o aperto. È proprio questo che differenzia un virus da un worm.
Se non si attiva il programma o file, il virus non viene eseguito e non si propaga
ulteriormente.
Virus nel settore di boot
La prima tipologia di virus creata è stata quella che infettava il settore di boot. Questo
significa che per infettare una macchina è necessario effettuare il boot da un floppy
infetto oppure riuscire ad ottenere i diritti di amministratore sulla macchina in modo
da poter avere i premessi per scrivere sul Master Boot Record (MBR). La maggior
parte dei virus lascia una firma rilevabile dai successivi tentativi di infezione, in modo
da non infettare ripetutamente lo stesso obiettivo.
Virus nei file eseguibili
I virus nei file eseguibili si inseriscono all’interno di file, quali .exe o .com.
Alcuni virus cercano programmi che sono parte integrante del sistema operativo (explorer.exe) , in modo tale da venir eseguiti tutte le volte che il pc viene acceso aumentando la loro possibilità di propagazioni successive. Le tecniche per infettare tramite
un virus un file eseguibile sono molteplici, ma per ragioni di attinenza, non verrano
trattate esaustivamente. La modalità più semplice è sovrascrivere la prima parte del
file eseguibile con il codice del virus, oppure porlo alla fine del file eseguibile.
Virus TSR (Terminate and Stay Resident)
TSR è un termine che indica un’applicazione che si carica in memoria e successivamente vi rimane in background. I virus più complessi di questo tipo intercettano le
chiamate di sistema, utilizzandole come trampolino per poter propagare l’infezione;
altri si attaccano al comando “dir” e infettano ogni applicazione della directory sulla
quale si è fatto il listing, altri ancora terminano (o cancellano) il software anti-virus
installato sul sistema.
Capitolo 1.
Malicious software
5
Virus Polimorfi
Questi virus modificano il loro codice ogni volta che si replicano, al fine di eludere i
controlli dei software anti-virus basati sul riconoscimento di signature di codice fissato
rappresentanti il virus o parti di esso.
Macro Virus
Il macro virus sfrutta la capacità propria di molti programmi, ad eseguire al loro interno
del codice esterno. Applicazioni come MS Word e MS Excel hanno una versione semplificata del linguaggio di programmazione Visual Basic, ma molto potente. Questo
consente l’automazione delle operazioni ripetitive e la configurazione automatica di
impostazioni. Questi macro linguaggi sono utilizzati per allegare ai documenti codice
virale che si copierà automaticamente su altri documenti e si propagherà.
1.1.2
Worm
Un worm è un programma che, dopo essere stato avviato, si replica autonomamente.
Si propaga da un host a un altro, sfruttando vulnerabilità di uno o più servizi non
protetti. La maggior parte degli incidenti recenti sono stati causati da worm piuttosto che da virus. Uno dei worm più famosi e datati fu creato da Robert Morris nel
1988 [21]. Questo worm fece uso di un’imperfezione del comando finger di UNIX e
altre vulnerabilità, per bloccare la maggior parte di Internet.
1.1.3
Trojan horse e Spyware
I trojan horse sono parti di software dannoso mascherato come qualcosa di utile o
desiderabile per far si che vengano eseguiti. A questo punto gli stessi danneggiano il
computer installando una backdoor o un rootkit. Il primo trojan horse (cavallo di troia)
fu creato dai greci migliaia di anni fa. Il concetto base è quello di costruire qualcosa
di sgradevole nel computer sicuro di qualcuno sotto la parvenza di qualcosa di piacevole. Questo varia da un trailer di un gioco a qualsiasi cosa possa indurre l’utente ad
eseguire il programma infetto.
Capitolo 1.
Malicious software
6
Uno spyware è un tipo di software che raccoglie informazioni riguardanti un utente
senza il suo consenso, trasmettendole tramite internet ad un’organizzazione che le
utilizzerà, tipicamente attraverso l’invio di pubblicità mirata (spam). Lo spam non
è l’unica funzionalità possibile di uno spyware, altre posso essere la modifica della
pagina iniziale del browser oppure attività illegali quali la redirezione su falsi siti i
e-commerce (phishing) o altro ancora.
1.1.4
Backdoor
Spesso quando un computer è stato compromesso, viene installato un meccanismo per
ottenere un facile accesso alla macchina. Ci sono molte varianti, alcune delle quali
sono diventate abbastanza famose, come ad esempio “Back Orifice” [1]. Le Backdoor
(porte di servizio) sono software che creano meccanismi per accedere ad una macchina. Variano dal semplice, un programma che ascolta su una porta, al complesso
(programmi che nascondono processi in memoria, modificano file di log e ascoltano
su una porta). Spesso creare una backdoor può essere un compito semplice; ad esempio
è sufficiente creare un nuovo utente con privilegi da super-user nella speranza che non
venga individuato. Questo perché una backdoor è progettata per permettere ad un
attaccante di non dover ripetere nuovamente l’attacco una volta ottenuto l’accesso alla
macchina. Un esempio di backdoor, si può trovare nel worm MyDoom [18] che ne
installa una sulla porta 3127/tcp per permettere di controllare la macchina da remoto.
1.1.5
Logicbomb e Timebomb
Una Logicbomb è un programma che compie un’azione predefinita al verificarsi di un
evento sul sistema. Ad esempio: un programma può essere fatto in un modo tale per
cui, nel caso l’amministratore fallisca il login per più di tre volte, inizi a cancellare
a caso bit di dati dal disco. Analogalmente, una Timebomb è un’azione impostata
sul sistema che viene intrapresa ad un’ora prefissata. Logicbomb e Timebomb sono
programmi che non hanno la capacità di replicarsi né di creare un meccanismo di
Capitolo 1.
Malicious software
7
accesso, ma sono applicazioni o parti di applicazioni che causano danni ai dati quando
attivati. Possono essere stand-alone o parte di worm o virus.
1.2
Cenni storici
In questa sezione verrà presentata una breve storia riguardante l’evoluzione dei malware, da quando fu concepito il primo codice maligno fino ad arrivare ai giorni nostri [2].
Gli inizi
Gli storici stanno ancora dibattendo su quando il primo virus da computer sia veramente apparso; quello che sappiamo di certo invece, è che il primo computer (the
Difference Engine), che generalmente si ritiene sia stato inventato da Charles Babbage [27], non era soggetto a nessun virus: perchè non fu mai realizzato.
Molti considerano come punto di partenza, il lavoro di John von Neumann riguardante
gli studi sui “self-reproducing mathematical automata” [26] famosi negli anni ’40.
Nel 1959, il matematico britannico Lionel Penrose presentò il suo punto di vista
sull’auto-replicazione automatizzata in un articolo apparso su Scientific American intitolato “Self-Reproducing Machines” [22]. Diversamente da Neumann, Penrose, descrisse un semplice modello bidimensionale di questa struttura, la quale poteva essere
attivata, moltiplicarsi, mutare e attaccare. Poco dopo la pubblicazione di Penrose,
Frederick G. Stahl riprodusse questo modello in codice macchina sull’IBM 650. Si
noti che questi studi non erano volti a procurare le basi per il futuro sviluppo dei virus,
al contrario questi scienziati si stavano sforzando per migliorare questo mondo e renderlo più adatto alla vita umana. Infatti questi lavori misero le basi per gli studi futuri
riguardanti l’intelligenza artificiale e la robotica.
Nel 1962, un gruppo di ingegneri dei Laboratori Bell, V. Vyssotsky, G. McIlroy e
Robert Morris, crearono un gioco chiamato ’Darwin’. Il gioco consisteva di un cosiddetto arbitro nella memoria del computer che determinava le regole e gli ordini di
battaglia fra i programmi in competizione creati dai giocatori. Il programma pote-
Capitolo 1.
Malicious software
8
va tracciare e distruggere i programmi opponenti e soprattutto replicarsi. Lo scopo
del gioco era quello di cancellare i programmi concorrenti e acquisire il controllo del
campo di battaglia.
Anni ’70
Nei primi anni ’70, il virus Creeper [3] venne rilevato su ARPANET [25], una rete di
computer del Dipartimento della Difesa statunitense che fu il precursore dell’odierna
Internet. Scritto per l’allora popolare sistema operativo Tenex, questo programma era
in grado di guadagnare un accesso indipendente attraverso un modem e copiare se
stesso nel sistema remoto che, una volta infettato, mostrava il messaggio “I’M THE
CREEPER: CATCH ME IF YOU CAN.”
Poco dopo, il programma Reaper fu creato per cancellare Creeper. Reaper era un virus
innoquo: esso si espandeva sulle macchine in rete e se trovava Creeper, lo cancellava.
Anni ’80
Quando i computer divennero popolari, sempre più persone iniziarono a scrivere programmi. Le conquiste nel campo delle telecomunicazioni fornirono comodi canali per
condividere programmi attraverso dei server pubblici come le BBS (Bulletin Board
System). I primi Trojan comparvero in grandi quantità. A partire dal 1981, l’utilizzo
su larga scala di Apple II predeterminò il fato di queste macchine attirando l’attenzione
dei creatori di virus.
Elk Cloner [4] si sparse infettando il sistema operativo di Apple II, attraverso i floppy
disk. Quando il computer veniva avviato usando un floppy infetto, una copia del virus
andava in esecuzione automaticamente. Quando un floppy non infetto veniva usato, il
virus vi si copiava sopra, infettandolo. Il payload1 di Elk Cloner includeva immagini,
testi lampeggianti e messaggi scherzosi.
1 Il
payload è l’attività compiuta da un virus o worm
Capitolo 1.
Malicious software
9
Nel 1986 avvenne la prima epidemia globale per i prodotti IBM. Brain [5], era un
virus che infettava il boot sector. La totale assenza di consapevolezza da parte della
comunità informatica su come proteggere le macchine dai virus, assicurò a Brain un
enorme successo.
Il virus Brain fu scritto da un giovane programmatore pakistano di 19 anni che rispondeva al nome di Basit Farooq Alvi, e da suo fratello Amjad. I due inclusero una stringa
di testo contenente i loro nomi, indirizzi e numeri di telefono. Il comportamento del
c
virus era quello di infettare il boot sector cambiando il nome del disco in “Brain”
e
null’altro.
Il virus Vienna apparve nel 1987: a oggi ancora si hanno dei dubbi su chi sia il vero
autore del virus. Uno dei possibili autori, Rolf Burger, inoltrò una copia del virus a
Bernt Fix, il quale fu in grado di neutralizzarlo; questa fu la prima occasione dove
qualcuno fu in grado di neutralizzare un virus, cosı̀ Fix divenne il precursore degli
odierni anti-virus.
Molti altri computer virus comparirono in quell’anno:
• il famoso Lehigh, chiamato cosı̀ in onore dell’università della Pennsylvania dove
venne scoperto per la prima volta;
• tutta la famiglia di virus Suriv [6];
• una serie di virus per il settore di boot in vari paesi;
• il primo virus cifrato: Cascade [7].
Lehigh [8] fece storia come il primo virus che causava dei danni ai dati: distruggeva
le informazioni sui dischi. Fortunatamente, c’erano un buon numero di esperti alla Lehigh University cosicchè riuscirono a circoscrivere l’infezione e il virus non uscı̀
mai dall’università. Dopo questo evento, gli utenti cominciarono a prendere la sicurezza molto seriamente imparando a proteggere loro stessi dai virus.
Il primo membro della famiglia Suriv(provate a leggerla al contrario) era Suriv-1. Esso
era in grado di infettare i file COM in real-time; per fare ciò il virus caricava se stesso
in memoria e rimaneva attivo fin quando il computer non veniva spento. Questo gli
permetteva di intercettare le operazioni sui file in modo da infettare i file COM nel
Capitolo 1.
Malicious software
10
momento in cui venivano caricati.
Suriv-2 al contrario del suo predecessore aveva come obiettivo i file EXE, e sotto
questo punto di vista fu il primo virus a farlo.
La terza incarnazione, Suriv-3, combinava le caratteristiche dei primi due in modo da
infettare entrambe le tipologie di file.
La quarta modifica del virus, chiamato Jerusalem, era in grado di propagarsi su larga
scala, e questo scatenò l’epidemia del 1988.
L’ultimo evento significativo avvenuto nel ’87 fu la comparsa del primo virus cifrato:
Cascade. Una volta attivato, i simboli sul monitor cadevano sul fondo dello schermo.
Il virus era composto da due parti: il corpo del virus e una routine per la cifratura e la
Figura 1.1: Cascade in esecuzione
decifratura. Il virus venne cifrato in modo da apparire differente in ogni file infettato;
dopo aver caricato il file, il controllo viene passato alla routine di decifratura che decodifica il corpo del virus e gli trasferisce il controllo.
Suriv-3 chiamato anche Jerusalem, causò la peggior epidemia dell’88; fu individuato
in molte imprese, uffici governativi e istituti accademici il giorno venerdı̀ 13 maggio.
Capitolo 1.
Malicious software
11
Il virus attaccò tutto il mondo ma soprattutto gli USA, l’Europa e il medio oriente; il
virus distrusse tutti i file presenti sul disco delle macchine infettate.
La diffusione di virus come Jerusalem, Cascade, Stoned and Vienna venne facilitata
anche da fattori umani: primo fra tutti l’ignoranza riguardante la necessità di proteggersi dai virus con dei programmi anti-virus; secondo, molti utenti e professionisti
non credevano nell’esistenza dei virus da computer. Per esempio, persino Peter Norton, il cui nome ai giorni nostri è sinonimo di sicurezza, era scettico riguardo i virus in
una fase della sua carriera: diceva che la loro esistenza era mistica, paragonabile alle
storie sull’esistenza di coccodrilli di grandi dimensioni che popolavano le fognature di
New York.
Nel 1988 venne rilevato anche il primo hoax2 su larga scala. Questo fu un fenomeno
molto interessante perché rispecchiava il grande sentimento di pericolo verso i nuovi
virus. Un altro tipo di hoax fu rilasciato da Robert Morris circa una diffusione di un
virus sulla rete e il relativo cambiamento di porte e configurazioni. In accordo con il
warning, il virus allegato infettò 300.000 computer nel Dakota in meno di 12 minuti. Il
virus infettò oltre 600 sistemi negli USA (compreso il centro ricerche della NASA), e
provocò quasi un blocco totale; come il worm Christmas Tree, il virus spedı̀ un numero
illimitato di copie di se stesso che sovraccaricarono le reti.
Nel 1989 comparı̀ un nuovo virus: Datacrime [17].
Datacrime era davvero pericoloso: dal 13 ottobre al 31 dicembre, iniziò una formattazione a basso livello che distrusse tutti i dati contenuti nei dischi formattati con il
FAT. Negli USA questo virus venne soprannominato Columbus Day perché si pensava che il suo creatore fosse un terrorista Norvegese che voleva punire gli USA per
aver accreditato a Cristoforo Colombo e non a Eric il Rosso la scoperta dell’America. Un’altro importante fatto accadde: 20.000 dischi contenenti un Trojan nominato
AIDS furono spediti a degli indirizzi in Europa, Africa, Australia, UK e Scandinavia.
Gli indirizzi vennero rubati dal database di PC Business World.
Una volta che il disco veniva caricato, il programma si auto installava sul sistema cre2 Vengono
cosı̀ definiti i finti virus che hanno come unico scopo quello di spaventare la gente
Capitolo 1.
Malicious software
12
ando file e directory nascosti e modificando i file di sistema; dopo 90 volte che veniva
caricato, il sistema operativo codificava il nome di tutti i file, rendendoli tutti invisibili
e lasciando solo un file accessibile. Questo file raccomandava di versare una somma di
denaro su un conto bancario specificato, quindi fu relativamente semplice individuare
il creatore del Trojan, tale Joseph Popp, che fu presto dichiarato insano di mente.
Anni ’90
Il 1990 fu un anno di grande sviluppo per i virus, infatti apparve il primo virus polimorfo: Chameleon [9], il quale fu un evoluzione dei due ben noti Vienna e Cascade. Il suo
autore, Mark Washburn, usò il libro scritto da Burger su Vienna e poi aggiunse alcune
peculiarità prese da Cascade. Diversamente da quest’ultimo, Chameleon non era solo
cifrato ma il suo codice mutava dopo ogni infezione; questa particolarità rendeva gli
allora attuali anti-virus pressoché inutili. Chameleon quindi diede una spinta considerevole allo sviluppo di nuovi tipi di anti-virus, cosı̀ poco dopo venne inventato uno
speciale algoritmo atto a identificare i virus polimorfi.
Nella seconda metá del 1990 apparirono due virus innovativi: Frodo e Whale. Entrambi usavano un algoritmo incredibilmente complesso per nascondersi nel sistema.
Nel marzo del 1992 avvenne un boom di diffusione del virus Michelangelo [10], che
infettó oltre 5.000.000 di macchine.
Dal 1994 divenne sempre più significativo il problema dei virus sui CD-ROM. Diventato molto popolare in breve tempo, questo supporto di archiviazione di massa diventò
una delle principali vie di diffusione per i virus. Vennero registrati molti incidenti
quando fu scoperto un virus sul master-disc di un produttore di dischi; il risultato fu
che il mercato venne inondato da un grosso numero di dischi infetti.
All’inizio dell’anno apparirono due virus polimorfi molto complessi: Pathogen [11]
della famiglia SMEG; l’anti-virus era preparato a rilevarlo al 100% .
Il 1996 cominciò con due virus interessanti:
Capitolo 1.
Malicious software
13
• Boza, il primo virus per Windows 95;
• Zhenxi, un virus polimorfo scritto da un programmatore russo di nome Denis
Petrovym.
Nel mese di marzo un’epidemia colpı̀ Windows 3.x, causata da Win.Tentacle; questo
virus infettò un rete di un ospedale e alcune altre organizzazioni in Francia.
In luglio invece, apparve Laroux: il primo virus per Microsoft Excel che venne rilevato
in due compagnie petrolifere in Alaska e Sud Africa. Come per i virus scritti per MS
Word, il suo payload era basato su delle macro: mini-programmi scritti in Visual Basic.
Giorni nostri
Nel luglio del 2003, un gruppo noto come Cult of Death Cow [12] produsse una nuova versione del virus BackOrifice (BO2K). Questo accadde in occasione dell’annuale
conferenza Defcon [13], e provocò un flusso di messaggi diretti ai produttori di antivirus provenienti dagli utenti spaventati da questa apparizione. La sua peculiarità era
che una volta installato forniva dei sistemi di amministrazione remota. Tutto ciò venne
classificato dalle compagnie anti-virus come BackdoorTrojan. In novembre venne rilevato un virus tecnologicamente complesso e molto pericoloso chiamato Hybris, creato
da un programmatore brasiliano. L’innovazione principale fu quella di ricorrere all’uso
di siti web per caricare nuovi moduli del virus per infettare i computer; in più Hybris
impiegava una chiave RSA [24] a 128 bit per identificare i moduli scritti dall’autore.
Nel 2003 ci furono due dei più importanti attacchi della storia di Internet. Il worm
Slammer mise le basi per gli attacchi e usò le vulnerabilità in MS SQL Server per
diffondersi. Slammer fu il primo worm privo di file, il quale illustrò le potenzialità
dei flash-worm. Il 25 gennaio, nell’arco di pochi minuti, il worm infettò centinaia di
migliaia di computer attorno al mondo e incrementò il traffico di rete a tal punto che
parecchi segmenti di rete andarono in crash. Slammer attaccava sulle porte 1433 1434
e una volta penetrato nella macchina non si copiava all’interno del disco ma rimaneva
residente in memoria.
Un altro importante evento del 2003 fu la comparsa del worm Sobig. Da ricordare, di
Capitolo 1.
Malicious software
14
questa famiglia di worm, è la versione f3 che fu quella con diffusione maggiore. Il fine
ultimo della famiglia Sobig era di generare attacchi di tipo Denial of Service (DoS)
verso siti scelti in modo arbitrario e quello di usare la rete per fare dello spam. Settembre fu il mese di Swen; mascherandosi come una patch proveniente da Microsoft gestı̀
un’infezione su centinaia di migliaia di computer nel mondo e ancora oggi rimane il
worm più diffuso via e-mail.
1.3
Statistiche di incidenti informatici
Qui di seguito verranno presentate delle statistiche riguardanti la diffusione dei malware in un arco temporale di qualche mese tra il 2004 e il 2005. Lo scopo di tale
prospetto è quello di mostrare come in un periodo di tempo relativamente breve, la
diffusione di codice maligno sia estremamente elevata.
Come si può notare, nel mese di febbraio sono stati documentati 3.185 nuovi esemplari
di programmi pericolosi, quasi il 50% in più rispetto ai 2.236 del mese precedente. I
worm hanno guidato la carica mensile del malware con oltre 1.200 nuovi rilevamenti.
I trojan horse, nonostante un lieve aumento rispetto alle cifre del mese precedente, si
sono fermati al secondo posto con 1.083 casi. Occorre notare come 150 Trojan horse
appena scoperti, quasi il 20% di 778 totali, siano varianti delle famiglie BANKER,
BANCBAN e BANCOS specializzate nel furto di password e informazioni relative ai
conti correnti bancari online.
3 Sobig.f
si diffonde via e-mail come le sue versioni prcedenti
Capitolo 1.
Malicious software
Figura 1.2: La tendenza dei rilevamenti di malware dall’ottobre 2004 al febbraio 2005
15
Capitolo
2
I Rootkit
2.1
Definizione
Un rootkit è un insieme di strumenti1 che permettono ad un attaccante, dopo aver
violato un sistema, di mantenere l’accesso sullo stesso e usarlo per altri scopi.
Mantenere l’accesso sulla macchina comporta l’attuazione di alcune tecniche, come
l’applicazione di patch al sistema o la modifica degli execution path di certi eseguibili.
Tutto ciò mira a violare l’integrità del sistema.
Qui di seguito verranno elencate alcune delle operazioni che può compiere un rootkit:
• Un rootkit può disabilitare l’auditing nel momento in cui un certo utente è
presente sulla macchina.
• Un rootkit può far guadagnare un accesso non autorizzato al sistema, attraverso
l’utilizzo di una backdoor.
• Un rootkit può applicare delle patch al kernel, permettendo di eseguire codice
con dei privilegi particolari.
1 Software
come Trojan,backdoor,ecc
16
Capitolo 2.
2.2
I Rootkit
17
Fasi di un attacco
L’insieme di operazioni che un attaccante convenzionalmente esegue per perpetrare un
attacco possono essere schematizzate dai seguenti punti:
1. Information gathering
2. Exploitation
3. Metastasi
- Consolidation
- Continuation
2.2.1
Information Gathering
La prima fase di un attacco, comprende la determinazione delle caratteristiche riguardanti l’host bersaglio, come la topologia della rete in cui si trova, il tipo di sistema
operativo utilizzato e i servizi messi a disposizione (ad esempio web server, ftp server
ecc.).
Le tecniche seguenti possono essere usate per completare la fase di raccolta informazioni:
Host Detection
Questa tecnica mira a rilevare gli host disponibili. Gli approcci per ottenere questo
tipo di informazioni generalmente sono abbastanza complessi; la loro complessità è
dovuta all’esistenza di firewall e filtri che molto spesso bloccano le semplici richieste
ICMP ECHO REQUEST, e che quindi costringono coloro che vogliono avere delle
informazioni su una specifica rete, ad utilizzare tecniche più sofisticate.
Service Detection
Anche chiamata port scanning, questa operazione serve a rilevare la disponibilità di
un servizio TCP, UDP (come ad esempio HTTP, DNS, NIS o altro). Alcuni servizi
Capitolo 2.
I Rootkit
18
in esecuzione su una macchina sono correlati ad un porta2 , quindi eseguendo l’analisi
delle porte aperte presenti su un host si possono ricavare informazioni riguardo i servizi
che lo stesso mette a disposizione.
Network Topology Detection
Il termine topology in questo contesto riguarda le relazioni che intercorrono tra gli
host in termini di numero di hop3 . Conoscere questo valore, consente di ricostruire la
topologia della rete presa in esame in termini di numero di host, e della distanza che
intercorre tra loro. Due tecniche per acquisire tali informazioni sono: TTL modulation
e record route.
La prima, permette di ottenere informazioni, esaminando il campo Time To Live4
(TTL) dei pacchetti IP: a causa del fatto che il TTL viene decrementato ad ogni hop,
è possibile capire quanta strada percorre prima di arrivare a destinazione; inoltre esso
varia da un sistema operativo a un’altro, quindi è possibile venire a conoscenza anche
del tipo si OS in funzione nella rete. I programmi che utilizzano questa tecnica sono
traceroute in ambiente UNIX e trecert.exe in ambiente Windows.
Il campo Record route del pacchetto IP tiene traccia del percorso di un pacchetto dalla sorgente alla destinazione. Queste informazioni posso essere usate per venire a
conoscenza della topologia della rete in esame; il comando ping è particolarmente
adatto a questo scopo.
Firewalk è una tecnica utilizzata per perpetrare entrambe le tecniche sopra descritte,
applicabili ad host protetti dietro firewall o dispositivi “simili”. Un altro metodo,
sebbene non invasivo, è il classico network sniffing, ma non è applicabile in quegli
scenari dove il traffico proveniente dalla target network non è visibile ad un attaccante
che si trova all’esterno della rete locale.
2 Solitamente
3 distanza
4 Indica
il server web sulla 80, il server ftp sulla 21 ecc.
tra gli host
il tempo di vita di un pacchetto IP in termini di hop
Capitolo 2.
I Rootkit
19
OS Detection
Una comune tecnica per determinare il sistema operativo presente sulla macchina remota è IP stack fingerprinting, che tramite il confronto delle variazioni di comportamento nello stack TCP/IP del sistema operativo. Le ambiguità delle definizioni RFC,
combinate con la complessità intrinseca dell’implementazione di uno stack funzionale,
permettono a molti sistemi operativi di essere identificati da remoto generando pacchetti costruiti ad-hoc, che provocano comportamenti differenti ma ripetibili tra OS
diversi.
Application-Layer Information Gathering
Le applicazioni in esecuzione sugli host presi come obiettivo, spesso, possono venire
manipolate per fornire informazioni.
I dispositivi SNMP (Simple Network Management Protocol5 ) spesso non vengono
configurati in modo sicuro, di conseguenza possono venire interrogati e fornire informazioni riguardanti la disponibilitá della rete. Similmente, i server DNS possono
venire interrogati per ricostruire la lista degli host registrati. Altresı́ i router attivi sulla
rete scelta come bersaglio, possono venire interrogati tramite il protocollo RIP. Queste
informazioni possono essere usate per aiutare la costruzione di una mappa concettuale
riguardante la topologia della rete bersaglio.
2.2.2
Exploitation
La fase di exploitation di un attacco è cronologicamente il punto dal quale un attaccante cerca di penetrare all’interno di un host.
5 SNMP
appartiene alla suite di protocolli internet definita dalla InternetEngineering Task Force. Il
protocollo opera al livello 7 del modello OSI.Esso consente la gestione e la supervisione di apparati
collegati in una rete, rispetto a tutti quegli aspetti che richiedono azioni di tipo amministrativo.
Capitolo 2.
I Rootkit
20
Le informazioni prodotte dall’Information Gathering, vengono utilizzate per venire a
conoscenza se, sulla macchina presa come obiettivo, ci sono dei servizi che presentano
delle vulnerabilità conosciute sfruttabili da remoto. I servizi possono essere insicuri sia
per loro natura (progettazione o implementazione) sia per le loro malconfigurazioni.
I metodi attraverso i quali i servizi possono essere sfruttati variano ampiamente ma
il risultato finale che spesso si manifesta è l’esecuzione di un processo all’interno di
un contesto privilegiato (ad es. l’esecuzione di una shell6 ), oppure la rivelazione di
informazioni critiche per la sicurezza del sistema (ad es. una lista di password cifrare
le quali potrebbero venire decifrate successivamente).
2.2.3
Metastasi
La Metastasi si riferisce al processo attraverso il quale un attaccante propaga l’attacco
attraverso una rete. L’uso del termine Metastasi fu suggerito per la prima volta nel
contesto della Computer Security da William Cheswick e Steve Bellovin, e si riferisce
al processo attraverso il quale un attaccante, dopo aver compromesso un host, attacca
gli altri host connessi al primo utilizzando le proprietà e le risorse rese disponibili dallo
stesso.
La fase di Metastasi di un attacco può essere logicamente separata in due parti:
1. Consolidamento
2. Continuazione
Consolidamento
È di estrema importanza per l’attaccante che la fase di Exploitation non venga scoperta; quindi egli deve rimuovere tutte le prove della sua intrusione sull’host eliminando
6 interprete
dei comandi
Capitolo 2.
I Rootkit
21
le tracce lasciate dai file di log e, se fosse possibile, anche tutte le tracce lasciate dalla
fase di Information Gathering. In base alla tecnica di intrusione usata, la fase di Exploitation potrebbe non aver garantito all’attaccante i privilegi massimi sulla macchina
compromessa (“root” per i sistemi UNIX e “Administrator” per quelli Windows NT),
e se ciò fosse vero egli dovrà tentare di elevare i privilegi fino al massimo livello.
Solitamente viene installata una backdoor che abilita un accesso remoto non autorizzato. Una ’backdoor’ si mette in ascolto su una porta esattamente come farebbe un
qualsiasi servizio o demone di rete e fornisce anche un accesso remoto oppure permette di intraprendere un insieme di azioni specifiche come ad esempio l’upload o il
download di file, l’esecuzione o la terminazione di processi etc.
I rootkit si inseriscono in questa fase dell’attacco.
Continuazione
Una volta compromesso un host residente sulla rete obiettivo, l’attaccante può utilizzare metodi di attacco passivo per aumentare il grado di intrusione nel sistema.
Tradizionalmente viene installato un password sniffer, un tipo di network protocol
monitor che lavora in modalità promisqua, progettato per registrare gli username e le
password associate a quei protocolli di livello applicazione che utilizzano trasmissioni
in chiaro, come ad esempio Telnet, FTP, rlogin, etc.
Un aspetto su cui si fa leva in questa fase dell’attacco è il livello di fiducia presente
all’interno della rete compromessa: per fiducia si intende “la situazione in cui un host
permette che una risorsa locale venga utilizzata da un client senza richiedere l’autenticazione tramite password, quando questa verrebbe normalmente richiesta”; la fase di
Metastasi fa proprio questo, sfrutta l’uso/abuso della fiducia insita nelle relazioni tra gli
host compromessi e altri host che probabilmente presto lo saranno. A prescindere dal
sistema operativo un host è coinvolto in relazioni di fiducia multiple, spesso nelle aree
riguardanti l’autenticazione(come già citato sopra), l’autorizzazione, l’accesso remoto e le risorse condivise; il processo di Exploitation riguardante le trust relationship,
implica il riconoscimento e la percorrenza delle relazioni esistenti presenti sull’host
Capitolo 2.
I Rootkit
22
compromesso in modo da aumentare il grado intrusione all’interno della rete. Molto
frequentemente non vi è la necessità di perpetrare un’altra fase di exploitation verso le
altre macchine presenti se le stesse, in qualche modo, ritengono l’host compromesso
fidato.
2.3
2.3.1
Il sistema Windows
Cenni sull’architettura di Windows
Per comprendere i concetti che verranno presentati [23] nei capitoli successivi, potrebbe
essere utile avere per lo meno un’idea generale sull’architettura del sistema operativo
MS Windows. Di seguito viene, in forma concisa, come è strutturato questo sistema
operativo.
Environment Subsystem e DLL Subsystem
Il ruolo dell’environment subsystem è fornire alcuni dei servizi base del sistema ai
programmi applicativi. Ogni sottosistema fornisce l’accesso a diversi sottoinsiemi di
servizi nativi, ciò significa che le azioni compiute da applicazioni fatte per un sottosistema non possono essere eseguite dalle applicazioni di un sottosistema diverso. Ad
esempio un’applicazione Windows non può usare la funzione POSIX fork.
Le applicazioni utente non possono richiamare direttamente i servizi di sistema di
Windows. Perciò passano attraverso una o più DLL del sottosistema. Queste librerie
esportano le interfacce documentate che i programmi legati a sottosistema possono
richiamare. Ad esempio le DLL del sottosistema Windows (come kernel32.dll, user32.dd ecc.) implementano le funzioni API di Windows.
Quando un’applicazione chiama una funzione in un sottosistema DLL, può accadere
una di queste tre cose:
• La funzione è implementata completamente in user mode dentro il sottosistema
delle DLL. In altre parole, non viene spedito nessun messaggio al processo del-
Capitolo 2.
I Rootkit
23
Figura 2.1: Architettura di Windows
Capitolo 2.
I Rootkit
24
l’environment subystem e non viene chiamato nessun servizio dell’executive. La
funzione7 viene eseguita in user mode e i risultati vengono ritornati al chiamante.
• La funzione necessita di una o più chiamate al Windows executive. Ad esempio
ReadFile e WriteFile richiamano NtReadFile e NtWriteFile che fanno parte dei
servizi di sistema di I/O di Windows.
• La funzione richiede che qualcosa venga svolto all’interno del processo dell’environment subsystem. In questo caso viene fatta una richiesta di tipo client/server
spedendo un messaggio al subsystem per eseguire l’operazione.
Come si può notare dalla figura 2.1 l’architettura di Windows ospita tre sottosistemi: OS/2, Posix, e Windows. Qui i primi due non verranno trattati perché di scarsa
rilevanza rispetto all’argomento portante di questo elaborato.
Windows Subsystem:
il subsystem di Windows è composto dai seguenti compo-
nenti:
• Environment subsystem process fornisce il supporto per:
- la Console testuale di Windows
- la creazione e la cancellazione di processi e thread
- un parte del supporto per i processi della macchina virtuale DOS a 16-bit
- altre svariate funzioni come GetTempFile, DefineDosDevice, ExitWindowsEx, e funzioni di supporto per alcuni linguaggi naturali.
• Kernel-mode device driver(Win32k.sys) contiene:
- il window manager, che controlla la visualizzazione delle finestre; gestisce
l’output a video, raccoglie l’input da tastiera, mouse, a altri dispositivi; e
passa i messaggi degli utenti alle applicazioni.
7 Fanno
parte di questo insieme funzioni come GetCurrentProcess e GetCurrentProcessId
Capitolo 2.
I Rootkit
25
- la GDI (Graphics Device Interface) che è una libreria di funzioni per gestire l’output dei device grafici. Essa include funzioni per il disegno e la
manipolazione grafica di linee, testi e figure.
• Sottosistema delle DLL (come Kernel32.dll, Advapi32.dll, User32.dll e Gdi32.dll)
traduce le funzioni API documentate nelle appropriate e spesse volte non documentate chiamate ai servizi di sistema in kernel-mode Ntoskrnl.exe e Win32k.sys.
• Graphics Device Driver sono driver grafici dipendenti dall’hardware, driver di
stampanti e driver di miniporte video.
Ntdll.dll
Ntdll.dll è un sistema di librerie di supporto principalmente per l’uso del sottosistema
delle DLL, situato in user mode (vedi Figura 2.1); esso contiene due tipi i funzioni:
• il System service dispatch dialoga con il Windows executive system service.
• le funzioni interne di supporto usate dai sottosistemi, sottosistemi di DLL, e altre
immagini native.
Il primo gruppo di funzioni fornisce un’interfaccia (stubstub8 ) ai Windows executive
system service che possono essere chiamati dallo spazio utente. Esistono più di 200 di
queste funzioni nell’executive, come NtCreateFile, NtSetEvent, e cosı̀ via [23].
Per ognuna di queste funzioni, Ntdll contiene un entry point con lo stesso nome. Il
codice all’interno della funzione contiene le istruzioni specifiche per l’architettura che
causano la transizione in kernel-mode per invocare il dispatcher, che dopo aver verificato alcuni parametri, chiama il servizio di sistema in kernel-mode che contiene il
codice all’interno di Ntoskrnl.exe.
8 Stub:generalmente
è una funzione o procedura, che fornisce la corretta interfaccia ma non la cor-
retta implementazione di qualche parte di un programma, tipicamente una funzione o una struttura dati
astratta.
Capitolo 2.
I Rootkit
26
Ntdll inoltre contiene molte funzioni di supporto, come ad esempio l’image loader, l’heap loader, e le funzioni di comunicazione del Windows subsystem process.
Contiene anche il dispatcher delle APC(user-mode Asyncronous Procedure Call) e il
dispatcher delle eccezioni.
Executive
L’executive è il livello soprastante Ntoskrnl.exe. Esso comprende:
• Funzioni che sono esportabili e chiamabili in user-mode. Queste funzioni vengono chiamate system services e sono esportate via Ntdll. La maggiorparte dei
servizi è accessibile attraverso le Windows API, ad eccezione di alcuni servizi
che non sono disponibili attraverso nessun sottosistema di funzioni documentato
(ad es. NtQueryInformationProcess).
• Funzioni per i device driver che vengono chiamate usando DeviceIoControl.
• Funzioni che possono essere chiamate unicamente in kernel-mode che sono
documentate nel Windows DDK9 .
• Funzioni che sono esportabili e chiamabili da kernel-mode ma non sono documentate nel Windows DDK.
• Funzioni che sono definite globali ma non sono esportate. Questo include le
funzioni interne di supporto chiamate all’interno di Ntoskrnl.
• Funzioni che sono interne a un modulo che non sono definite come simboli
globali.
Kernel
In kernel è composto da una serie di funzioni che si trovano in Ntoskrnl.exe che fornisce i meccanismi fondamentali usati dai componenti dell’executive. Il codice del
9 Device
driver Development Kit
Capitolo 2.
I Rootkit
27
kernel è scritto in C, il codice assembly viene usato solo per quelle operazioni che
richiedono un accesso a particolari istruzioni e registri del processore, non comodamente accessibili dal C.
Kernel Objects:
il kernel fornisce delle primitive e dei meccanismi a basso livel-
lo che permettono ai componenti di alto livello dell’executive di fare ciò che devono.
Il kernel separa se stesso dal resto dell’executive implementando i meccanismi del
sistema operativo ed disinteressandosi delle politiche, che vengono lasciate all’executive, eccezione fatta per il thread scheduling e dispatching che al contrario, il kernel
implementa.
System Processes
I seguenti i processi di sistema appaiono in ogni versione di Windows.
• Idle process: un thread per CPU
• System process: rappresentano la maggiorparte dei thread in kernel-mode
• Session manager (Smss.exe)
• Windows subsystem (Csrss.exe)
• Logon process (Winlogon.exe)
• Service control manager (Services.exe) e i processi figli che crea (ad es. Svchost.exe) Local security authentication server (Lsass.exe)
Session manager:
è il primo processo user-mode che il sistema crea. Il Session
manager è responsabile di una certa quantità di passi importanti nell’avvio di Windows, come l’apertura di page file addizionali, la creazione delle variabili d’ambiente
ecc. Inoltre esso lancia i subsystem processes e il processo Winlogon, che crea il resto
dei processi di sistema in user mode.
Dopo aver eseguito queste operazioni iniziali, il thread principale in Smss si mette in
attesa degli handle mandati da Csrss e Winlogon; al contempo Smss attende le richieste
di caricamento dei sottosistemi, eventi di debug e altro.
Capitolo 2.
I Rootkit
28
Winlogon:
gestisce il logon e il logoff degli utenti. Winlogon viene chiamato in
causa da un utente quando viene premuta la combinazione di tasti secure attention sequence (SAS); in Windows questa combinazione è Ctrl+Alt+Delete. Lo scopo di SAS
è proteggere gli utenti dai programmi di password-capture che simulano il processo
di logon, perchè questa combinazione non può essere intercettata da un’applicazione
user-mode.
Una volta inseriti username e password, vengono spediti al local autentication server
process(Lsass.exe).
LSASS:
richiama la parte di software preposta per effettuare i controlli del caso,
come il confronto della password digitata con quelle presenti nel SAM10 . Una volta che
l’autenticazione ha avuto successo, Lsass chiama una funzione nel security reference
monitor per generare un token di accesso contenente il profilo di sicurezza dell’utente.
Questo token viene usato da Winlogon per generare i processi iniziali nella sessione
dell’utente, i quali vengono registrati nella chiave di registro userinit.
Userinit: si occupa dell’inizializzazione dell’environment dell’utente e successivamente crea un processo per mandare in esecuzione l’interprete dei comandi predefinito
di sistema per poi terminare.
Service control manager:
è uno speciale processo di sistema responsabile di avviare,
terminare e interagire con i processi di sistema. I servizi solo semplicemente delle immagini eseguibili che richiamano le funzioni messe a disposizione da Windows per
avviare, rispondere alle richieste sullo stato, mettere in pausa o terminare il processo.
Si noti che ogni processo ha tre nomi in Windows: il nome che si può vede normalmente sul sistema, il nome che si trova sul registro di sistema e il nome visualizzato
dal Service administrative tool.
Una volta chiarita la suddivisione presente nell’architettura di Windows tra servizi
10 La
parte del registro che contiene le definizioni di utenti e gruppi
Capitolo 2.
I Rootkit
29
e funzioni appartenenti al livello kernel oppure al livello user, risulterà più semplice
comprendere le differenze tra i rootkit in kernel mode e quelli in user mode.
2.3.2
User mode VS Kernel mode
I rootkit si possono dividere in due categorie:
• User land rootkit
• Kernel land rootkit
I motivi per i quali si sceglie un tipo piuttosto che l’altro, possono essere i più disparati
a causa della loro diversa natura e conseguentemente a causa del loro diverso approccio.
Un kernel rootkit tenta di violare l’integrità del sistema agendo direttamente sul kernel del sistema operativo, magari rimpiazzando semplicemente le native API11 con
altre scritte appositamente dal creatore che rootkit, e sovrascrivendo i valori presenti
nel Service Descriptor Table (SDT)12 ; contro un sistema Windows non c’è bisogno
di preoccuparsi della persistenza una volta impostata la redirezione del flusso di esecuzione(hook), perché tutte le chiamate successive verranno dirottate verso il codice
maligno. Questo non è il caso di un rootkit a livello utente, infatti l’hook non è globale come per il kernel rootkit e quindi esso deve essere eseguito per ogni processo
capace di rilevare la sua presenza. Inoltre decidere di fare hooking su tutti i processi della macchina significa fare parte del gruppo SYSTEM: questo richiede tecniche
di injection, hooking avanzate e prendere di mira delle API ad un livello davvero basso.
Per comprendere le differenze tra un tipo e l’altro viene presentato un esempio riguardante
uno degli scopi che ogni rootkit tenta di raggiungere: l’invisibiltà nei confronti del sistema.
11 funzioni
12 SDT:
fornite dall’executive
è un tabella contenente l’elenco dei servizi dell’executive implementati in Ntoskrnl.exe
Capitolo 2.
I Rootkit
30
Consideriamo il fatto di non volere che alcune directory compaiano mentre ci si muove
all’interno dell’hard disk usando explorer. Dopo una rapida occhiata a questo programma scopriamo che la sua Import Address Table13 usa le API FindFirstFileA/W e FindNextFileA/W, quindi un rootkit per nascondersi dovrà fare hooking su queste due funzioni; si noti che entrambe fanno riferimento alla native API ntdll.ZwQueryDirectoryFile14 .
Le prime due sono funzioni di alto livello, quindi meno soggette a cambiamenti implementativi rispetto alla terza che al contrario può variare in base alla versione del
sistema operativo.
Alla luce di ciò si può concludere che la differenza sostanziale tra kernel land rootkit e
user land rootkit sta nella portabilità: fare hooking sulle funzioni di alto livello (meno
soggette a cambiamenti), oltre ad essere più semplice, garantisce una portabilità maggiore del rootkit da una versione ad un’altra di Windows; cosa invece non garantita
se la funzione scelta come bersaglio è più di basso livello. Inoltre la differenza tra
le due tipologie di rootikit sta nella tecnica, a causa del fatto operano in due contesti
completamente differenti.
2.3.3
Hooking e hiding in Windows
Per hooking si intende l’attuazione di un meccanismo di controllo e modifica delle
normali attività di un programma. Per hiding si intende l’attuazione di tecniche per
nascondere qualcosa (file, processi, chiavi di registro ecc.) al sistema. Qui di seguito
verranno presentate delle tecniche per fare API hooking [19] e hiding [20] di file,
processi e quant’altro il creatore del rootkit ritenga opportuno allo scopo di assicurare
la massima invisibilità.
13 IAT:
tabella propria di ogni programma contenente l’elenco delle API necessarie al funzionamento
dello stesso
14 permette
di interrogare il filesystem
Capitolo 2.
I Rootkit
31
Hooking
Lo scopo dell’ API hooking verte a rimpiazzare il codice esistente nella funzione presa
di mira con del codice arbitrario15 . Di seguito verranno presentati due metodi:
• Hooking before running
• Hooking during running
Hooking before running
Questo riguarda la modifica fisica della funzione (.exe
.dll). Ci sono almeno due possibilità per farlo:
1. La prima consiste nel trovare l’entry point della funzione e riscriverne il contenuto. Questo è limitato dalla dimensione della funzione, comunque si possono
caricare dinamicamente alcuni altri moduli (API load library) per completare il
lavoro. Le funzioni del kernel (kernel32.dll) possono essere usate in tutti i casi
perché in windows ogni processo ha la sua copia personale di questo modulo. Se
si conosce su che sistema operativo verranno cambiati i moduli, si possono usare
i puntatori diretti; questo perché l’indirizzo del modulo del kernel in memoria è
sempre lo stesso in una versione di Windows. Inoltre si può utilizzare il comportamento dei moduli caricati dinamicamente: in questo caso la loro inizializzazione viene eseguita subito il caricamento in memoria. La seconda possibilità
sta nell’estensione del modulo. Qui vengono rimpiazzati i primi 5 byte con una
relative jump. Come risultato, la redirezione del flusso di esecuzione dal codice
originale al codice maligno. Quando si chiama una funzione la cui IAT è stata
cambiata, il codice maligno verrà eseguito immediatamente.
2. Il secondo metodo consiste nel rimpiazzare l’intero modulo: questo significa
che bisogna creare una nuova versione del modulo in grado di caricare quella
pre-esistente e chiamare le funzioni non interessanti; invece quelle utili per raggiugere il fine prefissato vanno riscritte. Questo metodo non va bene per moduli
15 Qui
inteso come codice esterno alla funzione
Capitolo 2.
I Rootkit
32
molto grandi che contengono un gran numero di export, per diventerebbe troppo
oneroso.
Hooking during running
La tecnica presentata precedentemente, permette di rimpiaz-
zare le funzioni in tutti i processi che vengono lanciati dopo l’hooking, ma purtroppo
la sua applicazione risulta estremamente difficile a causa del fatto che bisogna reimplementare il modulo da sostituire in maniera a dir poco perfetta; senza contare il
fatto che il kernel di Windows tenta di proteggere i moduli, specialmente se sono importanti come kernel32.dll e ntdll.dll. Un’altra tecnica che si può utilizzare per fare
hooking, è quindi hooking during running.
L’hooking during running, può essere usato solo nei casi in cui abbiamo i permessi di
scrittura sulla memoria del processo; per fare ciò si usa la funzione API WriteProcessMemory.
Di seguito vengono presentati tre tecniche di hooking during running:
1. hooking usando IAT: Questa tecnica permette di fare hooking andando a riscrivere IAT. Questa tabella mostra la struttura di un file PE.
MS DOS Header (”MZ“) and stub
PE signature (”PE“)
.text
Program Code
.data
Initialzed Data
.idata
Import Table
.edata
Export Table
Debug symbols
La parte interessante ai fini di fare hooking, è il campo .idata. Questa parte
contiene la descrizione degli import e soprattutto gli indirizzi delle funzioni importate; ora farò una piccola digressione su come viene creato un file PE.
Capitolo 2.
I Rootkit
33
Se durante la programmazione di un’applicazione viene usata una API, il linker
non collega direttamente la chiamata al modulo, ma la collega alla IAT con una
istruzione jump che sarà immessa dal loader mentre il sistema operativo carica il
processo in memoria. Questo spiega perché si può usare lo stesso binario su due
versioni di Windows differenti, nelle quali, i moduli vengono caricati in memoria con indirizzi differenti. Il loader estrarrà le funzioni di jump dirette, presenti
nella IAT, che vegono usate dalle chiamate nel nostro codice sorgente; quindi se
siamo in grado di trovare nella IAT la funzione specifica su cui vogliamo fare
hooking, possiamo modificare facilmente l’istruzione di jump e redirezionare il
flusso di esecuzione verso il nostro codice. Ogni chiamata dopo aver fatto ciò
eseguirà il nostro codice.
Il vantaggio di questo metodo è la sua perfezione, lo svantaggio è la grossa
quantità di funzioni su cui farlo.
2. hooking usando la riscrittura degli entry point: Il metodo di sovrascrivere le
prime istruzioni sull’entry point di una funzione è molto semplice. Supponiamo
di voler fare hooking sui primi 5 byte della funzione data: l’inizio della funzione viene preso usando GetProcAddress, da questo indirizzo verrà inserita una
relative jump che punta il nostro codice.
3. altri metodi di hooking: ora verranno presentati due metodi che fanno uso di
CreateRemoteThread. Di seguito la definizione funzione:
HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTFREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlag
LPDWORD lpThreadId
);
Capitolo 2.
I Rootkit
34
L’handle hProcess può essere ottenuto con OpenProcess; il puntatore lpStartAddress
punta ad una locazione di memoria nel processo target dove si trova la prima
istruzione del nuovo thread. Poiché il nuovo thread viene creato nel processo in
questione, esso risiede nel suo spazio di indirizzamento. I due metodi sono:
• (a) DLL injection: Prendiamo come assunto di essere in grado di scrivere
in qualsiasi locazione all’interno dello spazio di memoria del processo in
questione. Questo non risulta molto utile finché abbiamo il nostro codice
all’interno del processo, ma noi useremo questa facoltà a nostro vantaggio.
Useremo GetProcAddress per avere l’indirizzo corrente per LoadLibrary;
poi faremo puntare lpStartAddress all’indirizzo di LoadLibrary che prende
un solo parametro come la funzione per il nuovo thread nel processo target:
HISTANCE LoadLibrary(
LPCSTR lpLibFileName
);
Useremo questa somiglianza e assegneremo il nome della nostra DLL a
lpParameter. Dopo aver eseguito il nuovo thread lpParameter si troverà al
posto di lpLibFileName. Ora viene la parte interessante: dopo aver caricato il nuovo modulo nella memoria del processo target, la parte di inizializzazione è completata. Dopo l’inizializzazione, il thread non dovrà fare
nient’altro e terminerà, ma il nostro modulo si trova ancora in memoria.
• (b) Codice Indipendente: Innanzi tutto dobbiamo inserire il nostro codice
all’interno del processo; poi la funzione CreateRemoteThread ci permetterà di eseguirlo. Quindi prima di ogni altra cosa dobbiamo estrapolare
alcune informazioni sul processo target e acquisire l’handle con OpenProcess; poi VirtualAllocEx allocherà un po’ di spazio nella memoria del processo; infine WriteProcessMemory scriverà il nostro codice nella memoria
allocata che verrà eseguito successivamente.
In CreateRemoteThread lpStartAddress riferirà alla memoria allocata e
lpParameter potrà essere ciò che vogliamo.
Capitolo 2.
I Rootkit
35
Hiding
In questa sezione parlerò delle tecniche di hiding di file, processi, servizi etc. su Windows. Questi metodi sono basati sulle tecniche di hooking presentate precedentemente
File
Ci sono molte possibilità per nascondere la presenza di alcuni file al sistema operativo.
Ogni tecnica presentata implica il cambiamento delle API e verranno tralasciate quelle
che vanno ad intaccare il filesystem.
A. NtQueryDirectoryFile
La ricerca di un file in qualche directory in Windows consiste nella ricerca di tutti i
suoi file all’interno della stessa e in tutte le sue sotto directory. Per enumerare i file
viene usata a funzione NtQueryDirectoryFile.
NTSTATUS NtQueryDirectoryFile (
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG FileInformationLength,
IN FILE_INFORMATION_CLASS FileInfomationClass,
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING Filename OPTIONAL,
IN BOOLEAN RestartScan
);
I parametri importanti sono FileHandle, FileInformation e FileInformationClass.
FileHandle è un handle dell’oggetto directory che può essere ottenuto con NtOpenFile;
FileInformation è un puntatore ad una locazione di memoria dove la funzione scrive i
dati richiesti; FileInformationClass determina il tipo di file scritto in FileInformation.
FileInformationClass è un tipo enumerativo, ma quello di cui necessitiamo sono solo
Capitolo 2.
I Rootkit
36
4 valori i quali vengono usati per enumerare il contenuto delle directory:
#define FileDirectoryInformation 1;
#define FileFullDirectoryInformation 2;
#define FileBothDirectoryInformation 3;
#define FileNamesInformation 12;
La funzione sopra citata scrive una lista di queste strutture in FileInformation. Solo
3 variabili di ogni struttura sono importanti per il nostro scopo. NextEntryOffset è
la lunghezza di una particolare lista di oggetti. Il primo oggetto si trova all’indirizzo
FileInformation + 0, cosı̀ il secondo si trova in FileInformation + NextEntryOffset;
l’ultimo oggetto ha l’EntryOffset settato a 0. Gli altri due campi di interesse sono
FileName e FileNameLength.
Se vogliamo nascondere un file, dobbiamo chiamare separatamente questi quattro tipi,
e per ogni valore ritornato dobbiamo confrontarlo con quello che vogliamo nascondere.
Ad esempio se vogliamo nascondere il primo record, dobbiamo spostare le strutture
seguenti della lunghezza del primo record in modo da saltarlo; se vogliamo nascondere
un altro record, possiamo cambiare il valore di NextEntryOffset del record precedente;
il nuovo valore dell’NextEntryOffset sarà zero se il file da nascondere sarà l’ultimo.
B. NtVdmControl
Se si vuole che alcuni file siano nascosti dalla visualizzazione tramite il prompt di DOS
di deve agire su NtVdmControl.
NTSTATUS NtVdmControl(
IN ULONG ControlCode,
IN PVOID ControlData
);
ControlCode specifica una sotto funzione la quale viene applicata ai dati nel buffer
ControlData. Se ControlCode è uguale a VdmDirectoryFile, NtVdmControl fa la stessa cosa di NtQueryDirectoryFile con FileInformationClass settato su FileBothDirectoryInformation.
#define VdmDirectoryFile 6
ControlData viene usato come FileInformation, la sola differenza qui è che non conos-
Capitolo 2.
I Rootkit
37
ciamo la lunghezza del buffer, e quindi si dovrà contare manualmente. Fatto ciò
dobbiamo aggiungere il NextEntryOffset a tutti i record,FileNameLenght all’ultimo
e 0x5E come lunghezza dell’ultimo record escluso il nome del file. I metodi di hiding
rimangono gli stessi descritti sopra.
Processi
Svariate informazioni di sistema possono essere ottenute usando NtQuerySystemInformation.
NTSTATUS NrQuerySystemInformation (
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
SystemInformationClass specifica il tipo di informazioni che si vogliono acquisire,
SystemInformation è un puntatore al buffer di output della funzione, SystemInformatioLength è la lunghezza del buffer e ReturnLength è il numero di byte scritti. Per
l’enumerazione dei processi in esecuzione useremo SystemInformationClass settato
su SystemProcessAndThreadsInformation.
#define SystemInformationClass 5
La struttura ritornata nel buffer di SystemInformation è:
typedef structure _SYSTEM_PROCESSES {
ULONG NextEntryDelta;
.
.
.
}SYSTEM_PROCESSES, *PSYSTEM_PROCESSES;
Capitolo 2.
I Rootkit
38
Nascondere processi è molto simile a nascondere file. Dobbiamo cambiare il valore di
NextEntryDelta del record precedente a quello che vogliamo nascondere. Solitamente
non si nasconde mai il primo elemento perché è il processo Idle.
Registro
Il registro di Windows è una struttura ad albero abbastanza grande contenente due tipi
di record, per noi molto importanti dato che saranno quelli da nascondere. Il primo
tipo sono le chiavi di registro e il secondo sono i valori.
A.NtEnumerateKey
A causa della sua struttura saremo in grado di listare tutte le chiavi presenti in una
regione del registro. Saremo in grado solo di raccogliere informazioni riguardanti una
chiave specifica grazie alla funzione NtEnumerateKey.
NTSTATUS NtEnumerateKey (
IN HANDLE KeyHandle,
IN ULONG Index,
IN KEY_INFORMATION_CLASS KeyInformationClass,
OUT PVOID
KeyInformation,
IN ULONG KeyInformationLength,
OUT ULONG ResultLength
);
KeyHandle è l’handle di una chiave della quale vogliamo informazioni riguardo una
sottochiave specificata nell’Index. Il tipo delle informazioni ritornate viene specificato
da KeyInformationClass. I dati vengono scritti nel buffer di KeyInformation, la cui
lunghezza è data da KeyInformationLength. Il numero di byte scritti è ritornato da
ResultLength.
La cosa più importante da tenere a mente è che se nascondiamo una chiave, l’indice
di di tutte le chiavi successive verrà spostato, e poiché siamo in grado di ottenere
informazioni riguardo la chiave con l’indice più alto e quella con l’indice più basso,
saremo sempre in grado di contare quanti record erano precedentemente nascosti e
ritornare quello giusto.
Capitolo 2.
I Rootkit
39
NtEnumerateValueKey
I valori del registro non sono in ordine alfabetico; fortunatamente il numero di valori
all’interno di una chiave è abbastanza piccolo, cosı̀ possiamo richiamare il metodo per
lo shift presentato in precedenza. L’API per ottenere informazioni riguardo il valore è
NtEnumerateValueKey.
NTSTATUS NtEnumerateValueKey (
IN HANDLE KeyHandle,
IN ULONG Index,
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
OUT PVOID KeyValueInformation,
IN ULONG KeyValueInformationLength,
OUT PULONG ResultLength
);
KeyHandle è ancora un handle di una chiave ordinata, Index è un indice della lista dei
valori di una chiave data, KeyValueInformationClass descrive un tipo di informazione
che verrà immagazzinato nel buffer KeyValueInformation la cui lunghezza è di KeyValueInfirmationLength byte; il numero di byte ritornati si trova in ResutLength.
Anche questa volta dobbiamo contare lo shift ma in accordo con il numero di valori
in una chiave possiamo richiamare questa funzione per tutti gli indici: da 0 al valore
di Index. Il nome del valore può essere preso quando KeyValueInformationClass è
settato su KeyValueBasicInformation: di questa struttura ci interessano i campi Name
e NameLength.
System service e driver
I servizi di sistema e i driver vengono enumerati da quattro funzioni API indipendenti;
a causa del fatto che le connessioni di queste 4 funzioni variano ad ogni versione di
Windows, vi è la necessità di fare hooking indistintamente su tutte.
I nomi di queste funzioni sono:
• BOOL EnumServicesStatusA(. . . );
• BOOL EnumServiceGroupW(. . . );
Capitolo 2.
I Rootkit
40
• BOOL EnumServicesStatusExA(. . . );
• BOOL EnumServicesStatusExW(. . . );
I parametri più importanti (per noi) in queste funzioni sono: lpServices che punta
al buffer dove è salvata la lista dei servizi, e anche lpServicesReturned che punta al
numero di record risultanti. La struttura dei dati nel buffer di output dipende da tipo
di funzione usata; di queste strutture, il campo su cui focalizzeremo l’attenzione è
lpServiceName che contiene il nome del servizio. I record hanno una dimensione
statica, quindi se vogliamo nasconderne uno sposteremo tutti i record successivi dalla
sua dimensione in poi.
2.3.4
Un esempio di win32 user land rootkit: NTIllusion
NTIllusion [14] è stato progettato per poter essere eseguito con i più bassi privilegi
che un utente può avere, quindi non usa nessun tipo di privilegio di amministratore
per rimanere invisibile poiché risiede unicamente nei processi di proprietà dell’utente
corrente. In una parola tutti i programmi appartenenti al ”ring 3“ che l’utente può
usare per enumerare file, processi, chiavi di registro e porte, sono tenuti sotto stretto
controllo; al contempo il rootkit si mette silenziosamente in attesa di password.
Tutto ciò viene fatto in due passi:
1. Iniettando il codice del rootkit all’interno di ogni applicazione appartenente
all’utente corrente;
2. Rimpiazzando delle funzioni strategiche.
Alla luce di quanto detto riguardo le tecniche di hooking e hiding nelle sezioni precedenti, andrò ad illustrare il funzionamento del rootkit preso in considerazione.
Impostare un hook globale per prendere il controllo dello spazio utente
Per essere efficiente, il rootkit deve essere eseguito sotto tutte le applicazioni visibili
che possono rilevare la sua presenza. Per raggiungere questo scopo, la strada seguita
è stata quella di impostare un un hook esteso sul sistema usando SetWindowsHookEx
Capitolo 2.
I Rootkit
41
per eventi del tipo WH CBT; quindi la dll del rootkit verrà iniettata all’interno di tutti i
processi con interfaccia grafica nel momento in cui appariranno sul schermo. Tuttavia
WH CBT concerne solo i programmi che fanno uso di user32.dll, quindi i programmi
da console non ne saranno affetti: questo è il caso di cmd, netstat, etc. Per ovviare
a questo inconveniente il rootkit deve influire sui processi in modo da venire iniettato
quando la creazione di un processo sta per essere ultimata; questo obiettivo viene raggiunto facendo hooking sulla funzione CreateProcessW all’interno di tutti i processi
iniettati. In questo modo il rootkit sarà eseguito in ogni nuovo processo.
Il rimpiazzo della CreateProcessW e l’hook del sistema sono metodologie tra loro
complementari che permettono di coprire tutte le possibili situazioni: l’esecuzione di
un’applicazione grafica o da console, il taskmanager o qualche altra applicazione.
Per prevenire che un processo venga iniettato due volte, il rootkit modifica
pDosHeader→ csum in modo che sia uguale a NTI SIGNATURE; una volta che viene
caricata la dll, innanzi tutto essa verifica la presenza della sua firma(NTI SIGNATURE)
e nel caso la trovi, termina.
Prendere il controllo delle applicazioni locali
Una volta che il rootkit è in grado di agire ovunque all’interno dello spazio utente,
deve prenderne il controllo e prevenire ad ogni nuovo modulo caricato di sfuggire alla
funzione di hooking. Per fare ciò vengono filtrate le chiamate a LoadLibraryA/W/Ex
in modo da fare hooking su ogni modulo caricato in memoria. La tecnica utilizzata è
quella dell’unconditional jump (vedi 2.2.3).
Rimpiazzo di funzioni
Process hiding
L’obiettivo principale che il rootkit tenta di raggiungere in materia di process hiding è
il taskmanager. Dopo uno sguardo approfondito alla sua Import Table viene alla luce
che esso fa chiamate dirette a ntdll.NtQuerySystemInformation che verrà rimpiazzata da un’altra funzione in modo da nascondere tutti i processi la cui immagine inizia
Capitolo 2.
I Rootkit
42
con RTK PROCESS CHAR che è la signature del rootkit. Un’altra operazione che
il rootkit fa per nascondere i processi è il rimpiazzo della funzione SendMessageW
all’interno del taskmanager per prevenire che il programma invii messaggi ai processi
nascosti.
File hiding
La parte di file hiding verrà illustrata in modo più approfondito a fronte del fatto che il
mio progetto è stato sviluppato esclusivamente con questo scopo: trovare i file nascosti
dai rootkit user-land.
Per nascondere i suoi file, NTIllusion fa hooking sulle funzioni FindFirstFileA/W e
FindNextFileA/W tramite la tecnica DLL injection. Questo basta per compromettere
le visualizzazioni di explorer, del comando dir, e tutti i dialog box forniti da Common
Controls.
In accordo con la documentazione MSDN la funzione FindFirstFile cerca all’interno
di una directory un file o una sottodirectory il cui nome corrisponde con il pattern
specificato.
HANDLE FindFirstFile (
LPCTSTR lpFileName,
LPWIN32_FIND_DATA lpFindFileData
);
La funzione prende due parametri: lpFileName che è una stringa terminata da NULL
che specifica una directory o un percorso e un nome di file, il quale può contenere
delle wildcards (* e ?); un puntatore alla struttura WIN32 FIND DATA che riceve informazioni riguardanti il file o sottodirectory trovato. Se la funzione ha successo, il
valore di ritorno è un handle usato nella successiva chiamata a FindNextFile o FindClose. Se la funzione fallisce, il valore di ritorno è INVALID HANDLE VALUE.
Ora diamo un’occhiata alla struttura di WIN32 FIND DATA:
Capitolo 2.
I Rootkit
43
typedef struct _WIN32_FIND_DATA {
.
.
.
TCHAR cFileName[MAX_PATH];
.
.
}WIN32_FIND_DATA, *PWIN32_FIND_DATA;
Il membro importante in questa struttura è cFileName, che è una stringa terminata da
null che specifica il nome del file.
Il rootkit per garantire l’invisibilità a certi file rimpiazza le funzioni illustrate precedentemente con altre, le quali oltre a fornire il listing, nascondono certi file nel caso vi
incappino.
Di seguito il codice delle funzioni di rimpiazzo:
HANDLE WINAPI MyFindFirstFileA(
LPCTSTR lpFileName,
LPWIN32_FIND_DATA lpFindFileData)
{
HANDLE hret= (HANDLE)1000;
int go_on=1;
// Get real address using GetProcAddress because the
//function may not have been hijacked at IAT
// level but using GetProcAddress()
if(!fFindFirstFileA) {
fFindFirstFileA =
(FARPROC)
fGetProcAddress(
GetModuleHandle("kernel32.dll"),
Capitolo 2.
I Rootkit
"FindFirstFileA"
);
if(!fFindFirstFileA) return 0;
}
if(!fFindNextFileA) {
fFindNextFileA =
(FARPROC)
fGetProcAddress(
GetModuleHandle("kernel32.dll"),
"FindNextFileA"
);
if(!fFindNextFileA) return 0;
}
if(VERBOSE_DIR_LIST)
OutputString("# FindFirstFileA ’%s’\n",lpFileName);
hret = (HANDLE)
fFindFirstFileA(
lpFileName,
lpFindFileData
);
if(hret==INVALID_HANDLE_VALUE){
if(VERBOSE_ERRORS)
OutputString("[X] INVALID_HANDLE_VALUE\n");
return hret;
}
if(!fFindNextFileA){
44
Capitolo 2.
I Rootkit
if(VERBOSE_ERRORS)
OutputString("[X] !fFindNextFileA\n");
return 0; //if hijack failed
}
// While we get a ’hidden file’, we loop
while(
!_strnicmp(
lpFindFileData->cFileName,
RTK_FILE_CHAR,
strlen(RTK_FILE_CHAR))
&& go_on) {
go_on = fFindNextFileA(hret, lpFindFileData);
if(!_strnicmp(lpFindFileData->cFileName, RTK_FILE_CHAR,
strlen(RTK_FILE_CHAR)) && VERBOSE_STEALTH) {
OutputString(
"[!] NTIllusion made the file ’%s’ invisible.\n",
(lpFindFileData->cFileName));
} else {
if(VERBOSE_DIR_LIST) {
OutputString(
"# FindNextFileA : ’%s’\n",
(lpFindFileData->cFileName)
);
}
}
}
// Oops, no more file ?
if(!go_on) {
45
Capitolo 2.
I Rootkit
46
//memset(lpFindFileData, 0,
//sizeof(LPWIN32_FIND_DATA));
if(VERBOSE_ERRORS)
OutputString("[X] INVALID_HANDLE_VALUE II\n");
return INVALID_HANDLE_VALUE;
}
return hret;
}
----------------------------------------------------BOOL WINAPI MyFindNextFileA(
HANDLE hFindFile,
// handle to search
LPWIN32_FIND_DATA lpFindFileData
// pointer to structure for data on found file
){
BOOL ret;
// Get real address using GetProcAddress
//because the function may
//not have been hijacked at IAT
// level but using GetProcAddress()
if(!fFindNextFileA) {
fFindNextFileA =
(FARPROC)
fGetProcAddress(
GetModuleHandle("kernel32.dll"),
"FindNextFileA");
if(!fFindNextFileA) return 0;
}
Capitolo 2.
I Rootkit
// While we get a file that
//should not be shown,
//we get another :
do {
ret = fFindNextFileA(
hFindFile, lpFindFileData);
if(!_strnicmp(
lpFindFileData->cFileName,
RTK_FILE_CHAR,
strlen(RTK_FILE_CHAR)) && VERBOSE_STEALTH) {
OutputString(
"[!] NTIllusion made the file :
’%s’ invisible.\n",
(char*)(lpFindFileData->cFileName));
} else {
if(VERBOSE_DIR_LIST) {
OutputString("# FindNextFileA : ’%s’\n",
(lpFindFileData->cFileName)
);
}
}
} while(
!_strnicmp(lpFindFileData->cFileName,
RTK_FILE_CHAR,
strlen(RTK_FILE_CHAR)) && ret!=0);
// We’re out of the loop so we may check
//if we broke because of no more file
// If it’s the case, we may clear the
47
Capitolo 2.
I Rootkit
48
//LPWIN32_FIND_DATA structure
// as this : my_memset(lpFindFileData, 0,
//sizeof(LPWIN32_FIND_DATA));
return ret;
}
--------------------------------------------------Si noti che queste due funzioni sono fatte per rimpiazzare la versione ANSI delle funzioni in questione. Esistono le relative funzioni per il rimpiazzo delle funzioni Unicode
che qui non verranno illustrate perché molto simili alle prime due.
Registry
Per questo tipo di rootkit è molto importante nascondere il fatto che viene lanciato.
Per permettere l’invisibilità nel registro, il rootkit rimpiazza l’API RegEnumValueW
all’interno dello spazio di memoria di tutti i processi. Il modo di lavorare della funzione di rimpiazzo è semplice: se trova se stessa durante il listing del contenuto di
una chiave che deve essere nascosta ritorna il valore 1 che si traduce in errore. L’unico problema con questo tipo di implementazione è che il processo chiamante fermerà
la richiesta del listing del contenuto delle chiavi di registro, perciò dovrà nascondere
anche chiavi successive. Partendo dal presupposto che le chiavi sono ordinate alfabeticamente, il rootkit risolve il problema facendo in modo che le sue chiavi inizino con
caratteri ASCII alti, in modo tale da trovarsi per ultime.
Global TCP backdoor password grabber
Una volta che il rootkit è stato iniettato in quasi tutti i processi dell’utente, è possibile
impostare una backdoor TCP globale facendo hijacking su recv e WSARecv, permettendo al rootkit di trasformare ogni applicazione in un’opportuna backdoor. NTIllusion implementa unicamente un password grabber virtualmente in grado di carpire le
password spedite da un client di posta elettronica; al momento i client presi in considerazione sono Outlook e Netscape. NTIllusion fa hijacking sul flusso TCP quando il
Capitolo 2.
I Rootkit
49
mail client dialoga con il server remoto, il che gli permette di intercettare i comandi
USER e PASS che in un futuro verranno usati per la scalata dei privilegi.
Elevazione dei privilegi
La cattura delle password POP3 può permettere la sua espansione sulla macchina locale dacché spesso gli utenti usano la stessa password per account differenti. Infatti
il rootkit effettua il login tentando di impersonare un altro utente sul desktop; questo
viene fatto, ad esempio, quando l’utente seleziona il “ run as user” dal menù contestuale facendo click con il tasto destro del mouse sul file eseguibile. Le API coinvolte in
questi casi vengono redirezionate, ogni login andato a buon fine viene salvato sull’hard
disk per altri utilizzi. Questo obiettivo viene raggiunto rimpiazzando LogonUserA e
CreateProcessWithLogon.
Le informazioni carpite vengono salvate in un file di log all’interno dello spazio dedicato all’utente e viene cifrato usando lo XOR a 1 byte.
Capitolo
3
Static and Dynamic Rootkit Detection
3.1
3.1.1
HFF: Hidden File Finder
Parte di un progetto piú grande
Come già spiegato nel capitolo precedente dove si è parlato dei rootkit e delle tecniche
di hooking e hiding, la peculiarità di questi tipi di malware (detti anche ghostware a
causa del loro comportamento) è quella di nascondere i file, le chiavi di registro, i
processi, eventuali porte aperte o quant’altro il creatore ritenga sia necessario.
Qui di seguito illustrerò l’intero progetto, in modo da poter collocare meglio il lavoro
da me svolto. L’intero progetto si articola in quattro fasi:
1. ricerca di file nascosti sul sistema,
2. ricerca di eventuali altri file non nascosti,
3. monitoraggio dei file nascosti trovati,
4. cancellazione o monitoraggio del rootkit.
Ricerca dei file nascosti
Questa è la fase iniziale del lavoro di ricerca, sulla quale si basano tutte le altre fasi; per
ora mi limiterò a dire che lo scopo da raggiungere qui è la creazione di un file contente
50
Capitolo 3.
Static and Dynamic Rootkit Detection
51
i file sul sistema che vengono identificati come nascosti. Una descrizione più estesa
e dettagliata verrà fatta nella sezione successiva, ovvero quella nella quale andrò ad
illustrare il mio lavoro.
Ricerca di altri file non nascosti
Questa seconda fase, parte dal presupposto che i rootkit non sempre nascondono tutti
i file appartenenti a loro. Nel caso di NTIllusion, ad esempio, i file kNtiLoader.exe,
kinject.exe e kNTIllusion.dll, al contrario di altri non vengono nascosti dal rootkit; ma
è chiaro che comunque vi appartengono quindi sarebbe una grave mancanza non rilevare la loro presenza. Questo rilevamento può venire eseguito facendo una scansione
dell’intero disco (o dischi se ce ne sono di più di uno), e per ogni file confrontarlo con
un database contenente i file statici (per statico intendo i file che non vengono creati nel
momento un cui viene eseguito il rootkit) appartenenti ad un rootkit specifico. Questo
confronto può essere fatto utilizzando un buon algoritmo di digest (MD5 o SHA1):
per ogni file scandito, se ne calcola il digest e subito dopo si cerca l’esistenza della
signature all’interno del database. Come è logico pensare, se la ricerca ha successo
significa che il file in questione appartiene ad un rootkit, quindi la probabilità generale
che la macchina si stata infettata o sia ancora infetta, aumenta.
Chiaramente il successo di questa ricerca è fortemente legato alla quantità di informazioni residenti nel database.
Monitoraggio dei file nascosti
Questa fase di analisi della macchina,che si suppone sia stata compromessa, dipende
fortemente dal risultato della fase 1. Una volta appurato che sul sistema ci sono dei
file nascosti (che posso o meno appartenere ad un rootkit) vi è la necessità di capire
come questi file vengono usati, o per meglio dire, chi accede a questi file. Questo è
il lavoro svolto in questo punto. Per fare ciò, ci si basa sul file prodotto dalla fase 1,
Capitolo 3.
Static and Dynamic Rootkit Detection
52
questo scopo viene raggiunto usando un processo che si mette in ascolto sui file: esso
monitorizza gli accessi ai file in questione: cerca di capire che processi vi accedono e
l’utilizzo che viene fatto di questi file. Il concetto utilizzato in questa fase di analisi è
simile al concetto sul quale si basano i rootkit per nascondersi al sistema, ossia l’API
Hooking. Una volta scoperti i processi che accedono ai file nascosti, si cercherà di
capire se sono unicamente residenti in memoria oppure esistono fisicamente su disco,
ma soprattutto se appartengono ad un rootkit in particolare per essere poi in grado di
aumentare il grado di certezza riguardante la sua presenza sulla macchina. Gli aspetti
riguardanti questa fase implementativi non verranno trattati perché esulano dall’oggetto del mio lavoro di tesi.
Cancellazione o monitoraggio del rootkit
Questa è l’ultima fase, ossia il momento di trarre delle conclusioni e attuare qualche
contro mossa. Il progetto al momento non prevede un protocollo ben definito riguardo
a come procedere una volta appurato che un rootkit è presente sulla macchina, quindi
ora fornirò solo delle possibili soluzioni.
Un’idea che si potrebbe attuare è la cancellazione del rootkit dalla macchina; detto cosı̀ potrebbe sembrare una cosa banale ma a volte le apparenze ingannano: se il
rootkit ha fatto hooking o peggio rimpiazzato delle funzioni, la cancellazione delle
stesse potrebbe portare ad una grave instabilità del sistema quindi per quanto riguarda
ciò si dovrebbe fare uno studio molto approfondito su come neutralizzare il rootkit in
modo tale da non intaccare la stabilità del sistema.
Un altro approccio che si potrebbe tenere, sarebbe quello di monitorare il comportamento del rootkit. Partendo dal presupposto che un rootkit spesse volte fornisce delle
backdoor per permettere all’attaccante di tornare sulla macchina, si potrebbe trasformare la macchina attaccata in una trappola per l’attaccante.
Chiaramente queste sono idee puramente personali, sulle quali non ho fatto uno studio
di fattibilià.
Capitolo 3.
3.1.2
Static and Dynamic Rootkit Detection
53
L’idea
L’idea dalla quale sono partito per la ricerca dei file nascosti fa uso del paradigma
trusted/untrusted. Il concetto è molto semplice: una volta che il rootkit è in funzione
sulla macchina, non si è più in grado di sapere se le interrogazioni fatte per la ricerca
di un file all’interno del sistema siano veritiere o meno. Come nel caso del rootkit
NTIllusion preso da me in esame, una volta entrato in funzione, tutti i programmi
appartenenti al ring 3 di sicurezza che fanno uso delle funzioni FindFirstFileA/W [15]
e FindNextFileA/W [15] (ed esempio explorer.exe) non visualizzano completamente il
contenuto di una specifica directory perché i file di appartenenza del rootkit vengono
omessi. Preso come assunto il fatto della visualizzazione non veritiera, ho pensato di
usarlo come arma contro il rootkit: se il rootkit quando è in funzione nasconde i file
da lui creati, gli stessi file sono perfettamente visibili nel momento in cui il sistema
viene spento e si accede al disco fisso da un altro sistema non compromesso. Quindi la
soluzione alla quale sono arrivato è quella di fare due scansioni di una stessa porzione
di disco, la prima a macchina avviata (e quindi anche il rootkit è in funzione) e la
seconda a macchina spenta tramite live cd. Le discrepanze tra queste due scansioni
probabilmente avrebbero dato come risultato proprio i file nascosti dal rootkit.
Questa scelta non è l’unica possibile, un altro approccio possibile era quello di usare
delle API [23] trusted e delle API untrusted. Questo metodo consiste sempre nella
doppia scansione ma la differenza sostanziale sta nel fatto che entrambe le scansioni
vengono fatte “a caldo”, ovvero a macchina, sistema e relativo rootkit avviati. Questo
approccio, per la sua riuscita, si basa sulla compilazione statica da un lato e non statica
dall’altro delle librerie necessarie per la creazione dei tool di analisi. Ritengo che
possa essere un buon metodo, soprattutto comodo dato il fatto che non c’è la necessità
di riavviare la macchina per effettuare la seconda scansione, ma non gode pienamente
della mia fiducia perché ritengo che vi possa essere (anche se remota) la possibilità che
il rootkit riesca in qualche modo a compromettere la veridicità della seconda scansione.
A fronte di ciò la scelta è caduta sul metodo del livecd.
Come sistema è stato usato BARTPE [16], ossia un tool che permette di creare un cd di
Windows live e fornisce anche la possibilà di aggiungere al prodotto finale altre cose
Capitolo 3.
Static and Dynamic Rootkit Detection
54
a scelta dell’utente; chiaramente nel mio caso si tratta del tool da me sviluppato.
3.1.3
Sviluppo e implementazione
Hidden File Finder è suddiviso in due parti ben distinte ma complementari che corrispondono a due fasi altrettanto distinte dell’analisi: una prima scansione fatta sul
sistema untrusted e una seconda dal sistema trusted che nel qual caso è il livecd. Di
conseguenza Hidden File Finder è composto da due eseguibili:
• bad scan.exe
• smart find.exe
Bad scan.exe
Bad scan ricopre la prima fase dell’analisi: permette di effettuare una scansione untrusted di una parte di una parte del disco. Come si può notare dalla figura 3.1 lo
schema concettuale delle classi per quanto riguarda bad scan.exe comprende:
• Untrusted API
• File List
• Disco
Untrusted API
Questa classe fornisce i metodi che permettono di portare a termine la prima fase del
progetto ossia la scansione di una porzione del disco. Concettualmente i metodi forniti
sono due:
• input: fornisce un prompt che permette all’utente di immettere il path iniziale
dal quale far partire la ricerca.
• bad scan: è il metodo che effettua la scansione della porzione di disco specificata
Capitolo 3.
Static and Dynamic Rootkit Detection
55
File List
Untrusted API
−list :File List
+ read ():
+ input ():
+ write ():
−bad_scan ():
1..*
Disco
Trusted API
−bad :badList
+ read ():
−trust :trustList
+ write ():
trustList
−mism :mismatch
+ read ():
−badListCreate ():
+ write ():
−trustListCreate ():
mismatch
−compare ():
+ list ():
+ read ():
+ mismatchListCreate ():
+ write ():
+ input ():
badList
+ read ():
+ write ():
Figura 3.1: Diagramma delle classi di HFF
Capitolo 3.
Static and Dynamic Rootkit Detection
56
File List
La classe File List fornisce i metodi per la gestione della lista contenente i nomi di file
che viene generata fronte di una scansione di un porzione di disco; nel caso reale File
List è un file di testo. Più nello specifico i metodi forniti sono:
• read: che permette di scorrere il file e leggerne il contenuto
• write: che permette di aggiungere dei nuovi record al file
Disco
La classe Disco rappresenta l’hard disk vero e proprio, il quale verrà scandito da Untrusted API, e il suo contenuto o parte di esso andrà a popolare un oggetto della classe
File List. Concettualmente i metodi forniti sono:
• read: che permettere si leggere il suo contenuto
• write: che permette di aggiungere nuovi elementi ad esso.
Nel caso reale le funzioni che il sistema operativo mette a disposizione per effettuare
la lettura del disco sono: FindFirstFileA/W e FindNextFileA/W.
untrusted API :Untrusted API
disco :Disco
: := read ()
:
: := write ()
Figura 3.2: Diagramma di sequenza di bad scan
bad.txt :File List
Capitolo 3.
Static and Dynamic Rootkit Detection
Funzionamento
57
La figura 3.2 rappresenta il diagramma di sequenza che mette in
luce il funzionamento interno del metodo bad scan. Come si può notare, l’oggetto
Untrusted API esegue una lettura sull’oggetto disco; il risultato ritornato dal metodo
read verrà scritto nell’oggetto bad.txt.
Nel caso reale tutto ciò si traduce nella scansione di una porzione di filesystem utilizzando le funzioni API di Windows FindFirstFileA/W e FindNextFileA/W (le quali si
presume siano state compromesse dal rootkit) e per ogni file scandito se ne ricava il
path assoluto e lo si aggiunge al file bad.txt.
Le scansioni possono essere piú d’una, e per ognuna di esse, un file bad.txt si troverà all’interno del path dal quale la scansione è partita. La scelta di utilizzare un file
come risultato finale di questa operazione nasce dalla necessità di poter dialogare con
la seconda fase dell’analisi che avverrà da livecd; quindi qualsiasi tipo di soluzione
dinamica è inutilizzabile.
Smart find
Smart find ricopre la seconda e ultima fase dell’analisi volta alla rilevazione dei file
nascosti: esso si preoccupa di effettuare una nuova scansione della stessa porzione di
disco già esaminata in precedenza. Una volta fatto ciò, i risultati delle due scansioni
verranno confrontati e le parti discordanti saranno sottoposte ad un’ulteriore analisi.
Una volta svolto anche questo step, il risultato ottenuto verrà passato alla fase successiva di analisi della macchina compromessa. Lo schema concettuale delle classi di
smart find è presentato nella figura 3.1. Le classi coinvolte sono:
• Trusted API
• File List
• Disco
• trustList
• badList
Capitolo 3.
Static and Dynamic Rootkit Detection
58
• mismatch
La descrizione delle classi File List e Disco non verrà nuovamente ripetuta essendo
già state ampiamente illustrate precedentemente.
trustList
La classe trustList fornisce i metodi per la gestione della lista trusted contenente varie
informazioni riguardanti i file scanditi a partire dal path specificato.
Metodi forniti:
• read: permette di scandire la lista e leggerne il contenuto
• write: permette di creare la lista o aggiungere nuovi elementi.
Nel caso reale, l’oggetto di tipo trustList è appunto una lista, che come precedentemente detto conterrà varie informazioni per ogni file scandito. La sua struttura è la
seguente:
typedef struct Trust
{
CString nome;
CString percorso;
CString attr[8];
}Trust;
Il campo nome contiene il nome del file, il campo percorso contiene il path assoluto
del file e il campo attr è un array che contiene gli attributi del file.
badList
La classe badList analogalmente a trustList fornisce due metodi per la gestione dell’oggetto atto a contenere il contenuto del file bad.txt rappresentato nello schema concettuale da un oggetto appartenente alla classe File List.
Metodi forniti:
Capitolo 3.
Static and Dynamic Rootkit Detection
59
• read: consente di scorrere la lista untrusted e leggerne il contenuto
• write: permette di creare o aggiungere nuovi elementi alla lista untrusted
Nel caso reale l’oggetto di tipo badList è una hash table nel quale verrà inserito il
contenuto del file creato da bad scan; la struttura di un nodo è la seguente:
typedef struct nodo
{
CString nome;
vector<CString> *next;
}nodo;
Il campo nome contiene il nome del file e il campo *next è un puntatore alla lista
delle collisioni, che qui viene implementata attraverso il tipo vector, ossia un array
dinamico.
mismatch
La classe mismatch fornisce i metodi per gestire un lista che dovrà con-
tenere le eventuali discrepanze venute alla luce dal confronto tra la lista untrusted e la
lista trusted.
I metodi, come per le prime due sono:
• read: permette di scandire la lista e leggerne il contenuto
• write: permette di creare o aggiungere record alla lista
Nel caso reale l’oggetto di tipo mismatch è una lista contenente appunto le suddette
discrepanze; la struttura del nodo di questa lista è cosi definita:
typedef struct Mism
{
CString nome;
Capitolo 3.
Static and Dynamic Rootkit Detection
60
CString percorso;
CString attributi[8];
double rootkit;
string nomeRootkit;
char digest[41];
}Mism;
I primi tre campi derivano direttamente dal nodo della lista Trust quindi non andrò
a spiegarli nuovamente; il campo rootkit indica un valore espresso in percentuale sul
livello di warning che andrà ad influire sul valore finale relativo al grado di infezione
della macchina.
Per questo campo ho previsto due valori:
• 30: se un file è nascosto ma non si riesce ad associarlo a nessun rootkit,
• 100: nel caso in cui si riesca ad associarlo ad un rootkit, ciò significa che vi è la
certezza che un rootkit sia o sia stato in esecuzione sulla macchina .
Il campo nomeRootkit conterrà il nome del rootkit al quale il file è associato e il campo digest è un array contenente per l’appunto il digest del file trovato calcolato con
l’algoritmo SHA1.
Trusted API
I metodi forniti da questa classe permettono concludere la seconda e ultima fase dell’analisi:
• input: permette di specificare il path dal quale far partire la scansione trusted
che deve corrispondere con il path specificato per bad scan
• trustListCreate: crea una lista trusted di file presenti nel path specificato nel
metodo input;
• badListCreate: importa il file creato da bad scan mappandolo in memoria
Capitolo 3.
Static and Dynamic Rootkit Detection
61
• mismatchListCreate: crea una lista contenente le discrepanze evidenziatesi dal
confronto delle liste trattate precedentemente.
• compare: effettua la comparazione tra la lista trusted e la lista untrusted
• list: permette di vedere il contenuto della lista contenente le discrepanze tra la
lista trusted e la lista untrusted.
Funzionamento
Qui di seguito verrà illustrato il funzionamento dei tre metodi prin-
cipali della classe Trusted API:
La 3.3 mostra il funzionamento del metodo trustListCreate. Lo scopo da raggiungere
trusted API :File List
disc :Disco
trust :trustList
: := read ()
:
: := write ()
Figura 3.3: Diagramma di sequenza di trustListCreate
qui è la creazione di una lista di file considerata trusted, ciò viene fatto scandendo
l’oggetto disc di tipo Disco: a partire dal path dato in input, l’oggetto trusted API effettua una scansione dell’oggetto disc usando il metodo read fornito dalla classe Disco
e successivamente popola la lista trust di tipo trustList.
Nella pratica tutto si traduce nella scansione della stessa porzione di disco già analizzata da bad scan e la conseguente creazione di una lista dei file presenti con alcuni
attributi utili x le fasi di analisi successive.
Capitolo 3.
Static and Dynamic Rootkit Detection
62
La 3.4 mostra il funzionamento di badListCreate: l’oggetto trusted scandisce l’oggetto bad.txt avvalendosi del metodo read e con le informazioni ottenute va a create
l’oggetto bad di tipo badList che al suo interno ospiterà le informazioni presenti in
bad.txt.
Nella pratica, smart find.exe creerà un tabella di hash a partire dal file bad.txt residente
nel path specificato. Come funzione di hash il calcolo della posizione in cui salvare
ogni singolo record è stata utilizzata RSHash implementata da Robert Sedgewick; si è
deciso di usare una funzione già fatta perchè una buona funzione di hash non si può
improvvisare, altrimenti si andrebbe incontro al rischio di avere troppe collisioni durante la fase di inserimento.
La ragione principale nella scelta di questo tipo di struttura dati sta nel tempo necessario per la ricerca di un elemento: infatti nel caso medio, abbiamo un tempo pari a
O(1+α) che nella situazione di avere grandi quantità da trattare si traduce in una notevole velocità di esecuzione
A questo punto tutto è pronto per l’analisi vera e propria, questa fase viene illustrata nel diagramma di sequenza nella figura 3.5. L’oggetto trusted, legge il contenuto
degli oggetti trust e bad. Tramite il metodo compare confronta ciò che ha appena letto
e tutto ciò che non combacia verrà inserito dell’oggetto mismatch. Da un punto di
vista implementativo questa ultima fase si svolge nel modo che ora andrò ad illustrare.
Una volta aver caricato in memoria il risultato della scansione trusted nella lista Trust
e il contenuto del file bad.txt nella hash table Bad, si è pronti per fare il confronto. La
lista Trust viene scandita completamente, e per ogni sua occorrenza, si andrà a ricercare il calore nel campo percorso all’interno di Bad; se tale valore viene trovato, non
succede nulla, ma nel caso non venisse trovata una corrispondenza, il nome del file
con i suoi attributi andrà a popolare la lista Mismatch.
A questo punto, il contenuto della lista Mismatch è considerato sospetto, quindi verrà
sottoposto ad un ulteriore controllo.
A questo scopo ho creato un database composto di due tabelle:
• file: contiene il nome del file, il relativo digest (SHA1) e il nome del rootkit al
quale appartiene.
Capitolo 3.
Static and Dynamic Rootkit Detection
trused API :Trusted API
63
bad.txt :File List
bad :badList
: x := read ()
:
: x := write ()
Figura 3.4: Diagramma di sequenza di badListCreate
trusted API :Trusted API
trust :trustList
bad :badList
mismatch :mismatch
: x := read ()
:
: write ()
:
: x := compare ()
: x := write ()
Figura 3.5: Diagramma di sequenza di mimatchListCreate
Capitolo 3.
Static and Dynamic Rootkit Detection
64
• rootkit: contiene per ogni rootkit la sua eventuale signature; in questo caso
specifico, per signature non si intende il digest, bensı̀ una stringa vera e propria
con la quale il rootkit marca i file che devono rimanere nascosti (nel caso di
NTIllusion è nti messa come prefisso ad ogni nome di file).
La lita Mismatch verrà scandita, e ad ogni occorrenza verrà effettuato un controllo
volto a scoprire se all’interno del file sospetto esiste la signature di qualche rootkit.
Se questo controllo risulta essere positivo, si procederà con l’assegnazione della percentuale di “warning” come già spiegato in precedenza.
Completato anche questo passo, la ricerca e nel caso sia stato rilevato qualcosa di
anomalo, verrà prodotto un file contenente le informazioni rilevate. Questo file è il
punto di partenza per le fasi successive dell’analisi post-mortem della macchina.
3.1.4
Conclusioni e sviluppi futuri
Figura 3.6: Esempio di esecuzione, HFF rileva un file nascosto appartenente a NTIllusion
Alla luce dei test effettuati prendendo come punto di riferimento il rootkit NTIllusion posso affermare che l’approccio usato per la ricerca dei file nascosti si è rivelato
corretto, da come si può notare dalla figura 3.6, HFF riesce a rilevare e identificare il
Capitolo 3.
Static and Dynamic Rootkit Detection
65
file nascosto appartenente a NTIllusion. Purtroppo non ho avuto la possibilià di testare
il tool che ho sviluppato su altri rootkit che rispondessero ai prerequisiti richiesti che
per inciso sono: l’essere un rootkit user-land e fare file hiding tramite API Hooking.
HFF per il momento è in grado di riconoscere formalmente solo un tipo di rootkit a
livello utente, quindi se si vuole aumentare il suo raggio d’azione sarebbe necessario
espandere il suo database interno. Per quanto riguarda il riconoscimento delle parti
fisse di un rootkit , o meglio tutto ciò che non viene creato dinamicamente al momento dell’installazione sulla macchina, sarebbe utile studiare un algoritmo più efficace
del semplice digest del file; è ben noto che basterebbe solo una piccola modifica all’interno del binario perché il digest non sia più valido; quindi un’altra metodologia
che magari non si basi sul solo SHA1 aumenterebbe sicuramente la probabilità di riconoscere release successive di uno stesso rootkit (a meno di una modifica radicale del
sorgente). Per quanto riguarda l’utilizzo da parte dell’utente finale, si potrebbe implementare un’interfaccia grafica per rendere il tutto più immediato e intuitivo.
Allo stato attuale delle cose, la scansione che va a creare la lista Trust è stato implementato usando le librerie MFC per una fatto di praticità; si potrebbe pensare di fare
questo tipo di scansione ad un livello più basso: magari implementando un scansione
in raw del disco, scavalcando la struttura del filesystem.
Un’altra feature molto utile che per ora non è stata implementata, è la possibilià di
salvare i vari file bad.txt e mismatch.txt in un altro device al di fuori del disco da analizzare: magari una memoria USB. Questo perché è buona regola non alterare lo stato
della macchina dopo una compromissione.
Bibliografia
[1] http://www.bo2k.com/.
[2] http://www.viruslist.com.
[3] http://vil.nai.com/vil/content/v 313.htm.
[4] http://en.wikipedia.org/wiki/Elk Cloner.
[5] http://www.f-secure.com/v-descs/brain.shtml.
[6] http://www.viruslist.com/en/viruses/encyclopedia?chapter=153311150.
[7] http://www.f-secure.com/v-descs/cascade.shtml.
[8] http://vil.nai.com/vil/content/v 705.htm.
[9] http://vil.nai.com/vil/content/v 1313.htm.
[10] http://www.cert.org/advisories/CA-1992-02.html.
[11] http://securityresponse.symantec.com/avcenter/venc/data/smeg.pathogen.html.
[12] http://www.cultdeadcow.com.
[13] http://www.defcon.org/.
[14] http://www.phrack.org/show.php?p=62&a=12.
66
BIBLIOGRAFIA
67
[15] http://msdn.microsoft.com/default.aspx.
[16] http://www.nu2.nu/pebuilder/.
[17] J. Burgess. Computer virus sparks a user scare. Washington Post, Sep 17 1989.
[18] F. de la Cuadra. Mydoom.ao: on the prowlb for email addresses. Panda software,
2005.
[19] H. Father. Hooking windows api - technics of hooking api functions on windows.
The CodeBreakers Journal, 2004.
[20] H. Father. Invisibilty on nt boxes - how to become unseen on windows nt. The
CodeBreakers Journal, 2004.
[21] K. Hafner and J. Markoff. Cyberpunk: Outlaws and hackers on the computer
frontier. Simon & Schuster, 1991.
[22] L. S. Penrose. Self-reproducing machines. Scientific American, 1959.
[23] S. Russovic. Windows Internals 4th edition. Microsoft press, 2005.
[24] A. Salomaa. Public-Key Cryptography, 2nd Edition. Springer, 1996.
[25] P. H. Salus. Casting the Net, From Arpanet to Internet and beyond. AddisonWesley.
[26] J. von Neumann and A. W. Burks. Theory of Self-Reproducing Automata. Univ.
of Illinois Press, 1966.
[27] M. R. Williams. A Hitory of Computing Tecnology. IEEE Computer Siciety
Press, 1997.
Scarica