2013-12-18 22 views
10

sto sperimentando con i buffer overflow e cercare di sovrascrivere l'indirizzo di ritorno dello stack con un certo ingresso di fgetscausano un buffer overflow con fgets

Questo è il codice:

void foo() 
{ 
    fprintf(stderr, "You did it.\n"); 
} 

void bar() 
{ 
    char buf[20]; 
    puts("Input:"); 
    fgets(buf, 24, stdin); 
    printf("Your input:.\n", strlen(buf)); 
} 


int main(int argc, char **argv) 
{ 
    bar(); 
    return 0; 
} 

Su un esecuzione normale il programma restituisce semplicemente il tuo input. Voglio che emetta foo() senza modificare il codice.

La mia idea era di traboccare il buffer di buf inserendo 20 'A' s. Funziona e causa un errore di segmentazione. La mia prossima idea era di scoprire l'indirizzo di foo() che è \x4006cd e aggiungere questo al 20 'A' s.

Da quanto ho capito, questo dovrebbe sovrascrivere l'indirizzo di ritorno dello stack e farlo saltare a foo. Ma provoca solo un segfault.

Cosa sto sbagliando?

Aggiornamento: Assembler discariche principale

Dump of assembler code for function main: 
    0x000000000040073b <+0>: push %rbp 
    0x000000000040073c <+1>: mov %rsp,%rbp 
    0x000000000040073f <+4>: sub $0x10,%rsp 
    0x0000000000400743 <+8>: mov %edi,-0x4(%rbp) 
    0x0000000000400746 <+11>: mov %rsi,-0x10(%rbp) 
    0x000000000040074a <+15>: mov $0x0,%eax 
    0x000000000040074f <+20>: callq 0x4006f1 <bar> 
    0x0000000000400754 <+25>: mov $0x0,%eax 
    0x0000000000400759 <+30>: leaveq 
    0x000000000040075a <+31>: retq 
    End of assembler dump. 

foo

Dump of assembler code for function foo: 
    0x00000000004006cd <+0>: push %rbp 
    0x00000000004006ce <+1>: mov %rsp,%rbp 
    0x00000000004006d1 <+4>: mov 0x200990(%rip),%rax  # 0x601068 <[email protected]@GLIBC_2.2.5> 
    0x00000000004006d8 <+11>: mov %rax,%rcx 
    0x00000000004006db <+14>: mov $0x15,%edx 
    0x00000000004006e0 <+19>: mov $0x1,%esi 
    0x00000000004006e5 <+24>: mov $0x400804,%edi 
    0x00000000004006ea <+29>: callq 0x4005d0 <[email protected]> 
    0x00000000004006ef <+34>: pop %rbp 
    0x00000000004006f0 <+35>: retq 
End of assembler dump. 

bar:

Dump of assembler code for function bar: 
    0x00000000004006f1 <+0>: push %rbp 
    0x00000000004006f2 <+1>: mov %rsp,%rbp 
    0x00000000004006f5 <+4>: sub $0x20,%rsp 
    0x00000000004006f9 <+8>: mov $0x40081a,%edi 
    0x00000000004006fe <+13>: callq 0x400570 <[email protected]> 
    0x0000000000400703 <+18>: mov 0x200956(%rip),%rdx  # 0x601060 <[email protected]@GLIBC_2.2.5> 
    0x000000000040070a <+25>: lea -0x20(%rbp),%rax 
    0x000000000040070e <+29>: mov $0x18,%esi 
    0x0000000000400713 <+34>: mov %rax,%rdi 
    0x0000000000400716 <+37>: callq 0x4005b0 <[email protected]> 
    0x000000000040071b <+42>: lea -0x20(%rbp),%rax 
    0x000000000040071f <+46>: mov %rax,%rdi 
    0x0000000000400722 <+49>: callq 0x400580 <[email protected]> 
    0x0000000000400727 <+54>: mov %rax,%rsi 
    0x000000000040072a <+57>: mov $0x400821,%edi 
    0x000000000040072f <+62>: mov $0x0,%eax 
    0x0000000000400734 <+67>: callq 0x400590 <[email protected]> 
    0x0000000000400739 <+72>: leaveq 
    0x000000000040073a <+73>: retq 
End of assembler dump. 
+0

Hai guardato l'assemblatore corrispondente (e quindi lo stack layout) per questo codice? –

