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.