
Exploit racine pour CVE-2021-4034 (PwnKit) qui abuse de l'écriture hors limites de pkexec pour élever les privilèges à root sur les systèmes Linux.
Exploit racine pour la vulnérabilité PwnKit. Consultez le rapport original ici.
Utilisez cet exploit avec l'autorisation expresse des propriétaires du système cible.
Aucune dépendance nécessaire en dehors de libc. Exécutez simplement make.
L'exécution sans options lancera l'exploit :
[linux@linux ~]$ ./exploit
-----------------------------------------------------------------------------
__\ / __ __ _ __ _ __ | \ / _ ___
/ V |_ --- _)/ \ _)/| ---|_|/ \__)|_| | V |_) _/|_|
\__ |__ /__\_//__ | |\_/__) | | | \/__| |
-----------------------------------------------------------------------------
sh-5.1# whoami
root
sh-5.1#
Vous pouvez personnaliser le chemin vers pkexec ainsi que le jeu de caractères « from » :
[linux@linux ~]$ ./exploit -h
...
./exploit [-c] [-h] [-f from_charset] [-p /path/to/pkexec]
-----------------------------------------------------------------------------
-c Uniquement le démontage - pas d'exploitation
-p <path> Chemin vers pkexec (défaut : "/usr/bin/pkexec")
-f <from_charset> Jeu de caractères « from » personnalisé (défaut : "UTF-8")
-h Afficher ce message
GIO_USE_VFS ?!J'ai vu quelques personnes sur les réseaux sociaux demander pourquoi certains exploits échouent si
GIO_USE_VFS= n'est pas défini ? Pourquoi fonctionnent-ils avec les versions plus anciennes ?
Le commit daf3d5c2d15466a267221fcb099c59c870098e03 dans polkit est le coupable.
Voici la partie pertinente du 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)
{
Les versions antérieures à ce commit sont exploitables sans avoir besoin de définir la
variable GIO_USE_VFS. Les versions postérieures - ne sont pas exploitables à moins que cette variable soit définie.
Le but de ce commit est en réalité une fausse piste. Ce n'est pas ce que la variable
signifie, c'est comment sa présence affecte l'environnement du programme. Pour la vérité, nous devons nous tourner vers libc.
L'environnement d'un processus dans libc est représenté par un tableau de char *,
pointé par cette variable globale :
char **environ;
environ vit sur le tas et est occasionnellement relocalisé. Vous savez peut-être
déjà où cela mène. Regardez cet extrait de code de
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;
// ... skipping
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;
/* We allocated this space; we can extend it. */
new_environ = (char **) realloc (last_environ,
(size + 2) * sizeof (char *));
// ... skipping
last_environ = __environ = new_environ;
}
__add_to_environ() est appelé à la fois par setenv(3) et putenv(3) pour accomplir
ce qu'ils promettent - définir une variable d'environnement. Si la variable d'environnement en
question n'est pas définie, environ doit être réalloué pour accueillir
une nouvelle entrée (un pointeur vers la nouvelle paire clé=valeur de l'environnement). Si elle est
définie, la taille du tableau environ n'a pas changé et il n'y a donc aucune
raison de réallouer. Par souci de concision, j'ai omis cette partie du code - je
vous encourage à la consulter.
Revenons maintenant à l'exploit. Si vous êtes arrivé jusqu'ici, vous connaissez probablement
déjà la méthodologie derrière cet exploit (si ce n'est pas le cas, veuillez consulter le
rapport original).
Nous essayons d'introduire subrepticement une variable d'environnement en passant des arguments de programme vides
(argv) à pkexec. Lorsque argc est véritablement vide (pas même un nom de
programme), les variables d'environnement, qui sont adjacentes, entrent en conflit avec les arguments.
Nous abusons de ce comportement pour forcer pkexec à écrire un chemin canonique d'un
exécutable cible dans l'environnement. Cependant, avant d'atteindre cette partie du
code, cela se produit :
setenv ("GIO_USE_VFS", "local", 1);
Si cette variable n'est pas présente dans l'environnement, environ sera
réalloué, n'entrant ainsi jamais en conflit avec argv. Par conséquent, l'écriture hors limites
n'affectera pas l'environnement du programme, ce qui fera échouer l'exploit.