Aus Linux-Magazin 12/2009

Shellskripte aus der Stümper-Liga - Folge 3: Passwörter und Race Conditions

Viele Entwickler stecken zwar viel Überlegung in die Sicherheitsfunktionen ihrer Programme, kümmern sich um Rechte und sorgen sich um den sicheren Umgang mit Passwörtern. Blöd nur, wenn Wrapperskripte all diese Überlegungen über den Haufen werfen .

Eine beliebte Lösungsstrategie kleiner Kinder ist das Schließen der Augen. Was sie nicht sehen, ist nicht da, weder für sie, noch für andere. Zum Beispiel das umgeschüttete Glas oder ein zerbrochenes Spielzeug der großen Geschwister. Wenige Jahre später holt die meisten ziemlich schnell eine brutale, andere Wirklichkeit ein, in der der Lider-zu-Ansatz nicht mehr klappt. Dennoch hofft eine kleine Gruppe weiterhin auf dessen Gültigkeit: naive Shellskript-Programmierer.

Passwörter im Klartext

Denn Geheimnisse gibt es viele zu hüten, aber manchmal sind sie auch lästig: Wer möchte eigentlich jedes Mal, wenn sich das Skript an die Datenbank wendet, neu das Passwort eingeben? Die Motivation eines Skripts ist schließlich meist, wiederkehrende Abläufe zu automatisieren. So landen immer wieder Passwörter im Klartext in Shellskripten oder in Cronjobs. Allein das stellt einem für die Sicherheit Verantwortlichen schon die Nackenhaare auf: Jeder, der Zugriff auf das Skript bekommt, kennt fortan auch das eigentlich geheimzuhaltende Passwort.

Immerhin muss eine zweite Bedingung erfüllt sein: die Möglichkeit, das Skript zu lesen. Solange das Skript ruht, lässt sich die Bedingung sogar halbwegs auf jedem Linux-System mittels »chmod og-r Datei« verhindern (sofern Anwender berücksichtigen, dass bekanntlich Root und indirekt alle Benutzer mit Zugang zu diesem Account jede Datei lesen dürfen). Das haben sich seinerzeit wohl auch jene SAP-Entwickler gedacht, die in ihrem stümperhaften Startskript für die SAP DB »/etc/init.d/sapdb74« das Codefragment aus Listing 1 stehen hatten [1].

Listing 1: Fehlerhaftes
Startskript für die SAP DB

01 DBMCLI=$X_PATH/dbmcli
02 if [ ! -x $DBMCLI ]; then
03     echo "dbmcli not found" >&2
04     exit 5
05 fi
06 $DBMCLI -d TST -u dbm,dbm db_warm > /dev/null &

Jeder Anwender, der auf derselben Maschine zur Laufzeit das Kommando »ps auxwww« startete, sah in der Prozessliste die Argumente und damit auch den Benutzernamen »dbm« und das gleichlautende Passwort. Dank des Tipps von Bash-Bashing-Leser Peter Conrad [2] haben die Entwickler den Code in die etwas sicherere Variante aus Listing 2 abgewandelt. Der dabei angewandte Übergabemechanismus über die Standardausgabe der Shell und die Standardeingabe der Datenbank ist ohne Root-Rechte von anderen Prozessen aus nicht abhörbar.

Listing 2: Korrigiertes
Startskript für die SAP DB

01 DBMCLI=$X_PATH/dbmcli
02 if [ ! -x $DBMCLI ]; then
03     echo "dbmcli not found" >&2
04     exit 5
05 fi
06 _o=`$DBMCLI -d TST74 << __EOD__ 2>&1
07     user_logon dbm,dbm
08     db_online
09 __EOD__`

Leider beherrscht nicht jedes Programm das Einlesen von Credentials über einen Filedescriptor. Manchmal benötigt es erst viel später nach seinem Aufruf ein Passwort oder möchte es nur bei Bedarf nachschlagen. Ein Beispiel ist ein Webbrowser, den Anwender normalerweise für das Intranet nutzen, der aber für den Zugriff auf das Internet einen Proxy benötigt. Viele Proxys wollen dann ein Passwort sehen – lästig für den Benutzer.

