Vermeiden von Sicherheitsl�chern beim Entwickeln einer Applikation - Teil 1

ArticleCategory:

Software Development

AuthorImage:

[image of the authors]

TranslationInfo:

Original in fr Frédéric Raynal, Christophe Blaess, Christophe Grenier

fr to en Georges Tarbouriech

en to de Guido Socher

AboutTheAuthor:

Christophe Blaess ist ein unabh�ngiger Raumfahrtingenieur. Er ist ein Linuxfan und arbeitet die meiste Zeit mit diesem System. Er koordiniert die �bersetzung der Man-pages die vom Linux Dokumentationsprojekt ver�ffentlicht werden.

Christophe Grenier ist Student im f�nften Semester an der ESIEA, wo er auch als Systemadministrator arbeitet. Er hat eine Leidenschaft f�r Computersicherheit.

Frédéric Raynal benutzt Linux, weil er mit Software, die keine Patente enth�lt, arbeiten m�chte. Er geht gerne ins Kino. Dancer in the Dark ist ein guter Film.

Abstract

Dies ist der erste Artikel in einer Serie von Artikeln �ber Sicherheitsl�cher, die beim Entwickeln von Software entstehen k�nnen. Diese Artikel werden zeigen, wie man Sicherheitsprobleme vermeiden kann, indem man seine Programmiergewohnheiten ein wenig �ndert.

ArticleIllustration

[article illustartion]

ArticleBody

Einf�hrung

Im allgemeinen dauert es nicht l�nger als zwei Wochen, bis wieder ein Sicherheitsloch in einer gr��eren Anwendung auftaucht. Ein Sicherheitsloch, das es z.B dem auf dem Rechner eingeloggten Benutzern erlaubt, root zu werden. Trotz der hervorragenden Qualit�t der meisten dieser Applikationen scheint es immer sehr schwierig zu sein, sichere Programme zu schreiben. Ein sicheres Programm gibt einem Benutzer mit b�sen oder kriminellen Absichten keine M�glichkeit, sich zu Systemteilen Zugang zu verschaffen, zu denen er keinen Zugang haben soll. Die Verf�gbarkeit von Quellcode ist eine gute Sache und wird von vielen Programmierern sehr gesch�tzt, aber der kleinste Fehler in einem Programm wird damit sichtbar f�r jedermann. Diese Fehler werden oft zuf�llig gefunden und Leute, die nach solchen Fehlern suchen, haben nicht immer gute Absichten.

F�r einen Systemadministrator besteht die t�gliche Arbeit darin, Newsgruppen und Webseiten, auf denen Sicherheitsl�cher ver�ffentlicht werden, zu studieren und f�r die entsprechenden Pakete einen Update einzuspielen. Der Programmierer kann durch die Studie dieser Informationen viel lernen, speziell wenn er selbst versucht, an seinem Rechner solche Sicherheitsl�cher auszuprobieren. Egal wie schnell ein Sicherheitsproblem gefunden wird, ist es immer besser, Sicherheitsprobleme von vornherein zu vermeiden. Wir werden hier einige klassische Fehler besprechen, und zeigen L�sungen f�r diese Probleme. Wir werden keine Sicherheitsprobleme besprechen, die speziell in Netzwerken auftreten, da diese meist Konfigurationsfehler sind. Systemfehler, die DOS (Denial Of Service) Angriffe zulassen. Diese Probleme betreffen den Systemadministrator oder den Kernelentwickler, aber manchmal auch Applikationsprogrammierer. Speziell dann, wenn diese Programme auf Daten zugreifen, die nicht unbedingt vertrauensw�rdig sind. Typische Programme in dieser Klasse sind pine, acroread, netscape, access,... Einige von diesen Programmen erlaubten es, Informationen auszuspionieren oder direkten Zugriff auf das System zu erhalten. Es ist eine Tatsache, da� sicheres Programmieren einfach jeden betrifft.

Diese Artikelserie zeigt Methoden, die benutzt werden k�nnen, um ein Unixsystem zu besch�digen. Wir h�tten diese Probleme einfach auflisten k�nnen, aber wir bevorzugen offene und klare Erkl�rungen, damit alle die Mechanismen verstehen. Nach dieser Serie solltest du in der Lage sein, Sicherheitsfehler zu beheben und zu vermeiden. F�r jedes Sicherheitsloch werden wir denselben Ansatz w�hlen. Wir erkl�ren, wie sie funktionieren und dann zeigen wir, wie man sie vermeidet.

Dieser erste Artikel erkl�rt grundlegende Sicherheitsmechanismen. Wir zeigen, wie man Privilegien durch den Mi�brauch von Set-UID und Set-GID erhalten kann. Als n�chstes analysieren wir die bekannte C Funktion system() und zeigen die Sicherheitsl�cher, die es in dieser Funktion gibt.

Wir werden h�ufig die Problematik anhand kleiner C Programme aufzeigen, aber diese Problematiken lassen sich auf andere Sprachen �bertragen: perl, java, shell scripts... Einige Probleme sind spezifisch f�r eine Sprache, aber das ist nicht immer so, wie wir anhand der Funktion system() sehen werden.

Privilegien

Auf einem Unixsytem sind die Benutzer nicht alle gleich. Sie haben nicht die gleichen Rechte und das gilt auch f�r Applikationen. Der Zugriff auf das Dateisystem und die Peripherie einer Maschine unterliegt einer strikten Identit�tskontrolle. Einige Benutzer d�rfen empfindliche Operationen ausf�hren, um das System in gutem Zustand zu halten, andere haben diese Rechte nicht. Eine Nummer, die als UID (User Identifier) bezeichnet wird, wird f�r diese Identit�tskontrolle benutzt. Um die Sache einfacher zu machen, gibt es einen Namen, der dieser Nummer entspricht und die Assoziation zwischen Namen und Nummer erfolgt �ber die Datei /etc/passwd.

Der Benutzer root, mit der UID 0, kann auf alles im System zugreifen. Er kann nicht nur auf alle Dateien zugreifen, er kann auch die physikalische Konfiguration einer Maschine �ndern. Er kann Partitionen mounten, Netzwerk Interfaces aktivieren, IP Adressen �ndern oder Systemaufrufe wie mmlock() benutzen, um auf den physikalischen Speicher zuzugreifen. In zuk�nftigen Artikeln werden wir die M�glichkeiten studieren, die Posix.1e bietet, um die Privilegien von Programmen, die mit root Rechten laufen, zu limitieren. F�r den Augenblick nehmen wir jedoch an, da� der Benutzer root alles kann.

Die Attacken, die wir besprechen, sind interne Attacken. Ein eingeloggter und authentifizierter Benutzer versucht, Privilegien zu erlangen, die er eigentlich nicht hat. Weiterhin gibt es Netzwerkangriffe, externe Angriffe, bei denen Leute versuchen, Verbindungen aufzubauen, die sie eigentlich nicht aufbauen d�rfen. Wenn man die Privilegien eines anderen Benutzers erhalten hat, bedeutet das, da� alles unter seinem Namen, seiner UID, gemacht wird und nicht unter der urspr�nglichen UID. Nat�rlich wird ein Angreifer versuchen, die ID von root zu erhalten, aber auch andere Benutzer sind von Interesse, weil man damit auf Informationen zugreifen kann, (news, mail, lp...) und man damit gesch�tzte Daten (Briefe, pers�nliche Dateien, etc) erhalten kann. Au�erdem werden sie benutzt, um illegale Aktivit�ten gegen�ber anderen zu verstecken.

