Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2021-3560 — Linux PolKit의 로컬 권한 상승 취약점인 CVE-2021-3560에 대한 심층 분석. 근본 원인 분석, 익스플로잇 메커니즘, 그리고 루트 액세스로 이어지는 경쟁 조건(race condition)의 시연을 포함합니다. | Kitploit
도구/GitHubGitHub/chenaotian/cve-2021-3560
Privilege EscalationVulnerability AnalysisExploitationBinary AnalysisLearning & Education
GitHubchenaotian/cve-2021-3560

CVE-2021-3560

Linux PolKit의 로컬 권한 상승 취약점인 CVE-2021-3560에 대한 심층 분석. 근본 원인 분석, 익스플로잇 메커니즘, 그리고 루트 액세스로 이어지는 경쟁 조건(race condition)의 시연을 포함합니다.

저장소 보기
9364년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

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 기본 이미지 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

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를 가져온 다음 콜백 함수 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로 설정한다:

  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 버스 쪽에서 예외가 발생하여 콜백 함수가 예외 플래그만 설정하고는 data 데이터를 전혀 처리하지 않은 경우이다. data는 0으로 초기화되므로, 이후 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

    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이라는 이름의 버스를 등록하고, 세 개의 콜백 함수가 있으며, 주로 on_bus_acquired를 주목해야 한다. 이 함수는 버스가 접근될 때 호출된다.

  3. on_bus_acquired 함수에서 등록 유형 함수인 polkit_backend_authority_register를 직접 호출한다

  4. polkit_backend_authority_register 함수에서 인터페이스 객체 org.freedesktop.PolicyKit1.Authority를 등록하고 콜백 함수 테이블 server_vtable을 전달한다. 취약점 트리거 지점은 바로 이 콜백 함수 테이블 중 하나에 있다:

    policykit-1_0.113.orig\polkit-0.113\src\polkitbackend\polkitbackendauthority.c : 1333 : server_vtable

    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 함수가 호출된다.

  5. server_handle_check_authorization 함수에서 polkit_backend_authority_check_authorization을 호출한다

도구 다운로드