|
|
| << | < | > | >> |IndicePrefazione ix Prefazione all'edizione americana xi Ringraziamenti xiii Capitolo 1 - Introduzione 1 0x1OO 1 Capitolo 2 - Programmazione 7 0x210 Che cosa è la programmazione? 8 0x220 Sfruttamento dei punti vulnerabili del programma 11 0x230 Tecniche generali di exploit 14 0x240 Autorizzazioni di accesso ai file per più utenti 14 0x250 Memoria 16 0x251 Dichiarazione di variabili 16 0x252 Terminazione a byte nullo 17 0x253 Segmentazione della memoria di programma 17 0x260 Buffer overflow 21 codice overflow.c 22 0x270 Overflow basati sullo stack 23 vuln.c code 24 codice exploit.c 25 0x271 Come realizzare degli exploit senza codice di exploit 27 0x272 Come utilizzare l'ambiente 30 vuln2.c code 30 env_exploit.c code 31 getenvaddr.c code 37 0x280 Overflow basati su heap e bss 39 0x281 Un semplice overflow basato su heap 39 codice heap.c 39 0x282 Overflow di puntatori a funzione 44 bss_ame.c code 44 0x290 Format string 51 0x291 Format string e printf() 51 fmt_example.c code 52 0x292 La vulnerabilità del format string 55 codice fmt_vuln.c 56 0x293 Lettura da indirizzi di memoria arbitrari 57 0x294 Scrittura su indirizzi di memoria arbitrari 59 0x295 Accesso diretto ai parametri 66 0x296 Sovrascrittura di dtors 69 codice dtors_sample.c 69 0x297 Sovrascrittura del GOT (Global Offset Table) 75 0x2a0 Scrittura dello shellcode 78 0x2al Istruzioni del linguaggio assembly comuni 78 0x2a2 Chiamate di sistema Linux 79 0x2a3 Hello, world! 81 hello.asm 82 0x2a4 Codice per generare una shell 83 shell.asm 84 0x2a5 Evitare di utilizzare altri segmenti 86 shellcode.asm 87 0x2a6 Rimozione dei byte null 87 shellcode.asm 88 shellcode.asm 90 0x2a7 Uno shellcode ancora più piccolo basato sull'utilizzo dello stack 92 stackshell.asm 92 tinyshell.asm 94 0x2a8 Istruzioni ASCII stampabili 96 0x2a9 Shellcode polimorfico 97 0x2aa Shellcode polimorfico stampabile ASCII 97 print.asm 101 printable_exploit.c 103 print2.asm 106 shellcode print2 assemblato 108 0x2ab Dissembler 111 only_print.c code 115 cleared_stack.c code 118 0x2b0 Ritorno alla libc 122 0x2bl Ritorno a system() 122 0x2b2 Ritorno alla libc concatenato 124 0x2b3 Utilizzo di un wrapper 125 0x2b4 Scrittura di null con ritorno alla libc 127 0x2b5 Scrittura di più word con un'unica chiamata 129 Capitolo 3 - Il networking 133 0x310 Che cos'è il networkingl 133 0x311 Il modello OSI 133 0x320 Dettagli interessanti su alcuni livelli 135 0x321 Livello di rete 135 0x322 Livello di trasporto 137 0x323 Livello data-link 138 0x330 Ascolto del traffico di rete 140 0x331 Sniffing attivo 142 arpredirect.pl 147 0x340 Dirottamento TCP/IP 149 0x341 Dirottamento RST 150 File: hijack_rst.sh 151 0x350 DoS (Denial of Service) 153 0x351 Il Ping of Death 153 0x352 Teardrop 154 0x353 Ping flood 154 0x354 Attacchi di amplificazione (di traffico) 154 0x355 DoS flooding distribuito 155 0x356 Flooding SYN 155 0x360 Scansione delle porte 156 0x361 Scansione SYN stealth (furtiva) 156 0x362 FIN, X-mas e scansione null 156 0x363 I decoy (esche) 157 0x364 Scansione idle 157 0x365 Difesa proattiva (shroud) 158 Scansione FIN prima della modifica del kernel 159 Scansione FIN dopo la modifica del kernel 159 File: shroud.sh 160 Da macchina overdose @ 192.168.0.193: 161 Lo script shroud.sh in funzione su 192.168.0.189: 161 File: shroud2.sh 163 Su 192.168.0.189: 164 Da macchina overdose @ 192.168.0.193: 165 Capitolo 4 - Criptologia 167 0x410 Teoria dell'informazione 168 0x411 Sicurezza assoluta 168 0x412 Sistemi a chiave non riutilizzabile 168 0x413 Distribuzione quantistica delle chiavi 169 0x414 Sicurezza computazionale 170 0x420 Runtime di un algoritmo 170 0x421 Notazione asintotica 172 0x430 Cifratura simmetrica 172 0x431 Algoritmo di ricerca quantistico di Lov Grover 173 0x440 Crittografia asimmetrica 174 0x441 RSA 174 gcd(7253, 120) 175 0x442 Algoritmo di fattorizzazione quantico di Peter Shor 178 0x450 Cifrari ibridi 179 0x451 Attacchi Man-in-the-Middle 179 Su macchina overdose @ 192.168.0.193 181 Su macchina euclid @ 192.168.0.118 181 0x452 Diverse versioni delle host fingerprint del protocollo SSH 182 Da macchina euclid @ 192.168.0.118 prima dell'attacco MiM 184 Su macchina overdose @ 192.168.0.118 durante la preparazione dell'attacco MiM 184 Da macchina euclid @ 192.168.0.118 dopo l'attacco MiM 184 0x453 Fuzzy Fingerprint 185 Sulla macchina overdose @ 192.168.0.193 189 Connessione normale senza attacco MiM 190 Connessione durante attacco MiM 190 0x460 Cracking delle password 190 File: hash.pl 190 0x461 Attacchi di tipo dizionario 191 File: crack.pl 191 0x462 Attacchi a forza bruta esaustivi 192 0x463 Tabella di ricerca degli hash 194 0x464 Matrice di probabilità delle password 194 File: ppm_gen.c 197 0x470 Cifratura 802.11 b wireless 204 0x471 WEP (Wired Equivalent Privacy) 204 0x472 Cifratura a flusso RC4 206 0x480 Attacchi WEP 207 0x481 Attacchi a forza bruta off-line 207 0x482 Riutilizzo del keystream 208 0x483 Tabelle dizionario di decifrazione basate su IV 209 0x484 Reindirizzamento IP 209 0x485 Attacco di Fluhrer, Mantin e Shamir (FMS) 211 File: fms.c 214 Capitolo 5 - Conclusione 221 Bibliografia e riferimenti 223 Indice analitico 227 |
| << | < | > | >> |Pagina 1Capitolo 1
Introduzione
La parola hacking evoca alcuni stereotipi: vandalismo elettronico, spionaggio industriale e personaggi pittoreschi con capigliature variopinte e anelli nel naso. È molto diffuso il pregiudizio secondo cui l'hacker sarebbe un criminale. Benché vi siano senz'altro individui che utilizzano le tecniche di hacking per scopi illeciti, l'hacking è tutt'altra cosa e tende semmai ad assecondare il rispetto delle leggi anziché la loro violazione. Essenzialmente, l'hacking consiste nell'individuare, in un determinato contesto tecnico, possibilità impreviste o trascurate e nell'utilizzarle in nuovi modi creativi per risolvere un problema, per esempio, per riuscire ad accedere a un sistema informatico eludendone le protezioni o per individuare una soluzione che consenta di controllare un trenino elettrico in miniatura mediante un'apparecchiatura telefonica obsoleta. Di solito, le soluzioni basate su un'hack consentono di risolvere questi problemi in modi originali, inimmaginabili da coloro che adottano una metodologia convenzionale.
Verso la fine degli anni '50, al MIT
(Model Railroad Club),
un gruppo di appassionati di modellini di treno, furono donate apparecchiature
obsolete, per lo più telefoniche. I membri del club le utilizzarono per creare
un sistema complesso che consentiva a più operatori di controllare diverse
tratte ferroviarie del trenino mediante una connessione telefonica all'opportuna
sezione.
0x100 Essi chiamarono hacking questo utilizzo nuovo e originale delle apparecchiature. Secondo l'opinione di molti, costoro furono i capostipiti degli hacker. Si dedicarono quindi alla programmazione su schede perforate e nastri scorrevoli per i primi computer, per esempio l'IBM 704 e il TX-0. Mentre altri si accontentavano di scrivere programmi che risolvessero problemi, questi pionieri erano ossessionati dall'idea di scrivere programmi che lo facessero in modo brillante. Essi ritenevano che un programma che permetteva di ottenere lo stesso risultato con un numero minore di schede perforate fosse migliore, sebbene facesse le stesse cose. La differenza stava nel modo, che potremmo definire elegante, in cui esso consentiva di ottenere i risultati auspicati. [...] Gli hacker si esaltavano scoprendo splendore ed eleganza nella matematica e nell'elettronica, ritenute tradizionalmente discipline molto aride. Essi considerarono la programmazione alla stregua di una forma di espressione artistica, mentre il computer costituiva lo strumento di questa arte. Il loro desiderio di dissezionare e comprendere non intendeva demistificare l'opera d'arte, ma era semplicemente un modo per apprezzarla maggiormente. Questi valori, ispirati alla sete di conoscenza, sarebbero poi divenuti parte integrante della cosiddetta etica degli hacker: l'apprezzamento della logica come forma d'arte e la promozione del flusso libero delle informazioni, al di là dei tradizionali confini e delle restrizioni, al solo fine di comprendere meglio la realtà. In effetti, niente di nuovo sotto il sole; nell'antica Grecia, i pitagorici avevano un'etica e una sottocultura simili, malgrado i computer non esistessero ancora. Essi colsero la bellezza insita nel ragionamento matematico e scoprirono molti concetti fondamentali della geometria. Questa sete di conoscenza e i suoi benefici sottoprodotti sarebbero stati tramandati nel corso della storia, dai pitagorici fino a Ada Lovelace, ad Alan Turing e agli hacker del MIT. L'informatica era destinata a progredire, sino a Richard Stallman e Steve Wozniak. Questi hacker ci hanno messo a disposizione moderni sistemi operativi, linguaggi di programmazione, personal computer e molti altri strumenti tecnologici avanzati che oggi vengono utilizzati nella vita quotidiana. | << | < | > | >> |Pagina 4I veri hacker sono i pionieri, coloro che mettono a punto metodi originali e creano i tool che vengono poi raccolti nei suddetti CD. Tralasciando il problema della legalità e ragionando secondo logica, a ogni exploit corrisponde un patch che consente di difendersi. Un sistema irrobustito con patch appropriati dovrebbe essere immune rispetto a questa tipologia di attacchi. Gli aggressori che utilizzano unicamente queste tecniche, senza un contributo originale, sono destinati a danneggiare solo gli utenti più indifesi e stupidi. I veri hacker sono in grado di individuare per tempo lacune e punti deboli nel software e creare dei loro exploit.Se decidono di non informare il fornitore di questi punti vulnerabili, possono utilizzare tali exploit per penetrare senza ostacoli, con opportuni patch anche in sistemi fortificati ritenuti "sicuri". Se quindi non esistono dei patch per eliminare i punti vulnerabili, che cosa si può fare per impedire che gli hacker individuino nuove lacune nel software e le sfruttino per penetrarvi? Ecco il motivo per cui sono stati costituiti dei team di addetti alla sicurena informatica: essi hanno il compito di individuare queste lacune e di informame i fornitori prima che esse vengano sfruttate. C'è una benefica sinergia tra l'attività degli hacker che giocano "in difesa", dedicandosi alla protezione dei sistemi, e l'attività degli hacker che cercano di violarli. Questa competizione genera una maggiore sicurezza, oltre che tecniche di attacco più complesse e sofisticate. L'introduzione e il perfezionamento dei sistemi di rilevazione dei tentativi di intrusione (IDS) sono un importante esempio di questa sinergia. Gli hacker che giocano in difesa creano IDS da aggiungere al proprio arsenale, mentre quelli che giocano all'attacco sviluppano tecniche per eludere gli IDS, stimolando a loro volta lo sviluppo di sistemi IDS più potenti ed efficaci. Il risultato netto di questa interazione è positivo, perché produce individui più preparati e scaltri, una maggiore sicurezza, software più stabili, originali tecniche di soluzione di problemi; crea persino nuovi posti di lavoro. Scopo di questo libro è di fornire informazioni sull'autentico spirito che anima gli hacker. In esso esamineremo varie tecniche di hacking, da quelle entrate nel libro della storia a quelle più recenti, dissezionandole per comprenderne il funzionamento e i motivi per cui sono efficaci. Presentando le informazioni in questo modo, intendiamo consentire al lettore di comprendere e apprezzare le attività di hacking, ed eventualmente stimolarlo a migliorare le tecniche esistenti o addirittura inventarne di nuove. Confido che questo libro riesca a stimolare la curiosità scientifica del lettore e spronarlo a contribuire in qualche modo all'arte dell'hacking... da qualunque parte della barricata decida di stare. | << | < | > | >> |Pagina 170x253 Segmentazione della memoria di programmaLa memoria di programma è divisa in cinque segmenti: text, data, bss, heap e stack. Ciascun segmento rappresenta una porzione particolare della memoria che viene riservata per un dato scopo. Il segmento text (testo) è talvolta chiamato "segmento di codice". In esso sono collocate le istruzioni del programma assemblate in linguaggio macchina. L'esecuzione delle istruzioni contenute in questo segmento non è equenziale per via delle strutture di controllo e delle funzioni di alto livello menzionate prima, che in linguaggio assembler vengono compilate in istruzioni branch, jump e call. Quando un programma è in esecuzione, il puntatore EIP viene impostato sulla prima istruzione del segmento di testo. Il processore segue quindi un loop di esecuzione che fa quanto riportato di seguito. 1. Legge l'istruzione alla quale punta EIP. 2. Aggiunge la lunghezza in byte dell'istruzione a EIP. 3. Esegue l'istruzione letta nel passaggio 1. 4. Va al passaggio 1. Talvolta l'istruzione sarà una jump o una call, che trasferisce l'EIP a un diverso indirizzo di memoria. Il processore non è interessato a tale trasferimento, perché si aspetta che l'esecuzione non sia lineare. Se quindi l'EIP viene modificato nel passaggio 3, il processore tornerà al passaggio 1 e leggerà l'istruzione corrispondente all'indirizzo indicato dall'EIP qualunque sia il valore su cui è stato reimpostato l'EIP. L'autorizzazione a scrivere è disabilitata nel segmento text perché esso non è destinato ad immagazzinare variabili ma solo codice. In tal modo si impedisce che qualcuno possa modificare il codice del programma; a ogni tentativo di scrivere in questo segmento della memoria, il programma avvertirà l'utente che si è verificato un evento anomalo e l'esecuzione del programma avrà termine. C'è inoltre il vantaggio che il segmento text può essere condiviso tra diverse copie del programma, consentendo più esecuzioni simultanee dello stesso senza problemi di sorta. Si noti che questo segmento della memoria ha una dimensione fissa, perché esso è immutabile. I segmenti data (dati) e i segmenti bss sono utilizzati per immagazzinare le variabili globali e statiche. Il segmento data è riempito con variabili globali inizializzate (definite in partenza), stringhe e altre costanti che sono usate in tutto il programma. Il segmento bss contiene le parti corrispondenti non inizializzate. Questi segmenti pur essendo modificabili, hanno anch'essi una dimensione fissa. Il segmento heap è utilizzato per le rimanenti variabili di programma. È importante sottolineare che il segmento heap non ha una dimensione fissa: la sua dimensione può aumentare o diminuire a seconda delle necessità. Tutta la memoria all'interno del segmento heap è gestita con algoritmi di allocazione e deallocazione che, rispettivamente, riservano zone di memoria quando occorre e le liberano quando non servono più, rendendole nuovamente disponibili per successive assegnazioni. Il segmento heap si espanderà e si contrarrà a seconda della quantità di memoria che deve essere riservata per l'utilizzo da parte del programma. Quando il segmento heap si espande, lo fa spostandosi verso indirizzi di memoria più alta. Anche il segmento stack ha una dimensione variabile ed è utilizzato come "taccuino" temporaneo per immagazzinare i dati contestuali durante il richiamo di funzioni. Quando un programma richiama una funzione, tale funzione avrà un proprio insieme di variabili che le vengono trasferite e il codice della funzione sarà collocato in una posizione diversa nel segmento testo (o segmento codice). Poiché il contesto e il puntatore EIP devono cambiare quando viene richiamata una funzione, si utilizza lo stack per ricordare tutte le variabili trasferite e l'indirizzo del punto al quale il puntatore EIP dovrà tornare una volta eseguita la funzione. In termini di teoria generale dei computer, uno stack è una struttura astratta di dati utilizzata frequentemente. Esso funziona secondo uno schema "first-in, last-out" (FILO): il primo termine che viene immesso nello stack è l'ultimo ad esserne estratto. Come quando si infilano delle perle su un filo avente un grosso nodo all'estremità, non si può arrivare alla prima perla finché non si sono levate tutte le altre. Quando un elemento viene collocato nello stack, si usa il termine push mentre quando un elemento viene prelevato dallo stack si parla di pop. Come dice la parola stessa, il segmento di memoria stack (pila) è in effetti una struttura di dati impilati. Il registro ESP è utilizzato per tenere traccia dell'indirizzo dell'ultimo elemento dello stack che cambia continuamente man mano che si fa il push e il pop dei dati. Poiché ciò avviene dinamicamente, è logico che la dimensione dello stack non sia fissa. All'aumentare della dimensione dello stack, esso si espande verso indirizzi di memoria più bassi, al contrario di quanto succede nello heap. | << | < | > | >> |Pagina 133Capitolo 3
Il networking
Gli hack per le reti vengono realizzati secondo gli stessi principi degli
hack per la programmazione. Per crearli, occorre innanzi tutto comprendere le
regole di funzionamento del sistema, poi capire come sfruttarle per ottenere un
determinato risultato.
0x310 Che cos'è il networking? Il networking riguarda essenzialmente le comunicazioni, nonché gli standard e i protocolli che consentono a due o più parti di comunicare in modo opportuno. Parlare in giapponese a qualcuno che capisce solo l'italiano non consente di intendersi; analogamente, i computer e altri componenti hardware della rete devono parlare lo stesso linguaggio per poter comunicare efficacemente. Bisogna creare innanzi tutto un insieme di standard per definire tale linguaggio. In realtà, questi standard non descrivono unicamente il linguaggio, ma contengono anche le regole di comunicazione. Ricorrendo a un'altra analogia, supponiamo che un utente telefoni all'help desk: quando l'operatore risponde, occorre che le informazioni vengano comunicate e ricevute in un certo ordine, conformemente a un determinato protocollo. Di solito, l'operatore dovrà chiedere il nominativo del chiamante e la natura del problema prima di poter smistare la chiamata all'ufficio giusto. Così prevede il protocollo, e qualsiasi deviazione da esso impedirà il regolare svolgimento delle comunicazioni.
Le comunicazioni di rete sono anch'esse regolate da un insieme
standardizzato di protocolli. Tali protocolli sono definiti dal modello di
riferimento OSI
(Open Systems Interconnection).
0x311 Il modello OSI Il modello di riferimento OSI (Open Systems Interconnection) contiene una serie di regole e standard internazionali che consentono a qualsiasi sistema che rispetti questi protocolli di comunicare con altri sistemi altrettanto conformi. Questi protocolli sono strutturati in sette livelli, separati ma interconnessi, ciascuno dei quali riguarda un aspetto specifico della comunicazione. Tra l'altro, ciò consente a dispositivi hardware quali router e firewall di occuparsi del particolare aspetto della comunicazione di loro competenza, ignorando gli altri aspetti. I sette livelli OSI sono quelli elencati di seguito. 1. Livello fisico: questo livello riguarda la connessione fisica tra due punti. Si tratta del livello più basso, il cui ruolo principale riguarda la trasmissione di flussi di bit grezzi (raw). Tale livello è inoltre responsabile dell'attivazione, del mantenimento e della disattivazione di queste comunicazioni a flusso di bit. 2. Livello data-link: riguarda l'effettivo trasferimento di dati tra due punti. Il livello fisico provvede all'invio dei bit grezzi, ma questo livello fornisce funzioni superiori, quali la correzione degli errori e il controllo di flusso. Inoltre esso prevede procedure per l'attivazione, il mantenimento e la disattivazione di connessioni data-link. 3. Livello di rete: è intermedio e ha soprattutto il ruolo di assicurare il trasferimento di informazioni tra livelli inferiori e superiori. Esso consente l'indirizzamento e l'instradamento. 4. Livello di trasporto: consente il trasferimento in modalità trasparente di dati tra due sistemi. Provvedendo alla trasmissione dei dati in modo affidabile, questo livello consente ai livelli superiori di "occuparsi" di altri aspetti esulanti dal problema di assicurare un mezzo di comunicazione affidabile o economicamente conveniente. 5. Livello di sessione: ha il compito di stabilire e poi mantenere le connessioni tra le applicazioni di rete. 6. Livello della presentazione: riguarda la presentazione dei dati alle applicazioni in una sintassi o un linguaggio che esse sono in grado di comprendere. Esso tra l'altro consente la cifratura e la compressione del dati. 7. Livello di applicazione: riguarda la registrazione continua delle richieste delle applicazioni. Quando i dati vengono comunicati tramite questi protocolli, essi vengono inviati sotto forma di frammenti, chiamati "pacchetti". Ogni pacchetto contiene implementazioni di questi protocolli nei livelli. Partiamo dal livello applicazione: il pacchetto viene avvolto dal livello della presentazione, poi dal livello di sessione, poi ancora dal livello di trasporto e così via. Questo processo è chiamato "incapsulamento". Ogni livello avvolto contiene un'intestazione (header) e un corpo (body): l'intestazione e il corpo contengono rispettivamente le informazioni di protocollo e i dati occorrenti per tale livello. Il corpo di un livello contiene l'intero pacchetto di livelli precedentemente incapsulati, così come la buccia di una cipolla, o i contesti funzionali presenti in uno stack di programma.
Quando due applicazioni esistenti
su due reti private comunicano attraverso Internet, i pacchetti di dati vengono
incapsulati fino al livello fisico tramite il quale vengono trasferiti a un
router. Poiché al router non interessa sapere cosa c'è realmente nei pacchetti,
esso deve solo implementare i protocolli fino al livello di rete. Il router
invia i pacchetti all'esterno su Internet ed essi raggiungono il router
dell'altra rete. Questo secondo router incapsula a sua volta il pacchetto in
questione con le intestazioni del protocollo di livello inferiore necessarie
affinché il pacchetto possa raggiungere la sua destinazione finale. Tale
processo è illustrato nella figura seguente.
|