Um die Privilegien, die f�r einen anderen Benutzer reserviert sind, zu nutzen, ohne als dieser Benutzer eingeloggt zu sein, mu� man zumindest die M�glichkeit haben, mit einer Applikation zu kommunizieren, die unter der UID des Opfers l�uft. Wenn eine Applikation --ein Proze�-- unter Linux l�uft, dann hat diese eine klar definierte Identit�t. Zun�chst hat ein Programm ein Attribut namens RUID (Real UID), die der UID (Benutzer Identit�t) des Benutzers entspricht, der das Programm gestartet hat. Diese Daten werden vom Kernel verwaltet und k�nnen sich normalerweise nicht �ndern. Es gibt noch ein weiteres Attribut, die EUID (Effective UID). Die EUID wird herangezogen, wenn der Kernel Zugriffsrechte regelt (beim �ffnen von Dateien, Benutzung spezieller Systemaufrufe ...).

Um eine Applikation mit einer Effective UID (bestimmte Privilegien), die anders als die Real UID (Benutzer, der das Programm startete) laufen zu lassen ist, mu� ein spezielles Zugriffsrechte-Bit namens Set-UID (wird mit chmod gesetzt) gesetzt sein. Dieses Bit befindet sich in dem Datei Permission Attribut. Es hat den oktalen Wert 4000. Das Set-UID Bit wird als ein s dargestellt, wenn man sich die Zugriffsrechte mit dem Befehl ls anzeigen l��t:

>> ls -l /bin/su
-rwsr-xr-x  1 root  root  14124 Aug 18  1999 /bin/su
>> 
 
Der Befehl "find / -type f -perm +4000" zeigt alle Applikationen im Dateisystem, die das Set-UID Bit gesetzt haben. Wenn der Kernel ein Programm, das dieses Set-UID Bit gesetzt hat, startet, dann benutzt er als EUID die UID des Benutzers, dem die Datei geh�rt. Die RUID �ndert sich nicht und entspricht weiterhin dem Benutzer, der das Programm startete. Der Befehl /bin/su benutzt z.B diese Eigenschaft. Jeder Benutzer kann den Befehl /bin/su starten, aber er l�uft mit der UID des Eigent�mers (root). Es braucht wohl nicht weiter betont zu werden, da� solch ein Programm sorgf�ltig programmiert werden mu�.

Jeder Proze� hat auch eine effektive Group ID, EGID, und eine real group ID , RGID. Das Set-GID (oktal 2000 ) regelt die Gruppenzugriffsrechte, wenn ein Programm gestartet wird. Eine merkw�rdige Kombination entsteht, wenn das Set-GID Bit gesetzt ist, ohne da� die Datei ein execute Bit gesetzt hat. Dies ist eine Konvention, die nichts mit den Privilegien einer Applikation zu tun hat, sondern eine Datei, die mit der Funktion fcntl(fd, F_SETLK, lock) geblockt werden kann. Normalerweise benutzen Applikationen diese Set-GID bit nicht. Einige Spiele benutzen es z.B um Highscores systemweit zu speichern.

Typen von Angriffen und m�gliche Ziele

Es gibt verschiedene Typen von Angriffen gegen ein System. Heute werden wir die Mechanismen studieren, mit dem ein Angreifer einen beliebigen Befehl aus einer Applikation heraus starten kann. Dieser Befehl ist normalerweise die Shell, die dann unter der UID der Applikation l�uft. Eine zweite Art von Angriff ist ein buffer overflow. Dieser gibt dem Angreifer die M�glichkeit, beliebigen Maschinencode auszuf�hren. Ein dritter Typ eines Angriffs basiert auf race conditions. Es wird die Zeit genutzt, die zwischen dem Ausf�hren verschiedener Codest�cke vergeht. In dieser Zeit wird irgendein Teil des Systems ver�ndert (normalerweise eine Datei), w�hrend die Applikation denkt, es sei gleichgeblieben.

Die zwei ersten Arten von Angriffen versuchen, die Shell mit den Privilegien des Eigent�mers eines Programmes auszuf�hren. Der dritte Type hingegen versucht, Zugriff auf gesch�tzte Systemdateien zu erhalten. Auch der Lesezugriff auf bestimmte Dateien wie z.B /etc/shadow ist ein Sicherheitsrisiko.

