Open Source im professionellen Einsatz

Sicheres Programmieren für Administratoren - Folge 6: Sicherheitslücken in Ridentd

Identitätskrise

Nur weil sich ein Programm besonders paranoid verhält, ist es noch lange nicht sicher: Das recht bekannte Perl-Programm Ridentd verschleiert zwar aufwändig die Identität eines Users, leidet aber an Sicherheitslücken. Welche das sind und was Programmierer dagegen unternehmen, zeigt dieser Artikel.

Sichere Programme entwickeln ist eine viel zu selten ausgeübte Kunst. Selbst wer sich Mühe gibt, greift häufig daneben: Wie leicht das passiert, belegen die "InSecurity News" in jedem Linux-Magazin und Sicherheits-Webseiten wie der Security Tracker. Auch Ridentd [1] hat dort seine Spuren hinterlassen [3] - ein Anlass, das Programm genauer zu analysieren. Dabei treten einige verbreitete Fehler zutage. Die im Folgenden erarbeiteten Korrekturen funktionieren ähnlich auch bei anderer Software.

Wie im Namen anklingt, geben der klassische Ident-Daemon »identd« und seine zahlreichen Ableger an Port 113 den Namen des Benutzers heraus, dem eine Netzwerkverbindung gehört [2]. Typischerweise fragt ein Server den Ident-Daemon des Clients nach dem Besitzer einer Verbindung, über die der Server eine Anfrage empfangen hat (Abbildung 1).

Abbildung 1: Annika startet über eine Applikation auf Tomate eine Anfrage an den Applikationsserver (1). Dieser fragt den Ident-Daemon von Tomate, wem die Verbindung mit dem lokalen Port 789 und dem entfernten Port 456 gehört (2). Normalerweise antwortet Identd wahrheitsgemäß mit »annika« (3).

Abbildung 1: Annika startet über eine Applikation auf Tomate eine Anfrage an den Applikationsserver (1). Dieser fragt den Ident-Daemon von Tomate, wem die Verbindung mit dem lokalen Port 789 und dem entfernten Port 456 gehört (2). Normalerweise antwortet Identd wahrheitsgemäß mit »annika« (3).

Diese Maßnahme scheint auf den ersten Blick die Authentizität des Benutzers sicherzustellen. In der Praxis klappt das nicht: Ein Angreifer lässt seinen eigenen Ident-Daemon mit beliebigem Quatsch antworten. Ehrliche Anwender gefährdet der Dienst sogar, weil er ohne Not lokale Benutzernamen preisgibt. Sinnvoll ist das bestenfalls bei gut administrierten Mehrbenutzermaschinen, von denen aus ein User fremde Rechner angreift. Das Opfer erfährt vom Ident-Daemon den Account der Quellmaschine und informiert dessen Admin.

Trotz aller Bedenken erlaubt es manche Software, dass sich Benutzer einzig über das Ident-Protokoll ausweisen. Zum Beispiel PostgreSQL bei entsprechender Konfiguration. Auf die Frage nach den Gründen für eine solche Konfiguration erhielt der Autor erschreckende Antworten wie: Das ist ja nur intern, wir haben eine Firewall.

Gesunde Paranoia

Der Random Ident Daemon (Ridentd) tritt mit dem Anspruch an, keinerlei Informationen über das System herauszugeben. Statt mit einem Benutzernamen antwortet er mit einem zufälligen Eintrag aus einem Ispell-Wörterbuch. Die Informationen über die abgefragte Verbindung liest er aus dem Proc-Dateisystem und ordnet ihr für ihre gesamte Lebensdauer ein gleich bleibendes Wort zu. Dabei setzt Ridentd eine Standardinstallation von Ispell mit den Daten in »/usr/lib/ispell« voraus.

Bisher behandelte die Reihe "Sicheres Programmieren" ([4] bis [8]) die Sprachen C, C++ und Shellskripte. Mit anderen Programmiersprachen sind Entwickler offensichtlich auch nicht besser bedient: Der Ridentd-Autor hatte sich für Perl entschieden. Im Folgenden arbeitet dieser Artikel Schwächen der Versionen 0.9.1b und 0.9.2b heraus. In Kooperation mit dem Ridentd-Autor Rob Meijer entstand im Januar 2006 die Version 0.9.3b, die einige kritische Probleme beseitigt.

Im Fokus

Anfang 2004 berichtete ein Benutzer auf der Sicherheitsseite Security Tracker von einer Symlink-Schwäche mit der temporären Datei »/tmp/rident.pid« in der Ridentd-Version 0.9.1b [3]. Das Listing 1 zeigt die interessantesten Ausschnitte aus dem Perl-Skript.

Listing 1: Ridentd
0.9.1b

