2013-12-18 8 views
10

ich mit Pufferüberlauf bin experimentieren und versuchen, die Absenderadresse des Stapels mit einem bestimmten Eingang von fgetseinen Pufferüberlaufes mit fgets Verursachung

Dies ist der Code zu überschreiben:

void foo() 
{ 
    fprintf(stderr, "You did it.\n"); 
} 

void bar() 
{ 
    char buf[20]; 
    puts("Input:"); 
    fgets(buf, 24, stdin); 
    printf("Your input:.\n", strlen(buf)); 
} 


int main(int argc, char **argv) 
{ 
    bar(); 
    return 0; 
} 

Auf einem normale Ausführung gibt das Programm nur Ihre Eingabe zurück. Ich möchte, dass es foo() ausgibt, ohne den Code zu ändern.

Meine Idee war, den Puffer von buf durch Eingabe von 20 'A' s überlaufen. Dies funktioniert und verursacht einen Segmentierungsfehler. Meine nächste Idee war es, die Adresse foo() zu finden, die \x4006cd ist und diese an die 20 'A' s anhängen.

Aus meinem Verständnis sollte dies die Rücksprungadresse des Stapels überschreiben und es zu foo springen lassen. Aber es verursacht nur einen segfault.

Was mache ich falsch?

Update: Assembler-Dumps Haupt

Dump of assembler code for function main: 
    0x000000000040073b <+0>: push %rbp 
    0x000000000040073c <+1>: mov %rsp,%rbp 
    0x000000000040073f <+4>: sub $0x10,%rsp 
    0x0000000000400743 <+8>: mov %edi,-0x4(%rbp) 
    0x0000000000400746 <+11>: mov %rsi,-0x10(%rbp) 
    0x000000000040074a <+15>: mov $0x0,%eax 
    0x000000000040074f <+20>: callq 0x4006f1 <bar> 
    0x0000000000400754 <+25>: mov $0x0,%eax 
    0x0000000000400759 <+30>: leaveq 
    0x000000000040075a <+31>: retq 
    End of assembler dump. 

foo

Dump of assembler code for function foo: 
    0x00000000004006cd <+0>: push %rbp 
    0x00000000004006ce <+1>: mov %rsp,%rbp 
    0x00000000004006d1 <+4>: mov 0x200990(%rip),%rax  # 0x601068 <[email protected]@GLIBC_2.2.5> 
    0x00000000004006d8 <+11>: mov %rax,%rcx 
    0x00000000004006db <+14>: mov $0x15,%edx 
    0x00000000004006e0 <+19>: mov $0x1,%esi 
    0x00000000004006e5 <+24>: mov $0x400804,%edi 
    0x00000000004006ea <+29>: callq 0x4005d0 <[email protected]> 
    0x00000000004006ef <+34>: pop %rbp 
    0x00000000004006f0 <+35>: retq 
End of assembler dump. 

bar:

Dump of assembler code for function bar: 
    0x00000000004006f1 <+0>: push %rbp 
    0x00000000004006f2 <+1>: mov %rsp,%rbp 
    0x00000000004006f5 <+4>: sub $0x20,%rsp 
    0x00000000004006f9 <+8>: mov $0x40081a,%edi 
    0x00000000004006fe <+13>: callq 0x400570 <[email protected]> 
    0x0000000000400703 <+18>: mov 0x200956(%rip),%rdx  # 0x601060 <[email protected]@GLIBC_2.2.5> 
    0x000000000040070a <+25>: lea -0x20(%rbp),%rax 
    0x000000000040070e <+29>: mov $0x18,%esi 
    0x0000000000400713 <+34>: mov %rax,%rdi 
    0x0000000000400716 <+37>: callq 0x4005b0 <[email protected]> 
    0x000000000040071b <+42>: lea -0x20(%rbp),%rax 
    0x000000000040071f <+46>: mov %rax,%rdi 
    0x0000000000400722 <+49>: callq 0x400580 <[email protected]> 
    0x0000000000400727 <+54>: mov %rax,%rsi 
    0x000000000040072a <+57>: mov $0x400821,%edi 
    0x000000000040072f <+62>: mov $0x0,%eax 
    0x0000000000400734 <+67>: callq 0x400590 <[email protected]> 
    0x0000000000400739 <+72>: leaveq 
    0x000000000040073a <+73>: retq 
End of assembler dump. 
+0

Haben Sie sich den entsprechenden Assembler (und damit Stacklayout) für diesen Code angeschaut? –

+0

Wenn es nur so einfach wäre ... –

+0

Sie sollten sich die entsprechende Baugruppe ansehen. Manchmal führt der Compiler mehr als 20 Bytes aus, die Sie für die Ausrichtung erstellt haben. Sie sollten auch das Layout des Stacks betrachten und wie Register/gespeicherte ret addrs in welcher Reihenfolge gespeichert werden. Machen Sie weiter und buchen Sie die Montage der Funktion und wir können Ihnen weiterhelfen. –

Antwort

3

Sie nicht mit dem Gedächtnis aligment zählte. Ich habe den Code ein wenig geändert, um es einfacher zu machen, den richtigen Ort zu finden.

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

int **x; 
int z; 

void foo() 
{ 
    fprintf(stderr, "You did it.\n"); 
} 

void bar() 
{ 
    char buf[2]; 
    //puts("Input:"); 
    //fgets(buf, 70, stdin); 
    x = (int**) buf; 
    for(z=0;z<8;z++) 
      printf("%d X=%x\n", z, *(x+z)); 
    *(x+3) = foo; 
    printf("Your input: %d %s\n", strlen(buf), buf); 
} 


int main(int argc, char **argv) 
{ 
     printf("Foo: %x\n", foo); 
     printf("Main: %x\n", main); 
     bar(); 
     return 0; 
} 

Mit einem kleineren Puffer, 2 in meinem Beispiel, fand ich die Rücksendeadresse 24 Bytes weg (x + 3, für die 8-Byte-Zeiger; 64 Bits, keine debug, keine Optimierung ...) vom Beginn des Puffers. Diese Position kann sich je nach Puffergröße, Architektur usw. ändern. In diesem Beispiel kann ich die Rücksprungadresse von bar in foo ändern. Wie auch immer, Sie werden bei foo return einen Segmentierungsfehler bekommen, da er nicht richtig auf main gesetzt wurde.

Ich habe x und z als globale Variablen hinzugefügt, um die Stapelgröße von Balken nicht zu ändern. Der Code zeigt ein Array von zeigerartigen Werten an, beginnend mit buf [0]. In meinem Fall habe ich die Adresse in main in der Position 3 gefunden. Deshalb hat der endgültige Code * (x + 3) = foo. Wie gesagt, diese Position kann sich je nach den Kompilierungsoptionen, der Maschine usw. ändern. Um die richtige Position zu finden, suchen Sie die Adresse von main (gedruckt vor dem Anruf) in der Adressliste.

Es ist wichtig zu beachten, dass ich Adresse in main, nicht die Adresse von main sagte, weil die Absenderadresse auf die Zeile nach dem Aufruf von bar und nicht auf den Anfang von main gesetzt wurde. Also, in meinem Fall war es 0x4006af statt 0x400668.

In Ihrem Beispiel mit 20 Bytes Puffer wurde, soweit ich weiß, auf 32 Bytes (0x20) ausgerichtet.

Wenn Sie das gleiche mit fgets tun möchten, müssen Sie herausfinden, wie die Adresse von foo eingeben, aber wenn Sie eine x86/x64-Maschine ausführen, denken Sie daran, es in Little Enddian hinzuzufügen. Sie können den Code ändern, um die Werte Byte für Byte anzuzeigen, damit Sie sie in der richtigen Reihenfolge abrufen und sie mit ALT + Nummer eingeben können. Denken Sie daran, dass die Zahlen, die Sie bei gedrückter ALT-Taste eingeben, Dezimalzahlen sind. Einige Terminals werden nicht freundlich behandelt 0x00.

Meine Ausgabe wie folgt aussieht:

$ gcc test.c -o test 
test.c: In function ‘bar’: 
test.c:21: warning: assignment from incompatible pointer type 
$ ./test 
Foo: 400594 
Main: 400668 
0 X=9560e9f0 
1 X=95821188 
2 X=889350f0 
3 X=4006af 
4 X=889351d8 
5 X=0 
6 X=0 
7 X=95a1ed1d 
Your input: 5 ▒▒`▒9 
You did it. 
Segmentation fault 
2
void bar() 
{ 
    char buf[20]; 
    puts("Input:"); 
    fgets(buf, 24, stdin); 
    printf("Your input:.\n", strlen(buf)); 
} 

... Dies funktioniert und erzeugt einen Segmentation Fault ...

Der Compiler wahrscheinlich fgets mit einer sichereren Variante ersetzt Dazu gehört eine Überprüfung der Zielpuffergröße. Wenn die Prüfung fehlschlägt, ruft das Programm unbedingt abort() auf. In diesem speziellen Fall sollten Sie das Programm mit -U_FORTIFY_SOURCE oder -D_FORTIFY_SOURCE=0 kompilieren.