Die Ziele eines Angriffs auf die Systemsicherheit sind meist Programme, die das Set-UID (oder Set-GID) Bit gesetzt haben. Das betrifft auch andere Applikationen, die nicht unter der UID ihrer Benutzer laufen. Die System daemons (Server Prozesse) repr�sentieren einen gro�en Teil dieser Art von Programmen. Ein daemon wird im allgemeinen beim Booten gestartet und l�uft im Hintergrund. lpd, z.B erlaubt es jedem Benutzer Dokumente an den Drucker zu schicken. sendmail empf�ngt und verschickt E-Mail, oder apmd, der das Bios nach dem Status der Batterie befragt (l�uft meist auf Laptops). Einige daemons kommunizieren auch mit externen Benutzern �ber das Netzwerk (Ftp, Http, Telnet... ). Ein Proze� namens inetd verwaltet Netzwerkverbindungen.

Zusammenfassend k�nnen wir feststellen, da� ein Programm angegriffen werden kann, sobald es mit einem Benutzer ungleich dem Benutzer der es gestartet hat, kommunizieren kann. Wenn es in der Natur des Designs eine Applikation liegt, so etwas zu tun, dann mu� man sehr sorgf�ltig programmieren.

Privilegien �ndern

Eine Applikation l�uft normalerweise mit einer EUID ungleich der RUID, um dem Benutzer gezielten Zugriff auf Privilegien zu geben, die er normalerweise nicht hat (Dateizugriff, reservierte Systemaufrufe). Im allgemeinen wird das jedoch nur punktuell ben�tigt, zum Beispiel beim �ffnen einer Datei, ansonsten ist die Applikation in der Lage, mit den Rechten des Benutzers, der sie gestartet hat, auszukommen. Man kann die EUID tempor�r mit dem Befehl seteuid �ndern:

  int seteuid (uid_t uid);
Eine Applikation kann immer den Wert der EUID so �ndern, da� er der RUID entspricht. In diesem Fall wird die alte UID gespeichert in einem Feld namens SUID (Saved UID). Es ist m�glich, die SUID zur�ckzuerhalten und als EUID zu benutzen. Nat�rlich kann ein Programm mit der EUID Null (root) sowohl die EUID als auch die RUID beliebig �ndern (Das Programm /bin/su funktioniert so).

Um das Risiko eines Angriffs zu reduzieren wird vorgeschlagen, die EUID zu �ndern und die RUID zu benutzen, wenn gerade keine speziellen Privilegien gebraucht werden. Wenn Privilegien gebraucht werden, schreibt man die Saved UID wieder in die EUID. Hier ist ein Beispiel:

  
  uid_t e_uid_initial;
  uid_t r_uid;
    
  int
  main (int argc, char * argv [])
  {
    /* Saves the different UIDs */
    e_uid_initial = geteuid ();
    r_uid = getuid ();

    /* limits access rights to the ones of the 
     * user launching the program */
    seteuid (r_uid);
    ...
    privileged_function ();
    ...
  }
  
  void
  privileged_function (void)
  {
    /* Gets initial privileges back */
    seteuid (e_uid_initial);
    ...
    /* Portion needing privileges */
    ...
    /* Back to the rights of the runner */
    seteuid (r_uid);
  }  
 

Diese Strategie ist besser als die oft gesehene andersherum arbeitende Strategie, bei der die EUID tempor�r zur RUID gesetzt wird, bevor man "riskante" Programmst�cke ausf�hrt. Diese Reduzierung der Privilegien ist jedoch nutzlos gegen einen Buffer overflow Angriff. Das werden wir im n�chsten Artikel sehen. Bei einem Buffer overflow wird beliebiger Code ausgef�hrt und dieser kann die Anweisungen enthalten, um die EUID zu ver�ndern. Trotzdem hilft dieses nur punktuelle Setzen der Privilegien gegen das beliebige Ausf�hren einiger Befehle und gegen die meisten Race Conditions gut.

Beliebige externe Befehle ausf�hren

