WWW2Exec - .dtors & .fini_array
.dtors
Oggi è molto strano trovare un binario con una sezione .dtors!
I distruttori sono funzioni che vengono eseguite prima che il programma finisca (dopo che la funzione main
ritorna).
Gli indirizzi di queste funzioni sono memorizzati all'interno della sezione .dtors
del binario e quindi, se riesci a scrivere l'indirizzo di un shellcode in __DTOR_END__
, questo verrà eseguito prima che il programma finisca.
Ottieni l'indirizzo di questa sezione con:
Di solito troverai i marcatori DTOR tra i valori ffffffff
e 00000000
. Quindi se vedi solo quei valori, significa che non c'è alcuna funzione registrata. Quindi sovrascrivi il 00000000
con l'indirizzo dello shellcode per eseguirlo.
Naturalmente, prima devi trovare un posto dove memorizzare lo shellcode per poterlo chiamare in seguito.
.fini_array
Essenzialmente si tratta di una struttura con funzioni che verranno chiamate prima che il programma finisca, come .dtors
. Questo è interessante se puoi chiamare il tuo shellcode saltando a un indirizzo, o nei casi in cui devi tornare di nuovo a main
per sfruttare la vulnerabilità una seconda volta.
Nota che quando una funzione dall'.fini_array
viene eseguita, passa alla successiva, quindi non verrà eseguita più volte (prevenendo loop eterni), ma ti darà solo un'esecuzione della funzione posizionata qui.
Nota che le voci in .fini_array
vengono chiamate in ordine inverso, quindi probabilmente vorrai iniziare a scrivere dall'ultima.
Loop eterno
Per abusare di .fini_array
per ottenere un loop eterno puoi controllare cosa è stato fatto qui: Se hai almeno 2 voci in .fini_array
, puoi:
Utilizzare il tuo primo write per richiamare di nuovo la funzione di scrittura arbitraria vulnerabile
Quindi, calcolare l'indirizzo di ritorno nello stack memorizzato da
__libc_csu_fini
(la funzione che chiama tutte le funzioni di.fini_array
) e mettere lì l'indirizzo di__libc_csu_fini
Questo farà sì che
__libc_csu_fini
si richiami nuovamente eseguendo di nuovo le funzioni di.fini_array
che richiameranno la funzione WWW vulnerabile 2 volte: una per la scrittura arbitraria e un'altra per sovrascrivere di nuovo l'indirizzo di ritorno di__libc_csu_fini
nello stack per richiamarsi di nuovo.
Nota che con Full RELRO, la sezione .fini_array
viene resa sola lettura.
link_map
Come spiegato in questo post, se il programma esce utilizzando return
o exit()
verrà eseguito __run_exit_handlers()
che chiamerà i distruttori registrati.
Se il programma esce tramite la funzione _exit()
, verrà chiamata la syscall di exit
e gli handler di uscita non verranno eseguiti. Quindi, per confermare che __run_exit_handlers()
viene eseguito, puoi impostare un breakpoint su di esso.
Il codice importante è (fonte):
Nota come map -> l_addr + fini_array -> d_un.d_ptr
viene utilizzato per calcolare la posizione dell'array di funzioni da chiamare.
Ci sono un paio di opzioni:
Sovrascrivere il valore di
map->l_addr
in modo che punti a un falsofini_array
con istruzioni per eseguire codice arbitrarioSovrascrivere le voci
l_info[DT_FINI_ARRAY]
el_info[DT_FINI_ARRAYSZ]
(che sono più o meno consecutive in memoria), in modo che puntino a una strutturaElf64_Dyn
falsificata che farà di nuovo sì chearray
punti a una zona di memoria controllata dall'attaccante.Questo articolo sovrascrive
l_info[DT_FINI_ARRAY]
con l'indirizzo di una memoria controllata in.bss
contenente un falsofini_array
. Questo array falso contiene prima un indirizzo one gadget che verrà eseguito e poi la differenza tra l'indirizzo di questo array falso e il valore dimap->l_addr
in modo che*array
punti all'array falso.Secondo il post principale di questa tecnica e questo articolo ld.so lascia un puntatore nello stack che punta al
link_map
binario in ld.so. Con una scrittura arbitraria è possibile sovrascriverlo e farlo puntare a un falsofini_array
controllato dall'attaccante con l'indirizzo di un one gadget ad esempio.
Segue il codice precedente un'altra sezione interessante con il codice:
In questo caso sarebbe possibile sovrascrivere il valore di map->l_info[DT_FINI]
puntando a una struttura ElfW(Dyn)
forgiata. Trova ulteriori informazioni qui.
Sovrascrittura di dtor_list di TLS-Storage in __run_exit_handlers
__run_exit_handlers
Come spiegato qui, se un programma esce tramite return
o exit()
, eseguirà __run_exit_handlers()
che chiamerà qualsiasi funzione di distruttore registrata.
Codice da _run_exit_handlers()
:
Codice da __call_tls_dtors()
:
Per ogni funzione registrata in tls_dtor_list
, demanglerà il puntatore da cur->func
e lo chiamerà con l'argomento cur->obj
.
Utilizzando la funzione tls
da questo fork di GEF, è possibile vedere che effettivamente il dtor_list
è molto vicino al canary dello stack e al cookie PTR_MANGLE. Quindi, con un overflow su di esso sarebbe possibile sovrascrivere il cookie e il canary dello stack.
Sovrascrivendo il cookie PTR_MANGLE, sarebbe possibile bypassare la funzione PTR_DEMANLE
impostandola su 0x00, ciò significherebbe che lo xor
utilizzato per ottenere l'indirizzo reale è proprio l'indirizzo configurato. Quindi, scrivendo sul dtor_list
è possibile concatenare diverse funzioni con l'indirizzo della funzione e il suo argomento.
Infine, notare che il puntatore memorizzato non solo verrà sottoposto a xor con il cookie ma verrà anche ruotato di 17 bit:
Quindi è necessario tenere conto di questo prima di aggiungere un nuovo indirizzo.
Trova un esempio nel post originale.
Altri puntatori modificati in __run_exit_handlers
__run_exit_handlers
Questa tecnica è spiegata qui e dipende nuovamente dal programma che esce chiamando return
o exit()
quindi viene chiamato __run_exit_handlers()
.
Controlliamo più codice di questa funzione:
La variabile f
punta alla struttura initial
e a seconda del valore di f->flavor
verranno chiamate diverse funzioni.
A seconda del valore, l'indirizzo della funzione da chiamare sarà in un posto diverso, ma sarà sempre demangled.
Inoltre, nelle opzioni ef_on
e ef_cxa
è anche possibile controllare un argomento.
È possibile controllare la struttura initial
in una sessione di debug con GEF eseguendo gef> p initial
.
Per sfruttare ciò è necessario leak o cancellare il cookie PTR_MANGLE
e sovrascrivere una voce cxa
in initial con system('/bin/sh')
.
È possibile trovare un esempio di ciò nel post originale del blog sulla tecnica.
Last updated