Umweltschutz

In einem Sun-Forum haben Programmierer den Code aus Listing 3 für die ».bashrc« vorgeschlagen [3]. Die Option »-e« von »read« schaltet die Readline-Funktionen ein, um die Eingabe bequem editierbar zu machen, »-s« in Zeile 4 unterdrückt das Echo auf dem Bildschirm bei der Passworteingabe. Danach speichert das Fragment die Angaben direkt in der Umgebungsvariablen »http_proxy«, die wie jede URL auch zwei Authentisierungsattribute enthalten darf, den Benutzernamen und das Passwort.

Listing 3: Passwort im
Environment

01 echo -n "Enter Proxy Username: "
02 read -e username
03 echo -n "Enter Proxy Password: "
04 read -es password
05 echo
06 export http_proxy="http://$username:$password@proxyserver:8080/"

Problematisch ist nur, dass die Autoren übersehen haben, dass das Environment ebenfalls für andere Anwender sichtbar ist. Bloß weil nur wenige Anwender die BSD-Option »e« des Kommandos »ps« häufig nutzen, heißt das nicht, dass die Information nicht vorhanden ist. Der Aufruf »ps auxe | tr ‘ ‘ ‘ ‘ | grep http_proxy | head -1« liefert nämlich:

http_proxy=magnus:dasIstNichtMeinPasswd@prx

Weil das Kommando die einzelnen Schlüssel-Wert-Paare alle in eine Zeile schreibt, nur durch Spaces getrennt, sorgt das »tr«-Kommando für einen Umbruch nach jedem Wort, damit das anschließende »grep« sich auf das Wesentliche konzentrieren kann.

Eine Idee wäre, das Passwort in Shellskripten zu verschlüsseln. Listing 4 implementiert einen einfachen involutorischen Verschiebe-Algorithmus, auch Caesar-M oder Rot13 genannt. Doch selbst wenn der Algorithmus ausgefuchster wäre, hilft das nicht weiter, denn zwangsläufig kann jeder, der Einblick in das Skript hat, die Entschlüsselungsfunktion ablaufen lassen, sodass sich das Problem in seinen eigenen Schwanz beißt. Man kann es drehen und wenden, wie man will: Auf Client-Seite ist ein Passwort am besten im Kopf seines Besitzers aufgehoben.

Listing 4:
Verschlüsselungsfunktion

01 function crypt() {
02     echo "$*" | tr 'A-Z' 'N-ZA-M'
03 }
04 
05 passwd=$(crypt "MVRZYVPU TRURVZ")

Zur Wurzel vorstoßen

Ein Ur-Prinzip von Unix und damit auch von Linux bleibt, dass Root faktisch alles darf. Die Bemühungen von SE Linux, App Armor und ähnlichen Projekten in allen Ehren: Die aufwändig anzulegenden Sicherheitsprofile gibt es oft nur für wenige Anwendungen, und eigene zu erstellen übersteigt die eigenen Kapazitäten. In der Zwischenzeit stürzen sich Angreifer auf weniger gut gesicherte Programme. Eine solche Möglichkeit bietet sich dann, wenn bekannt ist, dass ein Admin mit Privilegien dabei ist, ein Skript ungewisser oder ungewissenhafter Herkunft auszuführen.

Die USB-Empfänger Sundtek Media TV Pro/Gold kommen mit einem Shellskript, das den Linux-Treiber »em28xx« installieren soll [4]. Listing 5 zeigt einen Auszug aus dem Skript, das in Zeile 11 einen Teil des Skripts selbst, das in binärer Form vorliegt, als Tar-Archiv in »/tmp« abspeichert und auspackt. Das Archiv enthält unter anderem das Tool »chk64«, das den Prozessortyp prüft und dementsprechend Treibervarianten installiert.

