Copertina
Autore Jon Erickson
Titolo L'arte dell'hacking
SottotitoloLe idee, gli strumenti, le tecniche degli hacker
EdizioneApogeo, Milano, 2004, , pag. 240, cop.fle., dim. 170x240x14 mm , Isbn 978-88-503-2280-0
OriginaleHacking: The Art of Exploitation [2003]
PrefazioneRaoul Chiesa
TraduttoreMaurizio Valente
LettoreCorrado Leonardo, 2004
Classe informatica: sistemi , informatica: linguaggi
PrimaPagina


al sito dell'editore


per l'acquisto su IBS.IT

per l'acquisto su BOL.IT

per l'acquisto su AMAZON.IT

 

| << |  <  |  >  | >> |

Indice

Prefazione                                                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 1

Capitolo 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 4

I 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 17

0x253 Segmentazione della memoria di programma

La 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 133

Capitolo 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.

| << |  <  |