Overflow minimal: Unterschied zwischen den Versionen

Aus Xinux Wiki
Zur Navigation springen Zur Suche springen
Zeile 31: Zeile 31:
 
== Kompilieren ==
 
== Kompilieren ==
 
Stack-Protector deaktiviert, Debug-Symbole aktiv:
 
Stack-Protector deaktiviert, Debug-Symbole aktiv:
*gcc -fno-stack-protector -z execstack -g -o overflow_minimal overflow_minimal.c
+
* gcc -fno-stack-protector -z execstack -g -o overflow_minimal overflow_minimal.c
 
Analyse mit AddressSanitizer:
 
Analyse mit AddressSanitizer:
*gcc -g -fsanitize=address -fno-omit-frame-pointer -o overflow_minimal_asan overflow_minimal.c
+
* gcc -g -fsanitize=address -fno-omit-frame-pointer -o overflow_minimal_asan overflow_minimal.c
  
 
== Testen ==
 
== Testen ==
 
Normale Eingabe (≤ 6 Zeichen):
 
Normale Eingabe (≤ 6 Zeichen):
*./overflow_minimal ABCDEF
+
* ./overflow_minimal ABCDEF
 
  Ausgabe: Du hast eingegeben: ABCDEF
 
  Ausgabe: Du hast eingegeben: ABCDEF
*echo $?
+
* echo $?
 
  0
 
  0
 
Kurze Überschreitung (7 Zeichen):
 
Kurze Überschreitung (7 Zeichen):
*./overflow_minimal ABCDEFG
+
* ./overflow_minimal ABCDEFG
 
  Du hast eingegeben: ABCDEFG
 
  Du hast eingegeben: ABCDEFG
 
  Segmentation fault
 
  Segmentation fault
*echo $?
+
* echo $?
 
  139
 
  139
  
 
== Analyse mit GDB ==
 
== Analyse mit GDB ==
 
Das GNU Debugger (gdb) ist ein Werkzeug, um den Programmablauf zu verstehen und Fehler (wie hier durch Buffer Overflow) gezielt zu untersuchen.   
 
Das GNU Debugger (gdb) ist ein Werkzeug, um den Programmablauf zu verstehen und Fehler (wie hier durch Buffer Overflow) gezielt zu untersuchen.   
Alle folgenden Schritte dienen der **Analyse**, nicht der Ausnutzung.
+
Alle folgenden Schritte dienen der Analyse, nicht der Ausnutzung.
  
*gdb --args ./overflow_minimal "$(python3 -c 'print("A"*200)')"
+
* gdb --args ./overflow_minimal "$(python3 -c 'print("A"*200)')"
 
   # Startet gdb mit dem Programm und einem 200-Byte-String als Argument.
 
   # Startet gdb mit dem Programm und einem 200-Byte-String als Argument.
   # (gdb) run    → Programm starten
+
* run
   # (gdb) bt    → Backtrace anzeigen (zeigt Aufrufkette zum Absturz)
+
   # Programm starten
   # (gdb) info registers → Registerstatus (RIP, RSP, RBP usw.)
+
* bt
   # (gdb) frame 0 → In das aktuelle Stackframe wechseln
+
   # Backtrace anzeigen (zeigt Aufrufkette zum Absturz)
   # (gdb) info locals → Lokale Variablen (z. B. buffer) anzeigen
+
* info registers
  # (gdb) x/64bx $rsp 64 Bytes Speicher ab Stackpointer anzeigen (in hex)
+
   # Registerstatus (RIP, RSP, RBP usw.)
  # (gdb) x/s $rsp Speicher als String anzeigen
+
* frame 0
   # (gdb) disassemble func → Assemblercode der Funktion anzeigen
+
   # In das aktuelle Stackframe wechseln
 +
* info locals
 +
   # Lokale Variablen (z. B. buffer) anzeigen
 +
* x/64bx $rsp
 +
  # 64 Bytes Speicher ab Stackpointer anzeigen (hex)
 +
* x/s $rsp
 +
  # Speicher als String anzeigen
 +
* disassemble func
 +
   # Assemblercode der Funktion anzeigen
  
 
=== Erklärung der wichtigsten GDB-Befehle ===
 
=== Erklärung der wichtigsten GDB-Befehle ===
* **run**  
+
* run   
   Startet das Programm unter gdb-Kontrolle. Wenn ein Absturz (Segmentation fault) auftritt, stoppt gdb an der Absturzstelle.
+
   Startet das Programm unter gdb-Kontrolle. Stoppt bei Absturz (Segmentation fault).
  
* **bt / backtrace**  
+
* bt   
   Zeigt die komplette Aufrufkette vom main() über func() bis zur Absturzstelle.
+
   Zeigt die komplette Aufrufkette vom main() über func() bis zur Absturzstelle.
  Beispielausgabe: 
 
    ```
 
    #0  0x0000555555555153 in func (input=0x7fffffffe3e8 "AAAA...") at overflow_minimal.c:9
 
    #1  0x00005555555551a2 in main (argc=2, argv=0x7fffffffe2d8) at overflow_minimal.c:18
 
    ```
 
  
* **info registers**  
+
* info registers   
   Zeigt CPU-Register an, z. B.:
+
   Zeigt CPU-Register an, z. B. rip (Instruktionszeiger), rbp/rsp (Stackpointer). Überprüfen, ob der Overflow Rücksprungadressen überschrieben hat.
  - `rip`: Instruktionszeiger (zeigt, wo das Programm gerade war)
 
  - `rbp`/`rsp`: Stack-Frame- und Stackpointer
 
  - `rax`, `rdi`, `rsi`, ...: Datenregister (können Argumente enthalten) 
 
  Wenn durch Overflow `rip` oder `rbp` verändert wurden, sieht man hier die Folgen.
 
  
* **x/64bx $rsp**  
+
* x/64bx $rsp   
   Dump des Stackinhalts (64 Bytes, Byteweise, hex).
+
   Dump des Stackinhalts (64 Bytes, Byteweise). Praktisch, um zu sehen, wie der Eingabestring den Stack überschreibt.
  Praktisch, um zu sehen, ob der Eingabestring („AAAA...“) den Stack überschrieben hat.
 
  
* **x/s $rsp**  
+
* x/s $rsp   
   Zeigt Speicher als String an. Bei Overflows erkennt man hier oft endlose „A“-Zeichen.
+
   Speicher als String anzeigen. Hier erkennt man oft endlose "A"-Zeichen bei Overflow.
  
* **info frame**  
+
* info frame   
   Zeigt, wie groß das aktuelle Stackframe ist, welche Adresse die Rücksprungadresse enthält und wo lokale Variablen liegen. 
+
   Zeigt Stackframe-Größe, Rücksprungadresse und Lage der lokalen Variablen.
  Hilfreich, um zu verstehen, wie der Puffer im Stack eingebettet ist.
 
  
* **disassemble func**  
+
* disassemble func   
   Zeigt die CPU-Instruktionen der Funktion `func`.
+
   Zeigt CPU-Instruktionen der Funktion func. Hilfreich, um den Absturzpunkt zu lokalisieren.
  So kann man nachvollziehen, wo genau der Absturz auftritt – z. B. an einem Return, dessen Adresse überschrieben wurde.
 
  
* **break func / step / next**  
+
* break func   
   Mit `break func` setzt du einen Breakpoint, um den Ablauf zu stoppen, bevor die gefährliche Stelle ausgeführt wird.
+
   Setzt einen Breakpoint vor der gefährlichen Stelle.
  - `next` führt die nächste Zeile aus (überspringt Funktionsaufrufe) 
 
  - `step` betritt Funktionsaufrufe 
 
  - `finish` lässt die Funktion bis zum Ende laufen
 
  
* **info locals / info args**  
+
* next / step / finish  
   Zeigt lokale Variablen (z. B. `buffer`) und Funktionsargumente an. 
+
   next: nächste Zeile ausführen, step: Funktionsaufruf betreten, finish: Funktion bis Ende ausführen.
  Bei aktivem Debugging kannst du hier prüfen, ob der Puffer korrekt befüllt wurde.
 
  
* **print buffer**  
+
* info locals / info args  
   Gibt den Inhalt von Variablen aus (wenn Debug-Symbole vorhanden). 
+
   Zeigt lokale Variablen und Funktionsargumente an, z. B. den Inhalt von buffer.
  Beispiel: `(gdb) print buffer` → `"AAAAAA"` oder unlesbare Zeichen bei Overflow.
 
  
* **x/32x $rbp-0x20**  
+
* print buffer 
   Zeigt den Stackbereich unterhalb des Framepointers, wo der lokale Puffer liegt. 
+
  Inhalt von Variablen anzeigen.
  So erkennt man visuell, wie viele Bytes überschrieben wurden.
+
 
 +
* x/32x $rbp-0x20   
 +
   Zeigt Stackbereich unterhalb des Framepointers, um zu sehen, wie viele Bytes überschrieben wurden.
  
 
