Wie kann ich einen Pufferüberlauf auslösen?

Lesezeit: 5 Minuten

Benutzeravatar von sa125
sa125

Ich habe eine Hausaufgabe bekommen, in der ich aufgefordert wurde, eine Funktion aufzurufen, ohne sie explizit aufzurufen, indem ich einen Pufferüberlauf verwende. Der Code ist im Grunde dieser:

#include <stdio.h>
#include <stdlib.h>

void g()
{
    printf("now inside g()!\n");
}


void f()
{   
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)
}

int main (int argc, char *argv[])
{
    f();
    return 0;
}

Obwohl ich nicht sicher bin, wie ich vorgehen soll. Ich habe darüber nachgedacht, die Rückkehradresse für den Programmzähler so zu ändern, dass er direkt zur Adresse von g() weitergeht, aber ich bin mir nicht sicher, wie ich darauf zugreifen soll. Wie auch immer, Tipps werden großartig sein.

  • 4 Upvotes für eine Hausaufgabenfrage! Das OP hat nicht einmal die Frage gestellt … wow, manche Leute sind leicht zu beeindrucken.

    – Lazarus

    25. Februar 2010 um 12:41 Uhr

  • @Lazarus, ich habe deinen Kommentar positiv bewertet. Oh oh! 🙂

    – Alok Singhal

    25. Februar 2010 um 12:42 Uhr

  • @Lazarus die Tatsache, dass es sich um eine Hausaufgabenfrage handelt, hat nichts damit zu tun, dass ich sie interessant finde. Ich habe es auch positiv bewertet, weil ich interessante Hausaufgabenfragen stellen möchte und nicht einfach “Ich habe den Dateipuffer geschlossen, und wenn ich jetzt versuche, aus der Datei zu lesen, funktioniert es nicht. Warum?” (Mit anderen Worten, ich stimme den Fragen zu, auf die ich die Antwort nicht kenne, aber möchte)

    – Yacoby

    25. Februar 2010 um 12:54 Uhr


  • @Alok, LOL – Das waren alles meine eigenen Worte … hilft das, dein Gewissen zu beruhigen? 😉

    – Lazarus

    25. Februar 2010 um 13:11 Uhr

  • Whoa, das ist eine HW-Frage? Ich liebe deinen Lehrer jetzt schon 😀

    – Andreas Gretsch

    25. Februar 2010 um 15:10 Uhr

Die Grundidee besteht darin, die Rückkehradresse der Funktion so zu ändern, dass sie bei Rückkehr der Funktion an einer neuen gehackten Adresse weiter ausgeführt wird. Wie von Nils in einer der Antworten getan, können Sie ein Stück Speicher (normalerweise ein Array) deklarieren und es so überlaufen lassen, dass die Rücksendeadresse ebenfalls überschrieben wird.

Ich würde Ihnen vorschlagen, keines der hier angegebenen Programme blind zu nehmen, ohne tatsächlich zu verstehen, wie sie funktionieren. Dieser Artikel ist sehr gut geschrieben und Sie werden ihn sehr nützlich finden:

Eine Schritt-für-Schritt-Anleitung zur Pufferüberlauf-Schwachstelle

Das ist Compiler-abhängig, daher kann keine einzelne Antwort gegeben werden.

Der folgende Code wird das tun, was Sie für gcc 4.4.1 wollen. Mit deaktivierten Optimierungen kompilieren (wichtig!)

#include <stdio.h>
#include <stdlib.h>

void g()
{
    printf("now inside g()!\n");
}


void f()
{   
  int i;
  void * buffer[1];
  printf("now inside f()!\n");

  // can only modify this section
  // cant call g(), maybe use g (pointer to function)

  // place the address of g all over the stack:
  for (i=0; i<10; i++)
     buffer[i] = (void*) g;

  // and goodbye..
}

int main (int argc, char *argv[])
{
    f();
    return 0;
}

Ausgabe:

nils@doofnase:~$ gcc overflow.c
nils@doofnase:~$ ./a.out
now inside f()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
Segmentation fault

  • Ich verwende gcc 4.4.1 und bin mir nicht sicher, wie ich die Optimierung ausschalten soll: versucht gcc -O0 -o buff buff.c (das ist oh-zero) und auch gcc -O1 -fno-defer-pop -fno-thread- springt -fno-branch-probabilities -fno-cprop-registers -fno-guess-branch-probability -fno-omit-frame-pointer -o buff buff.c hat beides nicht funktioniert.

    – sa125

    25. Februar 2010 um 13:00 Uhr

  • Beenden Sie die Anwendung innerhalb der ‘g()’-Funktion, um einen Segmentierungsfehler zu vermeiden =)

    – Kieveli

    25. Februar 2010 um 13:00 Uhr

  • sa125, vielleicht versucht gcc, auf eine andere CPU-Architektur zu optimieren. Soweit ich weiß, ist es standardmäßig die CPU des Systems, das Sie ausführen. Das kann ändern, wie der Stackframe von f() aussieht, und kann verhindern, dass der Überlauf auftritt.

    – Nils Pipenbrinck

    25. Februar 2010 um 13:06 Uhr

  • Nils – vielleicht bin ich heute Morgen etwas langsam – aber wie kommt es, dass Sie “now inside g()” ausgeben lassen – ich sehe, wo Sie die Zeiger auf g() speichern, aber ich Sehen Sie in Ihrem Beispielcode nicht, wo Sie die Zeiger dereferenzieren und g() aufrufen

    – Dan

    26. Februar 2010 um 14:48 Uhr

  • Dan, f() wird von main aufgerufen. Während des Aufrufs legt der Compiler die Rückkehradresse auf den Stack, damit f() weiß, wohin es springen muss, wenn es fertig ist. Innerhalb von f() überschreibe ich jedoch einen großen Teil des Stacks mit der Adresse von g(). Die Chancen stehen gut, dass ich auch die Rücksendeadresse überschreibe. Wenn also f() beendet wird, kehrt es nicht zu main zurück, sondern springt stattdessen zu g(). Es ist wirklich schmutzig, aber darum ging es in der Frage.

    – Nils Pipenbrinck

    26. Februar 2010 um 15:05 Uhr

Benutzeravatar von jschmier
jschmier

Da dies Hausaufgaben sind, möchte ich wiederholen Codesüchtiger Vorschlag zu verstehen, wie ein Pufferüberlauf tatsächlich funktioniert.

Ich habe die Technik gelernt, indem ich den ausgezeichneten (wenn auch etwas veralteten) Artikel/Tutorial zum Ausnutzen von Pufferüberlauf-Schwachstellen gelesen habe Zerschmettere den Stack für Spaß und Profit.

Probier diese:

void f()
{   
    void *x[1];
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)
    x[-1]=&g;
}

oder dieses:

void f()
{   
    void *x[1];
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)
    x[1]=&g;
}

Benutzeravatar von jschmier
jschmier

Obwohl diese Lösung keine Überlauftechnik verwendet, um die Rückkehradresse der Funktion auf dem Stapel zu überschreiben, verursacht sie dennoch g() von angerufen zu werden f() auf dem Rückweg nach main() nur durch modifizieren f() und nicht anrufen g() direkt.

Epilog der Funktion-ähnliche Inline-Assembly wird hinzugefügt f() um den Wert der Rücksendeadresse auf dem Stack so zu ändern f() wird durch zurückkehren g().

#include <stdio.h>

void g()
{
    printf("now inside g()!\n");
}

void f()
{   
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)

    /* x86 function epilogue-like inline assembly */
    /* Causes f() to return to g() on its way back to main() */
    asm(
        "mov %%ebp,%%esp;"
        "pop %%ebp;"
        "push %0;"
        "ret"
        : /* no output registers */
        : "r" (&g)
        : "%ebp", "%esp"
       );
}

int main (int argc, char *argv[])
{
    f();
    return 0;
}

Wenn Sie verstehen, wie dieser Code funktioniert, können Sie besser verstehen, wie der Stapelrahmen einer Funktion für eine bestimmte Architektur eingerichtet ist, die die Grundlage für Pufferüberlauftechniken bildet.

1386830cookie-checkWie kann ich einen Pufferüberlauf auslösen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy