Open Source im professionellen Einsatz

OpenSSH: PAM hebelt Sandbox aus

Seit Version 5.9 verwendet OpenSSH zwei verschiedene Programmprozesse, um die Interaktion mit Benutzern abzuwickeln: einen unpriviligierten Child-Prozess und einen priviligierten Monitor-Prozess. Der Child-Prozess erledigt dabei die meiste Arbeit. Insbesondere kümmert er sich um das Verarbeiten von Netzwerkdaten. Der Monitor-Prozess führt alle Operationen aus, die höhere Rechte benötigen. Die Idee hinter dieser Aufgabenteilung ist, dass etwaige Programmierfehler im komplexen Child-Programmcode nicht zu sicherheitskritischen Problemen bei der Authentifizierung führen.

Die beiden Prozesse kommunizieren mit Hilfe von Sockets, wobei verschiedene Anfrage- und Antwort-Typen definiert sind. Diese sind in der »monitor.c«-Datei definiert und finden sich dort in den »mon_dispatch_proto{15,20«}- und »mon_dispatch_postauth{15,20}«-Strukturen. Dabei kommen auch bestimmte Flags zum Einsatz, die genau regeln wann und wie bestimmte Anfragen an den Monitor-Prozess verarbeitet werden. So gibt es beispielsweise das »MON_ONCE«-Flag, das festlegt, dass diese bestimmte Anfrage nur einmal durchgeführt werden darf. Diese Flags sind ebenfalls in der »monitor.c«-Datei definiert. Weiter sind bestimmte Monitor-Anfragen auch nicht zu jeder Zeit im Ablauf des SSH-Protkolls erlaubt. Ob und wann eine bestimmte Anfrage erlaubt ist regeln die »monitor_permit()«- und »monitor_permit_authentications()«-Funktionen. Sollte eine Anfrage im aktuellen Stadium des Protokolls nicht erlaubt sein, so ruft der Monitor-Prozess die »fatal()«-Funktion auf und terminiert so.

Das gesamte Konzept dieser Aufgabenteilung ist unter dem Namen Privilege Separation bekannt und dient der Sicherheit der OpenSSH Applikation. Es handelt sich also um eine Art Sandbox-Prinzip. Wie sich nun herausstellte hat dieser Ansatz in der aktellen OpenSSH-Implementierung allerdings zwei Schwachstellen, wenn zusätzlich noch PAM (Pluggable Authentication Modules) aktiviert ist. Das erste Problem erlaubt es einem entfernten Angreifer sich als anderer Benutzer am System anzumelden. Hierzu muss er allerdings eine etwaige Schwachstelle in dem unpriviligierten Child-Prozess finden und ausnutzen. Die Ursache dieses Problems liegt in dem PAM-relavanten Code. Falls OpenSSH mit PAM-Unterstützung übersetzt wurde, so stehen in »monitor.c« einige weitere Anfragetypen an den Monitor-Prozess bereit:

 #ifdef USE_PAM
{MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start},
{MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account},
{MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx},
{MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query},
{MONITOR_REQ_PAM_RESPOND, MON_ISAUTH, mm_answer_pam_respond},
{MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx},
#endif

Bevor irgendwelche PAM-Monitor-Anfragen gesendet werden, vergewissert sich der Child-Prozess via »MONITOR_REQ_PWNAM« zunächst, dass der angegebene Benutzername überhaupt gültig ist. Falls ja, so schickt der Monitor-Prozess die entsprechende Passwd-Struktur zurück. Weiter trägt der Monitor-Prozess den Benutzernamen und die Passwd-Struktur in den aktuellen Authentication-Kontext (»struct Authctxt *authctxt«) ein. Anschließend beginnt die eigentliche PAM-Authentifizierung indem der Child-Prozess die »MONITOR_REQ_PAM_START«-Anfrage an den Monitor sendet, um via »pam_start()« eine neue Authentication Transaktion zu starten für diesen Benutzer. Dabei sendet der Child-Prozess die »MONITOR_REQ_PAM_INIT_CTX«-Anfrage zur Initialisierung, wobei hierfür folgende Funktion aufgerufen wird:

int
mm_answer_pam_init_ctx(int sock, Buffer *m)
{
    debug3("%s", __func__);
    authctxt->user = buffer_get_string(m, NULL);
    sshpam_ctxt = (sshpam_device.init_ctx)(authctxt);
    sshpam_authok = NULL;              
    buffer_clear(m);
    [...]

Die zweite Zeile der Funktion zeigt hier, dass der Child-Prozess den Benutzernamen selbst noch einmal sendet als Teil seiner Anfrage. Eigentlich ist dies nicht nötig, da der Monitor-Prozess darüber schon vorher informiert wurde. Schlimmer noch, der Child-Prozess überschreibt den vorher gesendeten Benutzernamen in dem Authentication-Kontext des Monitors.

Hier liegt nun genau das Problem: Sollte nämlich ein Angreifer Kontrolle über den Child-Prozess haben, so könnte er hier einen beliebigen anderen Benutzernamen eintragen. In diesem Fall würde der Benutzername in dem Authentication-Kontext nicht mit dem der gespeicherten Passwd-Struktur übereinstimmen. Allerdings verlässt sich die gesamte weitere PAM-Authentifizierung lediglich auf den zuletzt abgespeicherten Benutzernamen und nicht mehr auf den Namen in der Passwd-Struktur. Hat ein Angreifer erst einmal Kontrolle über den Child-Prozess, so bricht an dieser Stelle das gesamte Sandbox-System zusammen, da er dem Monitor-Prozess dann einfach einen anderen Benutzernamen vorgeben kann.

Die Attacke könnte also einfach wie folgt ablaufen. Der Angreifer sendet via »MONITOR_REQ_PWNAM«-Anfrage den gewünschten Benutzernamen (beispielsweise »root«), der in die Passwd-Struktur eingetragen wird. Anschliessend gibt er einen anderen Benutzernamen im Child-Prozess an für den er das Passwort besitzt. Auf diese Weise wird er dann mit seinem unpriviligierten Account als Root am System angemeldet. Die Attacke ist natürlich nicht ganz einfach durchzuführen, denn erstens muss der Angreifer einen gültigen Account auf dem System haben und zweitens muss er auch den Child-Prozess entsprechend manipulieren können. Trotzdem zeigt diese Lücke schön, wie gut gemeinte Sandbox-Ansätze oft recht einfach ausgehebelt werden können.

Die zweite Schwachstelle hängt auch mit PAM-Monitor-Anfragen seitens des Child-Prozesses zusammen. Bei diesem Fehler spielt die Reihenfolge dieser Anfragen eine zentrale Rolle. Zusammengefasst besteht die zweite Sicherheitslücke darin, dass eine bestimmte Anfrage einen Speicherbereich freigibt, welcher dann danach von einer weiteren Anfragen noch genutzt wird. Dieses Use-After-Free-Problem tritt im Zusammenhang mit dem der »MONITOR_REQ_PAM_FREE_CTX«-Anfrage auf. Das Advisory weist allerdings daraufhin, dass es höchstwahrscheinlich sehr schwierig ist diese Schwachstelle für eine gezielt Attacke auszunutzen. Die beiden Schwachstellen wurden in der aktuellen Version 7.0 von OpenSSH korrigiert. Auch wenn die beiden Schwachstellen nicht einfach auszunutzen sind, so wird ein Update auf diese Version dringend empfohlen.

comments powered by Disqus

Stellenmarkt

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.