=== Beispielanalyse ===
 
=== Beispielanalyse ===
*gdb --args ./overflow_minimal "$(python3 -c 'print("A"*40)')"
+
* gdb --args ./overflow_minimal "$(python3 -c 'print("A"*40)')"
  (gdb) run
+
* run
 
   Program received signal SIGSEGV, Segmentation fault.
 
   Program received signal SIGSEGV, Segmentation fault.
  (gdb) bt
+
* bt
 
   #0  func (input=0x7fffffffde80 "AAAAAAAAAAAA...") at overflow_minimal.c:9
 
   #0  func (input=0x7fffffffde80 "AAAAAAAAAAAA...") at overflow_minimal.c:9
 
   #1  0x00005555555551a2 in main (argc=2, argv=0x7fffffffe2d8) at overflow_minimal.c:18
 
   #1  0x00005555555551a2 in main (argc=2, argv=0x7fffffffe2d8) at overflow_minimal.c:18
  (gdb) info registers
+
* info registers
 
   rip = 0x4141414141414141  ← Rücksprungadresse durch "AAAA" überschrieben
 
   rip = 0x4141414141414141  ← Rücksprungadresse durch "AAAA" überschrieben
  (gdb) x/32bx $rsp
+
* x/32bx $rsp
 
   0x7fffffffdc80: 0x41 0x41 0x41 0x41 ...
 
   0x7fffffffdc80: 0x41 0x41 0x41 0x41 ...
 
   → Der Stack wurde mit 0x41 (ASCII 'A') gefüllt – typisches Overflow-Symptom.
 
   → Der Stack wurde mit 0x41 (ASCII 'A') gefüllt – typisches Overflow-Symptom.
  
 
=== Weitere nützliche Befehle ===
 
=== Weitere nützliche Befehle ===
  *x/200bc $rsp      → zeigt 200 Bytes als char   
+
* x/200bc $rsp      → zeigt 200 Bytes als char   
  *x/10i $rip        → zeigt 10 Instruktionen ab aktueller Codeadresse   
+
* x/10i $rip        → zeigt 10 Instruktionen ab aktueller Codeadresse   
  *info proc mappings → zeigt Speicherbereiche (Text, Stack, Heap)   
+
* info proc mappings → zeigt Speicherbereiche (Text, Stack, Heap)   
  *quit              → gdb beenden
+
* quit              → gdb beenden
  
 
== Sicherer Fix (Kontrast) ==
 
== Sicherer Fix (Kontrast) ==

Version vom 3. November 2025, 20:21 Uhr

Buffer Overflow: Die unsichtbare Gefahr in 6 Bytes

Kurzbeschreibung

Dieses Lehrbeispiel zeigt, wie ein unsicherer Kopiervorgang in C bei einem 6-Byte-Puffer zu einem Stack-Buffer-Overflow und damit zu einem Absturz führen kann. Nur Analyse/Lehre in einer isolierten VM. Kein Exploit.

Voraussetzungen

Debian/Ubuntu oder eine ähnliche Linux-Distribution mit gcc, gdb und python3.

Beispielcode (kopierbar)

  • cat overflow_minimal.c
 /* overflow_minimal.c - Buffer Overflow Demonstration */
 #include <stdio.h>
 #include <string.h>
 void func(char *input)
 {
     char buffer[6];   // Nur 6 Bytes Puffer (einschließlich NUL)
     strcpy(buffer, input);  // Absichtlich unsicher: keine Längenprüfung
     printf("Du hast eingegeben: %s\n", buffer);
 }

 int main(int argc, char *argv[])
 {
     if (argc < 2) {
         printf("Usage: %s <input>\n", argv[0]);
         return 1;
     }
     func(argv[1]);
     return 0;
 }

Kompilieren

Stack-Protector deaktiviert, Debug-Symbole aktiv:

  • gcc -fno-stack-protector -z execstack -g -o overflow_minimal overflow_minimal.c

Analyse mit AddressSanitizer:

  • gcc -g -fsanitize=address -fno-omit-frame-pointer -o overflow_minimal_asan overflow_minimal.c

Testen

Normale Eingabe (≤ 6 Zeichen):

  • ./overflow_minimal ABCDEF
Ausgabe: Du hast eingegeben: ABCDEF
  • echo $?
0

Kurze Überschreitung (7 Zeichen):

  • ./overflow_minimal ABCDEFG
Du hast eingegeben: ABCDEFG
Segmentation fault
  • echo $?
139

Analyse mit GDB