Eine Applikation mu� oft einen externen Befehl aufrufen. Ein bekanntes Beispiel ist der Befehl mail, um ein Mail zu verschicken ( einen Alarm zu melden oder einfach Statistiken zu schicken). Die einfachste L�sung, um das zu machen, ist die Library Funktion system() zu benutzen:

  int system (const char * command)

Die Gefahren der system() Funktion

Diese Funktion ist sehr gef�hrlich: Sie ruft die Shell auf, um einen Befehl, das in der Variable "command" spezifiziert wurde, auszuf�hren. Das Verhalten der Shell h�ngt dabei von den Vorlieben des Benutzers ab. Ein typisches Beispiel ist die PATH Umgebungsvariable. La� uns annehmen, da� eine Applikation mail aufruft. Dieses Programm schickt z.B den Quellcode dem Benutzer, der es aufruft:

/* system1.c */

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

int
main (void)
{
  if (system ("mail $USER < system1.c") != 0)
    perror ("system");
  return (0);
}
Nehmen wir weiterhin an, da� das Programm mit Set-UID root arbeitet:
>> cc system1.c -o system1
>> su
Password:
[root] chown root.root system1
[root] chmod +s system1
[root] exit
>> ls -l system1
-rwsrwsr-x  1 root  root  11831  Oct 16  17:25 system1
>>
 
Beim Ausf�hren dieses Programmes wird die Shell (/bin/sh) mit der Option -c aufgerufen und �bergibt den String der als Befehl ausgef�hrt werden soll. Die Shell sucht dann die Verzeichnisse aus der Umgebungsvariable PATH ab, um eine ausf�hrbare Datei names mail zu finden. Der Benutzer braucht blo� die Variable PATH zu �ndern und der Befehl, den er ausf�hren m�chte in mail umbenennen und schon kann er irgendetwas ausf�hren:
  >> export PATH=.
  >> ./system1
 
Damit wird z.B versucht, mail im augenblicklichen Verzeichnis zu finden. Jetzt schreiben wir einfach ein kleines Shell Script und nennen es mail. Das Skript wird dann mit der EUID des Dateieigent�mers der Applikation ausgef�hrt. Hier ist ein Skript, das /bin/sh ausf�hrt. Da stdin umgeleitet ist, m��en wir uns die Eingabe zur�ck vom Terminal holen. Unser Skript sieht damit so aus:
#! /bin/sh
# "mail" script running a shell
# getting its standard input back.
/bin/sh < /dev/tty
 
Hier ist das Ergebnis:
>> export PATH="."
>> ./system1
bash# /usr/bin/whoami
  root
bash# 
 

Der erste L�sungsansatz besteht nat�rlich darin, immer einen vollen Pfadnamen zu benutzen, z.B /bin/mail. Damit gibt es ein neues Problem. Die Applikation vertraut darauf, da� mail an einer bestimmten Stelle im System zu finden ist. W�hrend /bin/mail im allgemeinen in jedem System zu finden ist, gibt es andere Programme wie z.B GhostScript, die in verschiedenen Distributionen unterschiedlich installiert sind. Desweiteren gibt es noch einen anderen Typ von Angriff bei einigen alten Shells, der auf er Umgebungsvariable IFS beruht. Die Shell benutzt sie, um die Trennzeichen zwischen Befehl und Argument zu finden. Im allgemeinen sind das Leerzeichen Tab und Return. Wenn der Benutzer / hinzug�gt, dann wird der Befehl "/bin/mail" als "bin mail" interpretiert. Eine ausf�rbare Datei namens bin kann jetzt im augenblicklichen Verzeichnis ausgef�hrt werden, wenn PATH entsprechend gesetzt ist.

Unter Linux ist die IFS Umgebungsvariable kein Problem mehr, da die bash sie mit Leerzeichen, Tab und Return beim Start vervollst�ndigt. Das gleiche gilt f�r pdksh. Dennoch sollte man sich nicht darauf verlassen, denn Applikation werden oft auf andere Systeme portiert und diese Systeme k�nnen sich hier anders verhalten.

Einige andere Umgebungvariablen k�nnen unerwartete Probleme machen. Das Programm mail erlaubt es z.B dem Benutze,r ein anderes Programm auszuf�hren, w�hrend er eine Nachricht schreibt. Das geht mit der Escapesequenz "~!". Schreibt der Benutzer den String "~!command" am Anfang einer Zeile, dann wir dieser ausgef�hrt. Das Program /usr/bin/suidperl zum Schreiben von Set-UID perl Scripten rief /bin/mail auf, wenn es irgendein Problem hatte, um root eine Nachricht zu schicken. Da suidperl ein Set-UID root Programm ist, wird alles mit root Rechten ausgef�hrt. In der Nachricht an root steht der Name der fehlerhaften Datei. Jemand kann eine Datei erzeugen mit einem Dateinamen, der carriage return gefolgt von ~!command enth�lt. Wenn ein suidperl Programm �ber ein Problem stolpert, das mit dieser Datei zusammenh�ngt, dann wird /bin/mail aufgerufen und mit der Escapesequenz aus dem Dateinamen gef�ttert.

Eigentlich sollte das kein Problem machen, da mail keine Escapesequenzen akzeptiert, wenn es nicht innerhalb eines Terminalfensters aufgerufen wird. Leider gibt es ein nicht dokumentiertes Feature (vermutlich vom Debuggen �brig geblieben), das es erlaubt, Escapesequenzen zu benutzen, sobald die Umgebungsvariable interactive gesetzt ist. Das Ergebnis? Ein leicht ausbeutbares Sicherheitsloch innerhalb einer Applikation, die eigentlich die Sicherheit eines Systems verbessern sollte. Der erste Fehler ist ein zwischen zwei Programmen geteilter Fehler. /bin/mail enth�lt ein nicht dokumentiertes Feature, das das Ausf�hren von beliebigem Code erlaubt. Das zweite Problem ist, selbst wenn die Entwickler des /usr/bin/suidperl dieses Feature nicht kannten, sollten sie nicht einfach alle Umgebungsvariablen �bernehmen.

Linux ignoriert normalerwiese die Set-UID und Set-GID bits bei Skripten. Siehe /usr/src/linux/fs/binfmt_script.c und /usr/src/linux/fs/exec.c. Einige Tricks erlauben es, diesen Mechanismus zu umgehen. /usr/bin/suidperl benutzt solche Tricks.

L�sungen

Es ist nicht immer einfach einen Ersatz f�r die Funktion system() zu finden. Die erste Variante ist, Systemaufrufe wie execl() oder execle() zu benutzen. Das Verhalten ist dann jedoch v�llig anders, da das externe Programm nicht mehr als Subroutine (Unterprogramm) aufgerufen wird. Das externe Programm ersetzt den augenblicklichen Prozess. Man mu� den Prozess dublizieren und die Kommandozeilenargumente durchsuchen wie in dem folgenden Programm.


Aus
  if (system ("/bin/lpr -Plisting stats.txt") != 0) {
    perror ("Printing");
    return (-1);
  }
 
wird :
pid_t pid;
int   status;
  
if ((pid = fork()) < 0) {
  perror("fork");
  return (-1);
}
if (pid == 0) {
  /* child process */
  execl ("/bin/lpr", "lpr", "-Plisting", "stats.txt", NULL);
  perror ("execl");
  exit (-1);
}
/* father process */
waitpid (pid, & status, 0);
if ((! WIFEXITED (status)) || (WEXITSTATUS (status) != 0)) {
  perror ("Printing");
  return (-1);
}
 
Offensichtlich viel mehr Code. Unter einigen Umst�nden wird es sehr komplex. Zum Beispiel, falls man die Standardeingabe (stdin) umleiten m�chte. Wie zum Beispiel hier:
system ("mail root < stat.txt");
 
Die Umleitung (<) wird von der Shell erledigt. Man kann das gleiche durch eine komplexe Verkn�pfung von fork(), open(), dup2(), execl(), etc. erreichen. In diesem Fall ist es eine akzeptable L�sung, die system() Funktion zu benutzen, aber man mu� vorher die gesamte Umgebung konfigurieren.

Umgebungsvariablen werden unter Linux in Form eines Zeigers auf ein Array abgespeichert: char ** environ. Dieses Array endet mit NULL. Die Strings haben die Form "NAME=value".

Wir fangen damit an, die gesamte Umgebung zu l�schen. Wir benutzen die Gnu Erweiterung clearenv:

    int clearenv (void);
 
oder wir setzen den Pointer
    extern char ** environ;
 
einfach zu NULL. Als n�chstes werden die Umgebungsvariablen, die wir brauchen, initialisiert:
    int setenv (const char * name, const char * value, int remove)
    int putenv(const char *string)
 
Vor einem system() aufruf z.B so:
    clearenv ();
    setenv ("PATH", "/bin:/usr/bin:/usr/local/bin", 1);
    setenv ("IFS", " \t\n", 1);
    system ("mail root < /tmp/msg.txt");
 
Falls n�tig, kann man sich den Inhalt einiger n�tzlicher Umgebungsvariablen merken, bevor man die gesamte Umgebung l�scht. Z.B. LANG, TERM, TZ, HOME. Der Inhalt, die Form und die Gr��e dieser Variablen mu� genauestens gepr�ft werden. Es ist wichtig, da� man die gesamte Umgebung l�scht, bevor man einige ben�tigte Variablen definiert. Das suidperl Sicherheitsproblem w�re nicht aufgetatucht, wenn die Umgebung korrekt gel�scht worden w�re.

Entsprechend geht man bei einer Netzwerkkonfiguration vor. Zun�chst verw�hrt man jeder Maschiene die Verbindung. Als n�chstes werden die ben�tigten Services aktiviert. Genauso geht man bei der Entwicklung einer Set-UID Applikation vor. Erst die gesamte Umgebung l�schen und dann einzlne Umgebungsvariablen setzen.

Das Verifizieren der Parameter wird gemacht, indem man die erwarteten Werte mit den erlaubten Formaten vergleicht. Macht man es anders und �berpr�ft nur auf m�gliche Fehler in den Parametern, so k�nnte es sein, da� man einen Fall vergessen hat.

Was bei system() gef�hrlich ist, gilt nat�rlich auch f�r verwandte Funktionen wie popen(), oder Aufrufe wie execlp() und execvp(), die die PATH Variable ber�cksichtigen.

Indirektes Ausf�hren von Befehlen

Um die Ergonomie eines Programmes zu verbessern, ist es eine einfache M�glichkeit, ein Program konfigurierbar zu machen. Z.B. mit Hilfe von Makros. Um Variablen oder generische Muster handzuhaben, gibt es eine Funktion names wordexp(). Man mu� mit ihr sehr vorsichtig sein, da sie einen String wie $(commande) als externen Befehl ausf�hrt. Es ist genug, den String "$(/bin/sh)" einzugeben, um eine Set-UID Shell zu erhalten. Um so etwas zu vermeiden, hat wordexp() ein Attribut namens WRDE_NOCMD, das die Sequenz $( ) deaktiviert.

Beim Aufruf von externen Kommandos mu� man vorsichtig sein, damit man kein Kommando erwischt, das eine Escapesequenz zum �ffnen einer Shell zul��t. Wie z.B vi :!command. Es ist schwierig, alle aufzuf�hren. Einige Applikationen sind offensichtlich (Texteditoren, Dateimanger...) andere sind schwieriger zu entdecken, wie wir es bei /bin/mail gesehen haben.

Zusammenfassung

Dieser Artikel hat folgende Aspekte erl�utert :

Im n�chsten Artikel werden wir �ber Speicher sprechen. Wie er organisiert ist, Funktionsaufrufe .... und dann werden wir zu buffer overflows kommen und zeigen, wie man dieses Sicherheitsloch mit shellcode ausnutzen kann.