Open Source im professionellen Einsatz

Kernel Root Exploit: Fehlerhafte Schlüsselbund-Verwaltung

In dem Linux Kernel schlummert seit 2012 eine kritische Sicherheitslücke. Diese erlaubt es einem lokalen Angreifer Root-Rechte auf dem System zu erlangen. Das Problem findet sich in dem Code zur Schlüsselbund-Verwaltung. Hier kann es zu einem Integer-Overflow-Fehler kommen, der von einem Angreifer ausgenutzt werden kann.

Anfällig für die Schwachstelle sind alle Kernel-Versionen seit Release 3.8. Damit sind auch zahlreiche Android-System von dem Problem betroffen. Die Schlüsselbund-Komponente wird von Userspace-Programmen und Treibern zum Verwalten sicherer Daten verwendet. Dazu werden verschiedene Syscalls bereitgestellt: »keyctl«, »add_key«, »request_key«. Mit »keyctl« kann ein Prozess einen Schlüsselbund anlegen. Dabei kann er dann entweder einen Namen hierfür oder Null übergeben. Verschiedene Prozesse können auf denselben Schlüsselbund über den Namen zugreifen. Sollte ein Prozess schon einen Schlüsselbund besitzen, so kann via »keyctl« auch ein neuer Schlüsselbund zugewiesen werden. Das Schlüsselbund-Objekt zählt dabei mit einem internen Zähler (usage) mit wie oft es verwendet wird.

Das Problem tritt nun auf, wenn ein Prozess versucht den aktuellen Schlüsselbund durch genau den gleichen zu ersetzen. Die folgende Funktion wird hierbei aufgerufen und springt nach einigen If()-Abfragen zu dem »error2« Label.

long join_session_keyring(const char *name)
{
    new = prepare_creds();
    keyring = find_keyring_by_name(name, false);
    if (PTR_ERR(keyring) == -ENOKEY) {
    /* not found - try and create a new one */
           keyring = keyring_alloc(
                      name, old->uid, old->gid, old,
                      KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK,
                      KEY_ALLOC_IN_QUOTA, NULL);
          if (IS_ERR(keyring)) {
                      ret = PTR_ERR(keyring);
                      goto error2;
           }
    } else if (IS_ERR(keyring)) {
           ret = PTR_ERR(keyring);
           goto error2;
   } else if (keyring == new->session_keyring) {
          ret = 0;               goto error2; //<-- Hier ist der Fehler, weil key_put nicht aufgerufen wird
  }
/* we've got a keyring - now install it */

 Das Problem besteht nun darin, dass in diesem Fall die key_put()-Funktion nicht aufgerufen wird: Diese Funktion verwaltet die key->usage Variable und sollte in oben gezeigtem Szenario dafür sorgen, dass der usage-Zähler züruckgesetzt wird und nicht immer weiter erhöht wird. Da diese Funktion nun aber nicht aufgerufen wird, ist es für einen Angreifer möglich die usage-Variable beliebig zu erhöhen. Damit kann der Angreifer die Variable bis zu einem Overflow erhöhen. Da es sich um eine Int-Variable handelt, ist es auch möglich hier negative Werte zu erreichen.

Auch ist es möglich den usage-Zähler einach auf Null zu setzen, wenn man obigen Trick nur 2^32 mal durchführt. Da hat dann zur Folge, dass der Kernel fälschlicherweise denkt, dass dieses Objekt von keinem Prozess mehr verwendet wird, und es freigibt. Dies führt dann zu einem typische Use-After-Free-Fehler. Dies eröffnet dann dem Angreifer die Möglichkeit durch einen entsprechenden Exploit Befehle mit Root-Rechten auszuführen.

Der Exploit für diese Schwachstelle wurde ebenfalls schon hier.

comments powered by Disqus

Ausgabe 11/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Stellenmarkt

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