• Regolamento Macrocategoria DEV
    Prima di aprire un topic nella Macrocategoria DEV, è bene leggerne il suo regolamento. Sei un'azienda o un hosting/provider? Qui sono anche contenute informazioni per collaborare con Sciax2 ed ottenere l'accredito nella nostra community!

Guida Buffer OverFlow (Parte pratica)

ErJeY

Utente Attivo
Autore del topic
10 Maggio 2010
379
0
Miglior risposta
0
La Teoria la potete leggere dalla guida di system32, alcune pagine fa..
Guida al Buffer Overflow di un solo byte
Dividerò questa guida in paragrafi per farvi capire meglio..
Ovviamente dovrete sapere un po di assembly e dovete anche saper maneggiare un pochino ollydbg.

Di cosa parliamo?
Di un buffer, più precisamente come provocarlo su un programma.. Secondo voi perchè da C e i suoi derivati si iniziava a contare da 0 mentre dagli altri linguaggi più vecchi a partire da 1?
Rispondo io... per migliorare la sicurezza!
Per eseguire un overflow di questo tipo (1 byte) abbiamo bisogno di un buffer, di un programma buggato(ovvero con errori di codice, scritto male) e dello spazio per metterci dentro lo shellcode (Uno shellcode è un programma in linguaggio assembly che tradizionalmente esegue una shell)


Parte pratica:
Ecco il source di una semplice applicazione:
void overflowbytes(char *string);

int main(int argv, char *argc[]){
overflowbytes(argc[1]);
}

void overflowbytes(char *string){
char buffer[112];
int i;
for(i=0; i<=112; i++)
buffer = string;
}

bene... andiamo ad analizzare il codice sorgente:
char buffer[112]; // Il buffer vulnerabile
int i; // Contatore per il for

for(i=0; i<=112; i++) // Il for che mette il buffer
buffer = string; // Copiamo in buffer gli argomenti del programma

Cosa c'è di strano in ciò? Bhe in C e derivati scrivere char buffer[112]; significa che si allocano de ibyte da 0 a 111 e così ci rimane un byte nel quale possiamo fare quasi di tutto.

Quindi quel codice equivale ad andare fuori dal buffer e come gia detto ci rimane un byte da utilizzare... Ora è tempo di disassemblare!
Useremo Ida che secondo me è anche il migliore perchè ci fa il listato delle azione del processore.
Compiliamo il programma e analizziamolo con Ida partendo da main():
.text:08048540 main proc near
.text:08048440
.text:08048440 var_4 = dword ptr -4
.text:08048440 arg_4 = dword ptr 0Ch
.text:08048440
.text:08048440 push ebp
.text:08048441 mov ebp, esp
.text:08048443 sub esp, 4
.text:08048446 mov eax, [ebp+arg_4]
.text:08048449 add eax, 4
.text:0804844C mov eax, [eax]
.text:0804844E mov [esp+4+var_4], eax ; argc[1]
.text:08048451 call overflowbytes ; overflowbytes(argc[1])
.text:08048456 leave
.text:08048457 retn
.text:08048457 main endp

poi andiamo ad analizzare la funzione overflow bytes:

.text:08048458 overflowbytes proc near
.text:08048458
.text:08048458 i = dword ptr -74h
.text:08048458 buffer = byte ptr -70h
.text:08048458 arg_0 = dword ptr 8
.text:08048458
.text:08048458 push ebp
.text:08048459 mov ebp, esp
.text:0804845B sub esp, 74h ; 74h = 111 utilizzati dal programma e 1 libero
.text:0804845E mov [ebp+i], 0
.text:08048465
.text:08048465 for:
.text:08048465 cmp [ebp+i], 70h ; for(int i=1; i<=111;)
.text:08048469 jle short loop ; if (i<= 112) goto loop
.text:0804846B jmp short exit ; else goto exit

Di tutto ciò la parte di codice che ci interessa è questa:

.text:08048458 push ebp
.text:08048459 mov ebp, esp ; Crea un nuovo frame
.text:0804845B sub esp, 74h ; Lo spazio per tutte le variabili usate dalla funzione

queste righe di codice servono a creare un nuovo frame all'interno della chiamata, provate a vedere a questa istruzione:
.text:08048451 call overflowbytes

Quando il proc. va alla call salva eip ( un registro usato in assembly) nello stack, dopodichè crea un nuovo frame nel quale c'è lo spazio per le variabili e alla fine c'è il retn:
.text:08148487 retn
Retn prende l'eip dello stack precedente e lo porta alla call seguente: esso ha una funzione: leave... useremo questa...


E' ora di attaccare lo stack..Ricordatevi che però esso lavora al contrario cioè cresce verso numeri minori:

int main(int argv, char *argc[]){
overflow(argc[1]); //1. EIP nello stack
}

void overflow(char *string){ //2. qui salva EBP
char buffer[112]; //3. Adesso c'è il buffer, l'ultimo byte si trova sotto EPB
int i; //4. L'intero i

for(i=0; i<=112; i++)
buffer = string;
}

Quindi vediamo col debugger:

m.ain()
.text:0804844C mov eax, [eax]
.text:0804844E mov [esp+4+var_4], eax ; argc[1]
.text:08048451 call overflowbytes

overflowbytes()
.text:08048458 push ebp ; Qui c'è ebp
.text:08048459 mov ebp, esp
.text:0804845B sub esp, 74h
.text:0804845E mov [ebp+i], 0

Quindi ora scriviamo EIP all'address 08048451, poi entriamo nella chiamata(cosa che richiede un po di esperienza consiglio di leggere qualche guida) e cambiamo il byte di ebp, l'ultimo.Così facendo se avviamo il programma così:
paolo@panther:~$ ./one AAAAAAAAAAAA (111 + 1 A o +)

Vediamo che in effetti il codice ascii è cambiato davvero e quindi il byte è stato riscritto.. Bhe per ora è finita.. quando sarete riusciti a fare ciò vi dirò come mettere uno shellcode nel byte.. alla prossima...
 
Ultima modifica: