Kerberos ist ein umfangreiches Authentifzierungsystem, das am MIT entwickelt wurde und frei zugänglich ist. Es umfasst auch einen FTP-Server, in dessen Code eine Sicherheitslücke entdeckt worden ist. Sie ermöglicht es es einem entfernten Angreifer mit gültigem FTP-Account, höhere Gruppenrechte zu erlangen.
Die Verwendung von Netzwerkprogrammen wie FTP mit Kerberos-Unterstützung bietet nicht nur ein hohes Maß an Sicherheit, sondern ist auch für den Anwender durch Single Sign-On sehr komfortabel, da das Kerberos-Authentifizierungsschema Tickets zur Authentifizierung verwendet. Auf der Kerberos-Website gibt es neben dem eigentlichen Kerberos-Download (“krb5”) auch ein Paket mit Kerberos-fähigen Applikationen (“krb5-appl”). Diese enthalten auch den FTP-Server und -Client.
Bei der Schwachstelle im FTP-Server handelt es sich um eine Verkettung mehrerer Programmierfehler, die dazu führen, dass ein entfernter Angreifer mit gültigem FTP-Account Befehle mit der effektiven Gruppen-ID des FTP-Prozesses ausführen kann. Im allgemeinen ist dies GID 0, also die Root- oder Wheel-Gruppe. Die Fehlerkette beginnt in der Autoconf-Datei “configure.ac”: Diese ruft für die Gruppenrechte-Funktionen “setegid()”, “setregid()” und “setresgid()” nicht “AC_CHECK_FUNCS” auf. Dieses Makro überprüft, ob die als Argument übergebenen Funktionen auf dem System vorhanden sind oder nicht. Falls vorhanden, wird “HAVE_FUNCTION” gesetzt. Dadurch kann im Programmcode einfach nach vorhandener Funktionalität gefiltert werden. Da der Aufruf dieses Makros für die genannten Funktionen in “configure.ac” aber fehlt, sind die entsprechenden “HAVE_”-Einträge nicht gesetzt. Das hat nun Konsequenzen in der Include-Datei “k5-util.h”, wo die Makros “krb5_seteuid()” und “krb5_setegid()” definiert werden:
#if defined(HAVE_SETEUID) # define krb5_seteuid(EUID) (seteuid((uid_t)(EUID))) #elif defined(HAVE_SETRESUID) # define krb5_seteuid(EUID) setresuid(getuid(), (uid_t)(EUID), geteuid()) #elif defined(HAVE_SETREUID) # define krb5_seteuid(EUID) setreuid(geteuid(), (uid_t)(EUID)) #else /* You need to add a case to deal with this operating system.*/ # define krb5_seteuid(EUID) (errno = EPERM, -1) #endif #ifdef HAVE_SETEGID # define krb5_setegid(EGID) (setegid((gid_t)(EGID))) #elif defined(HAVE_SETRESGID) # define krb5_setegid(EGID) (setresgid(getgid(), (gid_t)(EGID), getegid())) #elif defined(HAVE_SETREGID) # define krb5_setegid(EGID) (setregid(getegid(), (gid_t)(EGID))) #else /* You need to add a case to deal with this operating system.*/ # define krb5_setegid(EGID) (errno = EPERM, -1) #endif
Je nachdem, was auf dem System zur Verfügung steht werden diese Makros auf reale oder effektive ID-Aufrufe abgebildet. Im zweiten Ifdef-Block liegt nun das Problem, da “HAVE_SETEGID”, “HAVE_SETRESGID” und “HAVE_SETREGID” zuvor von Autoconf nicht gesetzt wurden. Dadurch fällt “krb5_setegid()” immer auf den Fall “(errno = EPERM, -1)” zurück: Die Funktion bewirkt außer der Rückgabe des Fehlercodes -1 nichts. Das ist soweit in Ordnung, weil der Fehlercode -1 im weiteren Programmverlauf eigentlich als Problem beim Setzen der GID interpretiert werden sollte.
Allerdings setzt dies voraus, dass alle Funktionen, die “krb5_setegid()” später aufrufen, auch tatsächlich dessen Rückgabewert inspizieren – und bei einem entsprechenden Fehlercode abbrechen. In der Datei “gssftp/ftpd/ftpd.c” findet sich allerdings folgender Programmcode in der “login()”-Funktion:
static void
login(passwd, logincode) char *passwd; int logincode;
{ if (have_creds) {
#ifdef GSSAPI const char *ccname = krb5_cc_get_name(kcontext, ccache); chown(ccname, pw->pw_uid, pw->pw_gid);
#endif } (void) krb5_setegid((gid_t)pw->pw_gid); (void) initgroups(pw->pw_name, pw->pw_gid);
...
Der Code versucht, die Gruppenrechte mit “krb5_setegid()” zu ändern, wobei er den Rückgabewert komplett ignoriert. In einer solchen Konfiguration erhält ein Angreifer mit gültigem Account also ohne weiteres Zutun hohe Gruppenrechte, mit denen eher unbefugt auf das System zugreifen kann.
Die einzelnen Programmierfehler lassen sich folgerndermaßen korrigieren: In “configure.ac” muss zunächst “AC_CHECK_FUNCS(setegid setregid setresgid)” hinzugefügt werden, um zu prüfen, welche Funktionen auf dem System zur Verfügung stehen. Der Code der “login()”-Funktion in “ftpd.c” muss zudem sicherstellen, dass die Rückgabewerte kontrolliert werden. Hierzu sind die letzten beiden Aufrufe oben durch Folgendes zu ersetzen:
if (krb5_setegid((gid_t)pw->pw_gid) < 0) { reply(550, "Can't set egid."); goto bad;
}
if (geteuid() == 0 && initgroups(pw->pw_name, pw->pw_gid) < 0) { reply(550, "Can't initgroups"); goto bad;
}
Sicherheitshalber sieht das offizielle Patch auch noch eine Korrektur in der Datei “k5-util.h” vor.
Betroffen sind die Versionen krb5-appl-1.0.1 und älter.