Listing 5: Unsicherer
Installer

01 if [ "$UID" != "0" ]; then
02     echo "To install this driver, run it as root"
03     echo "eg. $ sudo $0"
04     exit 0;
05 fi
06 [...]
07 app=$0
08 [...]
09 echo "installing em28xx driver to /opt"
10 echo "unpacking..."
11 dd if=${app} of=/tmp/installer.tar.gz skip=1 ibs=4635 bs=4635 obs=4635 2> /dev/null
12 cd /tmp
13 tar xzf installer.tar.gz
14 echo -n "checking system... "
15 /tmp/chk64 1>/dev/null 2>&1
16 if [ "$?" = "0" ]; then
17     echo "64Bit System detected"
18     SYSTEM="64bit"
19 else
20     [...]
21 fi
22 [...]

Allerdings prüft das Skript nicht, ob beim Auspacken »/tmp/chk64« schon existiert. Weiß etwa ein Schelm, dass der Systemadministrator demnächst den Treiber installieren möchte (oder bittet ihn hinterlistig darum), bringt er den Admin dazu, seinen Code auszuführen. Dazu legt er eine eigene Datei an und schützt sie vor dem Überschreiben:

echo "chmod 777 /" > /tmp/chk64
chmod 755 /tmp/chk64c

Das Tar-Kommando aus Zeile 13 überschreibt – selbst als Root ausgeführt – das Skript nicht. Statt des CPU-Testers führt Root anschließend das Mini-Skript aus und macht zum Beispiel das Wurzelverzeichnis schreibbar. Diese Änderung fällt nur den wenigsten Admins manuell auf, erlaubt es aber dem Angreifer, ganze Parallelwelten etwa für »/usr/bin« anzulegen, bei denen die dort enthaltenen Programme Schadcode enthalten.

Alternativ ließe sich auch ein SUID-Flag auf ein wichtiges Systemkommando setzen, etwa per »chmod 4755 /usr/bin/vi«. Ironischerweise klappt das nicht mit Shellskripten, da sich die Entwickler der Bash entschlossen haben SUID-Flags dort effektiv zu ignorieren [5].

Wer also temporäre Dateien anlegt, ist gut damit beraten, ein dynamisches Verzeichnis anzulegen und zu prüfen, ob es existiert. Wer den wiederkehrenden Aufwand für diese Aktion scheut, benutzt dazu das fertige Tool »mktemp«, das es als separates Projekt [6] oder als Teil der auf den meisten Distributionen installierten Coreutils gibt.

Augen auf

Sicherheit und Shellskripte sind wegen latenter Schwachstellen keine Wunschpartner. Wer sich beim Skripten, der Systembetreuung und in Sachen Security auskennt, übergibt Passwörter nie über Aufrufparameter oder Umgebungsvariablen, sondern besser durch Filedescriptoren oder direkten Dateizugriff. Zudem prüft er, ob temporär angelegte Dateien auch die richtigen sind, bevor er sie ausführt – mit offenen Augen.

Infos

[1] SAP DB:[http://www.sapdb.org/7.4/rpm_linux.htm]

[2] Peter Conrad, “Insecure example in »/etc/init.d/sapdb74«”: [http://osdir.com/ml/db.sapdb.general/2002-12/msg00176.html]

[3] Amduser, “Encrypting Password in ShellScript”: [http://forums.sun.com/thread.jspa?threadID=5397773&messageID=10784264#10784264]

[4] Sundtek Media TV Pro, Linux-Installation: [http://support.sundtek.de/index.php?topic=4.0]

[5] Maarten Litmaath, “How can I get setuid shell scripts to work?”: Unix FAQ, [http://www.faqs.org/faqs/unix-faq/faq/part4/]

[6] Dokumentation und Beispiele für Mktemp: [http://www.mktemp.org/manual.html]

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben