kein Segmentation fault nach Buffer Overflow?



  • Ich beschäftige mich für meine Facharbeit mit dem Thema Buffer Overflows, hab ansonsten nicht so viel Ahnung von Assembler.

    Als Demonstration hab ich mir ein Programm geschrieben mit dem eine Schwachstelle demonstriert werden soll:

    #include <stdio.h>
    #include <string.h>
    
    void funktion1(char *parameter);
    void funktion2();
    
    int main()
    {
      char string[255];
      scanf("%s",string);
      funktion1(string);
    
     // funktion1("AAAAAAAABBBB\x20\x13\x40\x00");  
      printf("\nPasswort nicht akzeptiert");
      return 0;
    }
    
    void funktion1(char *parameter)
    {
       char buff[8];
       strcpy(buff,parameter);  
    
    }
    
    void funktion2()
    {
       printf("Passwort akzeptiert");
    }
    

    Das funktioniert auch ganz gut, durch die A's wird das Array gefüllt, die B' überschreiben den Saved Frame Pointer im EBP des Registers und die Hexzahlen überschreiben die Rücksprungadresse (Zeile auskommentiert weil das durch ne Benutzereingabe mit entsprechend umgerechneten ASCIIs geschehen soll) mit der Adresse der funktion2.

    Das dumme ist dass nach ausführen der funktion2 das Programm abstürzt. Warum? Meine Vermutung ist dass dies durch den überschreibenen SFP geschieht, so dass der Frame hinterher total kaputt ist..richtig?
    Wenn ja, oder auch wenn nein, wie kann ich das Programm ohne Absturz hinterher weiterlaufen lassen(Rücksprung in main?!?) ?

    Eine weitere Frage die sich mir stellt: Bisher kann ich nur Schwachstellen in eigene Programme einbauen, aber wie finde ich die in anderen? Disassemblieren und nach CALL-Anweisungen mit strcpy etc. suchen oder wie?



  • psycho7765 schrieb:

    Das dumme ist dass nach ausführen der funktion2 das Programm abstürzt. Warum? Meine Vermutung ist dass dies durch den überschreibenen SFP geschieht, so dass der Frame hinterher total kaputt ist..richtig?
    Wenn ja, oder auch wenn nein, wie kann ich das Programm ohne Absturz hinterher weiterlaufen lassen(Rücksprung in main?!?) ?

    Genau das ist das Problem, du springst ja plötzlich z.B. nach Funktion2 zurück. Der SFP würde anzeigen, wo der nächste Stackrahmen ist mit der nächsten Rücksprungadresse, da da aber nur B's drin sind, zeigt das in ungültigen Speicher und damit gibts nen SIGSEGV. Ne Methode dagegen wäre z.B. den SFP nicht zu überschreiben bzw. mit seinem Originalwert.

    Eine weitere Frage die sich mir stellt: Bisher kann ich nur Schwachstellen in eigene Programme einbauen, aber wie finde ich die in anderen? Disassemblieren und nach CALL-Anweisungen mit strcpy etc. suchen oder wie?

    Zum einen gibts einige OpenSource-Programme, da kann man im Source suchen gehen. Ansonsten ist Disassemblieren IMHO nicht immer der erste weg. Ich denke an etwas Binäres geht man mit Trial-and-error ran. Wenn man erst einen String gefunden hat, der nicht gecheckt wird, dann geht man mit Debugger durch und findet die Schwachstellen, dann konstruiert man einen passenden Angriffsstring.



  • Ok danke schonmal.

    Paar Fragen:
    1. Mal angenommen ich hätte nur gezeigtes Programm in kompilierter Form. Wie würde ich jetzt da rangehen? Wie soll ich Debuggen ohne Debuginformationen? Hab übrigens nur nen Windows hier..

    2. Ein entwickeltes Exploit funktioniert ja jedesmal, warum eigentlich? Ich meine ich gebe ja eine absolute Adresse als Rücksprung an, sieht nicht der Speicher immer anders aus 😕 ? Hab gelesen dass einem Prozess zunächst ein virtueller Adressraum zugeordnet wird, hat das was damit zu tun?

    3. Um eigenen Code einzuschläusen benutz man ja Strings ala
    char shellcode[] =
    "\xeb\x40\x5e\x31\xc0\x88\x46\x09\xb0......" /* Assembler Erklärung */
    Wie mache ich eigentlich aus einem Assemlber Listing solche Hexcodes?

    4. Warum wird sowas dann überhaupt im Stack ausgeführt, ich dachte das Programm sitzt im gesicherten Codesegment und im Stack sind nur Variablen etc. 😕

    5. Nochmal zum SFP: Mal angenommen ich überschreibe ihn mit den richtigen Werten, würde ich dann mit Funktion2 nicht im Frame der Mainfunktion landen? Dann müsste das Programm doch eigentlich nach Ende der Funktion normal beendet werden, da der Stack danach leer ist oder wie ist das?



  • psycho7765 schrieb:

    1. Mal angenommen ich hätte nur gezeigtes Programm in kompilierter Form. Wie würde ich jetzt da rangehen? Wie soll ich Debuggen ohne Debuginformationen? Hab übrigens nur nen Windows hier..

    Gucken, welche Eingabestrings das Programm so hat, brutal mit großen Strings in Interssanten Größen füttern, bei Abstürzen den entsprechenden String genauer betrachten, mit nem Debugger gucken, an welcher Adresse das Programm crasht und dort die Umgebung anschauen.

    2. Ein entwickeltes Exploit funktioniert ja jedesmal, warum eigentlich? Ich meine ich gebe ja eine absolute Adresse als Rücksprung an, sieht nicht der Speicher immer anders aus 😕 ? Hab gelesen dass einem Prozess zunächst ein virtueller Adressraum zugeordnet wird, hat das was damit zu tun?

    Nicht jeder Exploit funktioniert immer, der ptrace-Exploit der auf älteren Linux-Kerneln funktionierte z.B. brauchte teilweise etliche Anläufe. Sofern man keinen Server hat sondern z.B. ein kleines Programm, was man immer wieder ausführen kann, kann man passende Adressen z.B. rausfinden, indem man eine mögliche Startadresse ansetzt und dann durchläuft. Allerdings kann man viele Exploits flexibel schreiben, da man ja meistens per Rücksprungadresse in Code, der auf dem Stack liegt springen will, füllt man diesen Code mit vielen NOP-Befehlen vor dem eigentlichen Code aus. Dann braucht man nur IRGENDWO in den NOP-Bereich springen. Außerdem wird durch den virtuellen Adressraum garantiert, dass jedes Programm sich in einem Adressraum aufführen kann, als wäre es alleine. Von daher sind bestimmte Adressen immer wieder gleich und mann kann eine Abschätzung machen, welche Adressen das Ziel sind.

    3. Um eigenen Code einzuschläusen benutz man ja Strings ala
    char shellcode[] =
    "\xeb\x40\x5e\x31\xc0\x88\x46\x09\xb0......" /* Assembler Erklärung */
    Wie mache ich eigentlich aus einem Assemlber Listing solche Hexcodes?

    Das macht jeder Assembler 🙂

    4. Warum wird sowas dann überhaupt im Stack ausgeführt, ich dachte das Programm sitzt im gesicherten Codesegment und im Stack sind nur Variablen etc. 😕

    Das ist der Normalfall. Aber auch einiges an dynamischen Variablen (insbesondere Funktionenlokale variablen) liegen auf dem Stack. Und damit zwangsläufig auch dort der eingeschleuste Code. Intel-Systeme kennen keinen Execute-Schutz für Speicherseiten und von daher bremst keiner den Code. Auf den heutigen großen betriebssystemen werden Segmente nicht mehr benutzt, von daher spielen Code- und Stacksegment (im x86-Sinn) selbst keine große Rolle mehr.

    5. Nochmal zum SFP: Mal angenommen ich überschreibe ihn mit den richtigen Werten, würde ich dann mit Funktion2 nicht im Frame der Mainfunktion landen? Dann müsste das Programm doch eigentlich nach Ende der Funktion normal beendet werden, da der Stack danach leer ist oder wie ist das?

    Sobald du den Stackpointer manipulierst, kannst du ganz beliebige Stackframes anlegen in begrenzt beliebiger Tiefe. Von daher bestimmt man dann auch selbst, was auf dem Stack überhaupt noch liegt.

    Wenns um eine Facharbeit geht oder du dich sonst dafür interessierst empfehle ich das Buch "Buffer Overflows und Format-String-Schwachstellen" von Tobias Klein. Buffer Overflows und Format-String-Schwachstellen | ISBN: 3898641929



  • Ok hast mir schonmal ordentlich geholfen.

    Dieses Buch da hab ich mir übrigens dafür gekauft. 😃 Das waren Fragen die sich mir beim Lesen so gestellt haben..Bin zwar lange noch nicht durch, werde mich aber auch wegen Platzmangels auf Stack-basierte BOs beschränken.
    Das Problem für mich ist nur dass alle Beispiele darin auf unixbasierte Systeme aufbauen.


Anmelden zum Antworten