Rispetto al precedente livello, in questo caso il focus inizia ad essere sullo heap e sulla sua struttura, ed andremo quindi a comprendere meglio come è fatto e come è possibile, ovviamente, exploitarlo.
Come per lo scorso esercizio, andiamo ad inserire un breakpoint alla fine del codice (riga 35) e eseguiamo dentro gdb con il comando r AA BB. Dopodiché analizziamo lo heap
Si può notare che al suo interno abbiamo:
Ora, osservando la riga 31 e 32 e la struttura appena analizzata, possiamo ipotizzare che l’overflow avvenga proprio durante il primo strcpy, in quanto se andiamo ad inserire più caratteri di quanti l’array name ne contenga (8), dovremmo riuscire a sovrascrivere la struct i2. Facciamo una prova con ltrace
Ed ecco che, come si può vedere, ora il puntatore dentro lo strcpy vale 0x41414141. Andando ad indagare con GDB e lo stesso payload
Non esiste più il puntatore alla variabile i2.name ed il programma è andato in crash. Ma come possiamo sfruttare questo overflow per avere un exploit funzionante?
Per prima cosa dobbiamo calcolare l’offset in modo tale da sovrascrivere l’indirizzo in maniera precisa, ed è presto detto.
In questo modo, abbiamo calcolato la differenza tra la posizione del puntatore a i2.name (0x804b02c) e la posizione di i1.name (0x0804b018). Facciamo un test rapido per vedere se è giusto con il payload
gdb-peda$ r `perl -e 'print "A"x20 . "BBBB"' CC
Viene sovrascritto in maniera perfetta.
Ora che abbiamo trovato il punto da sovrascrivere, dobbiamo trovare quale indirizzo sovrascrivere. Ci potrebbero essere più vie, vediamone un paio.
Un file ELF è composto da più sezioni, ognuna delle quali composta dai propri dati e dalle proprie peculiarità. Ad esempio, se guardiamo quelle di heap1
ne abbiamo molteplici,come:
Il Lazy Binding si assicura sostanzialmente che un dynamic linker non perda tempo a cercare tutte le librerie necessarie ad un programma ma solo quelle che sono necessarie a runtime. Questa procedura è implementata grazie a due sezioni:
Per chi non avesse chiaro il procedimento, consiglio questo ottimo articolo Ma quindi, se andassimo a sovrascrivere sul puntatore a i2.name la chiamata a printf della funzione winner?
Prima di tutto, con GDB-Peda analizzo la sezione GOT dell’eseguibile
Una volta identificata, dovremo andare ad inserire quale indirizzo andremo a far chiamare la printf. Per ricapitolare l’idea è:
Troviamo winner con objdump
osboxes@osboxes:~/Desktop/Protostar$ objdump -d heap1|grep winner
080484cb winner:
Sembra non avere funzionato. Osservando meglio la GOT, oltre ad una printf c’è anche una puts. Proviamo con quella
Analizzando dentro GDB, possiamo vedere il giro completo che siamo riusciti a creare
Ammetto di aver lasciato il metodo due dopo GOT perché decisamente più semplice rispetto al precedente. In questo caso, possiamo semplicemente sovrascrivere il puntatore a i2.name con l’indirizzo del return address e successivamente far puntare ad esso la funzione winner. Procedendo sempre per passi, dobbiamo quindi:
Inseriamo un breakpoint a main e troviamo EIP
Ora, ci basta sostituire questo indirizzo con quello di GOT del precedente payload
Ma sembra non funzionare. Non sono riuscito a capire come mai, e ho iniziato a sparare nel mucchio trovando poi l’indirizzo di EIP corretto (0xbffff60c)
Cosa molto simile fuori da GDB, in quanto ho dovuto modificare l’indirizzo del return address in 0xbffff67c per ottenere un exploit funzionante
Se sapete come mai questo comportamento di GDB commentate pure!
Già con questo esercizio abbiamo approfondito molti più argomenti e l’heap inizia ad essere un pelo più chiaro. Gli approfondimenti li lasciamo per il prossimo esercizio, già in questo di cose da studiare ne sono spuntate!