+0

Se solo fosse così facile ... –

+0

Dovresti dare un'occhiata all'assemblaggio corrispondente. A volte il compilatore farà più di 20 byte che hai fatto per l'allineamento. Si dovrebbe anche guardare il layout dello stack e come salvare i registri/salvati addrs e in quale ordine. Vai avanti e pubblica il meglio della funzione e possiamo aiutarti ulteriormente. –

risposta

3

Non contava con di allineamento memoria. Ho cambiato il codice un po 'per renderlo più facile trovare il posto giusto.

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 

int **x; 
int z; 

void foo() 
{ 
    fprintf(stderr, "You did it.\n"); 
} 

void bar() 
{ 
    char buf[2]; 
    //puts("Input:"); 
    //fgets(buf, 70, stdin); 
    x = (int**) buf; 
    for(z=0;z<8;z++) 
      printf("%d X=%x\n", z, *(x+z)); 
    *(x+3) = foo; 
    printf("Your input: %d %s\n", strlen(buf), buf); 
} 


int main(int argc, char **argv) 
{ 
     printf("Foo: %x\n", foo); 
     printf("Main: %x\n", main); 
     bar(); 
     return 0; 
} 

Con un buffer più piccolo, 2 nel mio esempio, ho trovato l'indirizzo di ritorno 24 byte di distanza (x + 3, per 8 puntatori di byte; 64 bit, senza eseguire il debug, senza ottimizzazione ...) fin dall'inizio del buffer. Questa posizione può cambiare in base alla dimensione del buffer, all'architettura, ecc. In questo esempio, riesco a cambiare l'indirizzo di ritorno della barra in foo. In ogni caso si otterrà un errore di segmentazione al ritorno, in quanto non è stato impostato correttamente per tornare al main.

Ho aggiunto x e z come vars globali per non modificare la dimensione dello stack della barra. Il codice mostrerà una matrice di valori come il puntatore, a partire da buf [0]. Nel mio caso, ho trovato l'indirizzo principale nella posizione 3. Ecco perché il codice finale ha * (x + 3) = pippo. Come ho detto, questa posizione può cambiare a seconda delle opzioni di compilazione, della macchina ecc. Per trovare la posizione corretta, trovare l'indirizzo di main (stampato prima della barra di chiamata) nell'elenco degli indirizzi.

È importante notare che ho detto indirizzo in main, non in quello di main, perché l'indirizzo di ritorno è stato impostato sulla linea dopo la chiamata a bar e non all'inizio di main. Quindi, nel mio caso, era 0x4006af invece di 0x400668.

Nell'esempio, con 20 byte di buffer, per quanto ne so, era allineato a 32 byte (0x20).

Se si desidera fare lo stesso con fgets, è necessario capire come digitare l'indirizzo di foo, ma se si sta eseguendo una macchina x86/x64, ricordarsi di aggiungerla in little endian. È possibile modificare il codice per visualizzare i valori byte per byte, quindi è possibile ottenerli nell'ordine corretto e digitarli utilizzando il numero ALT +. Ricorda che i numeri che digiti tenendo premuto ALT sono numeri decimali. Alcuni terminali non saranno gestiti in modo amichevole 0x00.

mio output è simile:

$ gcc test.c -o test 
test.c: In function ‘bar’: 
test.c:21: warning: assignment from incompatible pointer type 
$ ./test 
Foo: 400594 
Main: 400668 
0 X=9560e9f0 
1 X=95821188 
2 X=889350f0 
3 X=4006af 
4 X=889351d8 
5 X=0 
6 X=0 
7 X=95a1ed1d 
Your input: 5 ▒▒`▒9 
You did it. 
Segmentation fault 
2
void bar() 
{ 
    char buf[20]; 
    puts("Input:"); 
    fgets(buf, 24, stdin); 
    printf("Your input:.\n", strlen(buf)); 
} 

... questo funziona e causa un errore di segmentazione ...

Il compilatore è probabilmente sostituendo fgets con una variante più sicura che include un controllo sulla dimensione del buffer di destinazione. Se il controllo fallisce, il prgram chiama incondizionatamente abort().

In questo caso particolare, è necessario compilare il programma con -U_FORTIFY_SOURCE o -D_FORTIFY_SOURCE=0.