Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
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 — Write-up dettagliato ed exploit proof-of-concept per CVE-2021-4034 (escalation dei privilegi locali PolKit pkexec), incluso un ambiente di laboratorio Docker per analisi e debug pratici. | 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

Write-up dettagliato ed exploit proof-of-concept per CVE-2021-4034 (escalation dei privilegi locali PolKit pkexec), incluso un ambiente di laboratorio Docker per analisi e debug pratici.

Vedi Repository
123164 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:

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

Test dell'exp:

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:

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

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 argv[0] è \x00 e argv[1] punta già alle variabili d'ambiente! Quando viene letto argv[1], si esce dai limiti e si legge 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

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

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
    }
··· ···
··· ···
}
Scarica lo strumento