001 #!/usr/bin/perl
[...]
136 if (open(PID,"/tmp/rident.pid"))
137 {
[...]
139   $opid=<PID>;
140   chomp($opid);
141   close(PID);
142   unless ($opid =~ /^d+$/)
143   {
[...]
145     exit;
146   }
[...]
148   open(PS,"/bin/ps -p $opid|");
149   $killit=0;
150   while (<PS>)
151   {
152      if (/ridentd.pl/)
153      {
154        $killit=1;
155      }
156   }
157   if ($killit)
158   {
[...]
160     kill(9,$opid);
161     sleep(1);
162   }
[...]
167 }
[...]
176 $pid=fork();
[...]
181 if ($pid)
182 {
183   open(PID,">/tmp/rident.pid");
184   print PID "$pidn";
[...]
187 }

Der Block von Zeile 136 bis 167 öffnet, sofern vorhanden, die PID-Datei (Zeile 136), liest daraus die Prozess-ID eines noch laufenden oder abgestürzten Ridentd (139 bis 141) und stellt sicher, dass er eine Zahl gelesen hat (regulärer Ausdruck »^d+ in Zeile 142). Anschließend sucht Ridentd in der Ausgabe von »/bin/ps -p nach der Zeichenkette »ridentd.pl« (148 bis 156). Falls ein gleichnamiger Prozess mit der gegebenen PID existiert, killt ihn das Skript per Signal 9 (SIGKILL, 157 bis 162).

Dieser kurze Auszug hat bereits mindestens drei unangenehme Macken - dazu später mehr. Nachdem der Ridentd einen zweiten Prozess abgespaltet hat (Zeile 176), erzeugt oder überschreibt der Mutterprozess die Datei »/tmp/rident.pid«, indem er vor den Dateinamen das Zeichen »>« hängt. Damit gibt es wenigstens drei Erfolg versprechende Angriffspfade auf das System:

  • Der Angreifer erzeugt einen Symlink auf eine Datei, deren erste
    Zeile lediglich aus einer ganzen Zahl besteht.
  • Er legt einen Symlink an, der auf keine bestehende Datei zeigt,
    beispielsweise »/etc/rc2.d/S55foobar«.
  • Er versucht einen Symlink auf eine beliebige Datei zu
    erstellen, nachdem das Skript Zeile 136 passiert hat (Abbildung
    2).

Abbildung 2: Der Angreifer startet ein Shellskript, das abwechselnd den Symlink »/tmp/ridentd.pid« erzeugt und löscht. Mit etwas Glück existiert der Link nicht, wenn Ridentd 0.9.1b die Datei in Zeile 136 öffnen will, zeigt aber auf »/etc/passwd«, wenn das Skript in Zeile 183 vorbeikommt.

Abbildung 2: Der Angreifer startet ein Shellskript, das abwechselnd den Symlink »/tmp/ridentd.pid« erzeugt und löscht. Mit etwas Glück existiert der Link nicht, wenn Ridentd 0.9.1b die Datei in Zeile 136 öffnen will, zeigt aber auf »/etc/passwd«, wenn das Skript in Zeile 183 vorbeikommt.

Im Erfolgsfall mäht das »open()«-Kommando in Zeile 183 mit der anschließenden Schreiboperation eine bestehende Datei nieder (dritter Punkt) oder erzeugt sie neu (Variante zwei). Methode drei ist am schwierigsten auszuführen, erlaubt es dem Angreifer aber, beliebige Dateien zu zerstören. Das schafft ein kleines Shellskript mit guter Erfolgschance: Es legt pausenlos den Symlink an und löscht ihn sofort wieder (Abbildung 2, rechts oben). Mehr zu Symlink-Attacken steht in Folge 2 dieser Serie [5].

Bald nach der Sicherheitsmeldung hat Rob Meijer die Version 0.9.2b veröffentlicht, um die Symlink-Schwäche zu beseitigen. Hinzugekommen sind zwei Sicherheitsabfragen (Listing 2). Zeile 136 prüft per »(-l "/tmp/rident.pid")« vor dem Öffnen der Datei, ob »/tmp/ridentd.pid« ein symbolischer Link ist. Das deutet auf einen Angriff hin und das Skript beendet sich mit einer Fehlermeldung. Weiter unten löscht eine neuer Codeblock (Zeilen 187 bis 192) eine noch bestehende PID-Datei. Die Bedingung »(-e "/tmp/rident.pid")« testet, ob die Datei existiert.

Listing 2: Ridentd
0.9.2b

[...]
136 if (-l "/tmp/rident.pid") {
137    print "PANIC: It apears someone has tempored with the pid file /tmp/rident.pidn";
138    exit;
139 }
140 if (open(PID,"/tmp/rident.pid"))
141 {
[...]
146   unless ($opid =~ /^d+$/)
147   {
[...]
149     exit;
150   }
[...]
185 if ($pid)
186 {
187   if (-e "/tmp/rident.pid") {
188     unlink("/tmp/rident.pid");
[...]
192   }
193   open(PID,">/tmp/rident.pid");
194   print PID "$pidn";
[...]
197 }

Diesen Artikel als PDF kaufen

Als digitales Abo

Als PDF im Abo bestellen

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook