
Root-Exploit für CVE-2021-4034 (PwnKit), das den Out-of-Bounds-Schreibzugriff von pkexec ausnutzt, um auf Linux-Systemen Privilegien auf Root zu eskalieren.
Root-Exploit für die PwnKit-Schwachstelle. Den ursprünglichen Bericht finden Sie hier.
Verwenden Sie dieses Exploit nur mit ausdrücklicher Genehmigung der Eigentümer des Zielsystems.
Außer libc sind keine Abhängigkeiten erforderlich. Führen Sie einfach make aus.
Die Ausführung ohne Optionen führt das Exploit aus:
[linux@linux ~]$ ./exploit
-----------------------------------------------------------------------------
__\ / __ __ _ __ _ __ | \ / _ ___
/ V |_ --- _)/ \ _)/| ---|_|/ \__)|_| | V |_) _/|_|
\__ |__ /__\_//__ | |\_/__) | | | \/__| |
-----------------------------------------------------------------------------
sh-5.1# whoami
root
sh-5.1#
Sie können den Pfad zu pkexec sowie den "from"-Zeichensatz anpassen:
[linux@linux ~]$ ./exploit -h
...
./exploit [-c] [-h] [-f from_charset] [-p /path/to/pkexec]
-----------------------------------------------------------------------------
-c Nur Teardown - kein Exploit
-p <path> Pfad zu pkexec (Standard: "/usr/bin/pkexec")
-f <from_charset> Benutzerdefinierter "from"-Zeichensatz (Standard: "UTF-8")
-h Diese Meldung anzeigen
GIO_USE_VFS auf sich?!Ich habe in den sozialen Medien einige Leute gesehen, die fragten, warum einige Exploits fehlschlagen, wenn GIO_USE_VFS= nicht definiert ist? Warum funktionieren sie mit den älteren Versionen?
Der Commit daf3d5c2d15466a267221fcb099c59c870098e03 in polkit ist der Übeltäter.
Hier ist der relevante Teil des Diffs:
--- a/src/programs/pkexec.c
+++ b/src/programs/pkexec.c
@@ -503,6 +503,9 @@ main (int argc, char *argv[])
opt_user = NULL;
local_agent_handle = NULL;
+ /* Disable remote file access from GIO. */
+ setenv ("GIO_USE_VFS", "local", 1);
+
/* check for correct invocation */
if (geteuid () != 0)
{
Versionen vor diesem Commit sind ohne die Notwendigkeit, die Variable GIO_USE_VFS zu definieren, ausnutzbar. Die Versionen danach sind nicht ausnutzbar, es sei denn, diese Variable ist definiert.
Der Zweck des Commits ist eigentlich eine Irreführung. Es geht nicht darum, was die Variable bedeutet, sondern darum, wie ihre Präsenz die Programmumgebung beeinflusst. Für die Wahrheit müssen wir in libc schauen.
Die Umgebung eines Prozesses in libc wird durch ein Array von char *s repräsentiert,
auf das diese globale Variable zeigt:
char **environ;
environ lebt auf dem Heap und wird gelegentlich verschoben. Sie wissen
vielleicht schon, worauf das hinausläuft. Schauen Sie sich dieses Code-Snippet aus
setenv.c an:
#if !_LIBC
# define __environ environ
# ifndef HAVE_ENVIRON_DECL
extern char **environ;
# endif
#endif
int
__add_to_environ (const char *name, const char *value, const char *combined,
int replace)
{
char **ep;
// ... skipping
ep = __environ;
size = 0;
if (ep != NULL)
{
for (; *ep != NULL; ++ep)
if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
break;
else
++size;
}
if (ep == NULL || __builtin_expect (*ep == NULL, 1))
{
char **new_environ;
/* We allocated this space; we can extend it. */
new_environ = (char **) realloc (last_environ,
(size + 2) * sizeof (char *));
// ... skipping
last_environ = __environ = new_environ;
}
__add_to_environ() wird sowohl von setenv(3) als auch von putenv(3) aufgerufen, um zu erreichen,
was sie versprechen - eine Umgebungsvariable zu setzen. Wenn die betreffende Umgebungsvariable
nicht definiert ist, muss environ neu allokiert werden, um einen neuen Eintrag (einen Zeiger auf das neue Umgebungs-key=value-Paar) aufzunehmen. Wenn sie
definiert ist, hat sich die Größe des environ-Arrays nicht geändert, und daher gibt es keinen
Grund für eine Neuallokation. Der Kürze halber habe ich diesen Teil des Codes weggelassen - ich
empfehle Ihnen, ihn sich anzusehen.
Kommen wir nun zurück zum Exploit. Wenn Sie so weit gekommen sind, kennen Sie wahrscheinlich
bereits die Methodik hinter diesem Exploit (falls nicht, schauen Sie sich bitte den
ursprünglichen Bericht an).
Wir versuchen, eine Umgebungsvariable einzuschmuggeln, indem wir leere Programmargumente
(argv) an pkexec übergeben. Wenn argc wirklich leer ist (nicht einmal ein Programmname),
kollidieren die angrenzenden Umgebungsvariablen mit den Argumenten.
Wir missbrauchen dieses Verhalten, um pkexec zu zwingen, einen kanonischen Pfad einer
Zieldatei in die Umgebung zu schreiben. Bevor wir jedoch zu diesem Teil des
Codes gelangen, passiert Folgendes:
setenv ("GIO_USE_VFS", "local", 1);
Wenn diese Variable nicht in der Umgebung vorhanden ist, wird environ
neu allokiert und kollidiert daher nie mit argv. Infolgedessen wirkt sich der Out-of-Bounds-
Schreibzugriff nicht auf die Programmumgebung aus, wodurch das
Exploit fehlschlägt.