Overflow del buffer: consigli sull'utilizzo

0
int play() {

    int a;
    int b;
    char buffer[010];
    a = 0x41414141;
    b = 0x42424242;

    if (write(STDOUT_FILENO, "For a moment, nothing happened. Then, after a second or so, nothing continued to happen.\n> ", 91) < 0) {
        perror("write");
    }

    if (read(STDIN_FILENO, &buffer, 0xC) < 0) {                                              
        perror("read");                                                                   
    }

    if (a == 31337) {
        system(buffer);
    }

    else if (b == 1337) {
        readfile("flag.0");
    }

    else if (b == 42) {
        readfile("vuln1.txt");
    }

    else {
        write(STDOUT_FILENO, "So long and thanks for all the fish.\n", 37);
    }

}


int main(int argc, char *argv[]){
    play();
    exit(EXIT_SUCCESS);
}

Penso che quello che sta succedendo è che con un buffer se la dimensione di 11, sta cercando di aggiungere 12 byte ad esso? E 'anche vicino?

Sono un principiante, è spaventoso e mi scuso se questo non è il posto giusto per questo tipo di domande. Qualsiasi punto d'appoggio su cosa fare sarebbe molto apprezzato.

L'ho compilato e l'ho eseguito con GDB ma non sono riuscito ad arrivare da nessuna parte

    
posta pee2pee 10.01.2017 - 01:11
fonte

2 risposte

3

0x414141 e 0x424242 che vengono inseriti nei libri a scopo didattico perché si tratta semplicemente delle stringhe "AAAAAAAA" e "BBBBBBB" in notazione esadecimale. Assegnarli a numeri interi non ha nulla a che fare con i buffer overflow.

Un esempio molto migliore può essere ottenuto utilizzando un programma completo e una chiamata di funzione che ci permetterà di sapere quando avviene l'overflow. Ad esempio, cambiamo il programma in:

#include <stdio.h>
#include <unistd.h>

void
fun(void)
{
    char buffer[10];

    if (read(STDIN_FILENO, &buffer, 60) < 0) {
        perror("read");
    }
    write(STDOUT_FILENO, "So long and thanks for all the fish.\n", 37);
    return;
}

int
main(void)
{
    fun();
    write(STDOUT_FILENO, "WILL NOT PRINT.\n", 16);
    return 0;
}

Dato che stai usando read e write stiamo assumendo un sistema UNIX, quindi compiliamolo ed esegui un paio di volte. Supponendo che la sorgente del programma sia in buf.c :

[~]$ gcc -g -o prog buf.c
[~]$ perl -e 'print "A"x10' | ./prog
So long and thanks for all the fish.
WILL NOT PRINT.
[~]$ perl -e 'print "A"x20' | ./prog
So long and thanks for all the fish.
WILL NOT PRINT.

Perl è utile perché possiamo controllare esattamente il numero di byte che forniamo al programma. Poiché STDIN è collegato a una pipe, possiamo fornire una quantità variabile di byte, fino ai 60 byte codificati nell'origine del programma.

La prima esecuzione dà quello che ci aspettavamo, 10 byte sono posti nel buffer lungo 10 byte. Nella seconda esecuzione il buffer da 10 byte dovrebbe avere un overflow con 20 byte di input. Ma nulla sembra accadere. Bene, il buffer ha avuto un overflow, ma non ha esagerato abbastanza da ostacolare l'esecuzione del processo. Facciamo di più:

[grochmal@haps tmp]$ perl -e 'print "A"x30' | ./prog
So long and thanks for all the fish.
Segmentation fault (core dumped)

Bene, questo è ciò che ci aspettiamo di vedere da un eccesso. Ora che abbiamo un buon input, lo salviamo e vediamo come funziona il programma in GDB:

[grochmal@haps tmp]$ perl -e 'print "A"x30' > input
[grochmal@haps tmp]$ gdb -q prog
Reading symbols from prog...done.
(gdb) list
5   fun(void)
6   {
7       char buffer[10];
8   
9       if (read(STDIN_FILENO, &buffer, 30) < 0) {
10          perror("read");
11      }
12      write(STDOUT_FILENO, "So long and thanks for all the fish.\n", 37);
13      return;
14  }
(gdb) list
15  
16  int
17  main(void)
18  {
19      fun();
20      write(STDOUT_FILENO, "WILL NOT PRINT.\n", 16);
21      return 0;
22  }
23  
(gdb) break 9
Breakpoint 1 at 0x40058e: file buf.c, line 9.
(gdb) break 12
Breakpoint 2 at 0x4005b3: file buf.c, line 12.

Da quando abbiamo aggiunto -g alla riga del compilatore abbiamo tutti i simboli di debug (ad esempio il codice del programma) che potremmo volere. Ciò rende molto facile impostare i punti di interruzione in cui avviene la parte interessante dell'esecuzione. Eseguiamo il programma con l'input che abbiamo appena salvato e controlliamo come appare il buffer:

(gdb) run <input
Starting program: /home/grochmal/tmp/prog <input

Breakpoint 1, fun () at buf.c:9
9       if (read(STDIN_FILENO, &buffer, 30) < 0) {
(gdb) p &buffer
$1 = (char (*)[10]) 0x7fffffffe800
(gdb) x/15wx 0x7fffffffe800
0x7fffffffe800: 0x004005f0  0x00000000  0x00400490  0x00000000
0x7fffffffe810: 0xffffe820  0x00007fff  0x004005d3  0x00000000
0x7fffffffe820: 0x004005f0  0x00000000  0xf7a5c291  0x00007fff
0x7fffffffe830: 0xf7dd0798  0x00007fff  0xffffe908

Il buffer ha una lunghezza di soli 10 byte e abbiamo stampato 60 byte. Ma questo lascia fuori 0xffffe820 , che è la posizione in cui il programma deve tornare (in main) dalla chiamata fun() . Quando il buffer viene sovraccaricato, i byte lo sovrascrivono. Vediamolo continuando con la chiamata read .

(gdb) cont
Continuing.

Breakpoint 2, fun () at buf.c:12
12      write(STDOUT_FILENO, "So long and thanks for all the fish.\n", 37);
(gdb) x/15wx 0x7fffffffe800
0x7fffffffe800: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffe810: 0x41414141  0x41414141  0x41414141  0x00004141
0x7fffffffe820: 0x004005f0  0x00000000  0xf7a5c291  0x00007fff
0x7fffffffe830: 0xf7dd0798  0x00007fff  0xffffe908

E ora il programma proverà a tornare a 0x41414141 , il che causerà il segfault.

(gdb) cont
Continuing.
So long and thanks for all the fish.

Program received signal SIGSEGV, Segmentation fault.
0x0000414141414141 in ?? ()

E lo ha fatto! Come bonus abbiamo la stringa 0x41414141 che stai utilizzando come assegnazione int nel tuo programma, e ora puoi dedurre da dove proviene.

La sicurezza implicita di questo è che so esattamente quale parte dell'ingresso sovrascrive quel valore di ritorno sullo stack. E grazie a ciò, posso guidare il programma a qualsiasi istruzione all'interno della sua allocazione di memoria.

(In pratica è molto più difficile oggi perché ci sono parecchie misure per impedire a tali overflow di trovare luoghi utili a cui tornare. Ma il concetto è lo stesso.)

    
risposta data 10.01.2017 - 05:41
fonte
0

Usando il programma di @grochmal, sono stato in grado di ottenere l'errore di segmentazione sulla mia macchina locale. Non riesco ancora a risolvere il problema principale.

Poiché non è stato menzionato esplicitamente nella domanda originale, questo fa parte di una sfida in cui il file vuln1.txt deve essere letto causando un overflow attraverso una porta aperta in remoto dove il programma sopra è in esecuzione.

Sto cercando di capire come impostare il valore di B = 42. Qualsiasi aiuto è molto apprezzato.

    
risposta data 14.01.2017 - 02:51
fonte

Leggi altre domande sui tag