Original in fr Frédéric Raynal, Christophe Blaess, Christophe Grenier
fr to en Georges Tarbouriech
en to de Guido Socher
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.
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.
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.
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:
Der Befehl ">> ls -l /bin/su -rwsr-xr-x 1 root root 14124 Aug 18 1999 /bin/su >>
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.
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.
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.
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)
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:
Nehmen wir weiterhin an, daß das Programm mit Set-UID root arbeitet:/* system1.c */ #include <stdio.h> #include <stdlib.h> int main (void) { if (system ("mail $USER < system1.c") != 0) perror ("system"); return (0); }
Beim Ausführen dieses Programmes wird die Shell (>> 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 >>
/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:
Damit wird z.B versucht,>> export PATH=. >> ./system1
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:
Hier ist das Ergebnis:#! /bin/sh # "mail" script running a shell # getting its standard input back. /bin/sh < /dev/tty
>> 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.
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.
wird :if (system ("/bin/lpr -Plisting stats.txt") != 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: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); }
Die Umleitung (system ("mail root < stat.txt");
<
) 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:
oder wir setzen den Pointerint clearenv (void);
einfach zu NULL. Als nächstes werden die Umgebungsvariablen, die wir brauchen, initialisiert:extern char ** environ;
Vor einemint setenv (const char * name, const char * value, int remove) int putenv(const char *string)
system()
aufruf z.B so:
Falls nötig, kann man sich den Inhalt einiger nützlicher Umgebungsvariablen merken, bevor man die gesamte Umgebung löscht. Z.B.clearenv (); setenv ("PATH", "/bin:/usr/bin:/usr/local/bin", 1); setenv ("IFS", " \t\n", 1); system ("mail root < /tmp/msg.txt");
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.
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.
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.