Das GNU Debugger (gdb) ist ein Werkzeug, um den Programmablauf zu verstehen und Fehler (wie hier durch Buffer Overflow) gezielt zu untersuchen. Alle folgenden Schritte dienen der Analyse, nicht der Ausnutzung.

  • gdb --args ./overflow_minimal "$(python3 -c 'print("A"*200)')"
 # Startet gdb mit dem Programm und einem 200-Byte-String als Argument.
  • run
 # Programm starten
  • bt
 # Backtrace anzeigen (zeigt Aufrufkette zum Absturz)
  • info registers
 # Registerstatus (RIP, RSP, RBP usw.)
  • frame 0
 # In das aktuelle Stackframe wechseln
  • info locals
 # Lokale Variablen (z. B. buffer) anzeigen
  • x/64bx $rsp
 # 64 Bytes Speicher ab Stackpointer anzeigen (hex)
  • x/s $rsp
 # Speicher als String anzeigen
  • disassemble func
 # Assemblercode der Funktion anzeigen

Erklärung der wichtigsten GDB-Befehle

  • run
 Startet das Programm unter gdb-Kontrolle. Stoppt bei Absturz (Segmentation fault).
  • bt
 Zeigt die komplette Aufrufkette vom main() über func() bis zur Absturzstelle.
  • info registers
 Zeigt CPU-Register an, z. B. rip (Instruktionszeiger), rbp/rsp (Stackpointer). Überprüfen, ob der Overflow Rücksprungadressen überschrieben hat.
  • x/64bx $rsp
 Dump des Stackinhalts (64 Bytes, Byteweise). Praktisch, um zu sehen, wie der Eingabestring den Stack überschreibt.
  • x/s $rsp
 Speicher als String anzeigen. Hier erkennt man oft endlose "A"-Zeichen bei Overflow.
  • info frame
 Zeigt Stackframe-Größe, Rücksprungadresse und Lage der lokalen Variablen.
  • disassemble func
 Zeigt CPU-Instruktionen der Funktion func. Hilfreich, um den Absturzpunkt zu lokalisieren.
  • break func
 Setzt einen Breakpoint vor der gefährlichen Stelle.
  • next / step / finish
 next: nächste Zeile ausführen, step: Funktionsaufruf betreten, finish: Funktion bis Ende ausführen.
  • info locals / info args
 Zeigt lokale Variablen und Funktionsargumente an, z. B. den Inhalt von buffer.
  • print buffer
 Inhalt von Variablen anzeigen.
  • x/32x $rbp-0x20
 Zeigt Stackbereich unterhalb des Framepointers, um zu sehen, wie viele Bytes überschrieben wurden.

Beispielanalyse

  • gdb --args ./overflow_minimal "$(python3 -c 'print("A"*40)')"
  • run
 Program received signal SIGSEGV, Segmentation fault.
  • bt
  #0  func (input=0x7fffffffde80 "AAAAAAAAAAAA...") at overflow_minimal.c:9
  #1  0x00005555555551a2 in main (argc=2, argv=0x7fffffffe2d8) at overflow_minimal.c:18
  • info registers
  rip = 0x4141414141414141  ← Rücksprungadresse durch "AAAA" überschrieben
  • x/32bx $rsp
  0x7fffffffdc80: 0x41 0x41 0x41 0x41 ...
 → Der Stack wurde mit 0x41 (ASCII 'A') gefüllt – typisches Overflow-Symptom.

Weitere nützliche Befehle

  • x/200bc $rsp → zeigt 200 Bytes als char
  • x/10i $rip → zeigt 10 Instruktionen ab aktueller Codeadresse
  • info proc mappings → zeigt Speicherbereiche (Text, Stack, Heap)
  • quit → gdb beenden

Sicherer Fix (Kontrast)

 void func_safe(char *input)
 {
     char buffer[6];
     snprintf(buffer, sizeof buffer, "%s", input);
     printf("Du hast eingegeben: %s\n", buffer);
 }

Fazit

  • Schon minimale Programmierfehler können massive Sicherheits- oder Stabilitätsprobleme verursachen.
  • Sorgfalt bei Puffergrößen, Eingabevalidierung und das Nutzen moderner Schutzmechanismen sind Pflicht in der sicheren Softwareentwicklung.
  • gdb ist ein starkes Werkzeug, um den Ablauf zu verstehen: Stack-Frames, Register und Speicher lassen sich genau untersuchen.
  • AddressSanitizer ist die sichere Alternative für automatische Overflow-Erkennung.