zoukankan      html  css  js  c++  java
  • Kylin 复现 CVE-2021-3560

    CVE-2021-3560

    CVE-2021-3560 通过利用 polkit 中存在的漏洞可以达到使得非特权本地用户获得系统root权限的目的。
    复现环境:ubuntukylin-20.04-pro

    原理简述

    polkit

    polkit 是一个应用程序框架,通过定义和审核权限规则,实现不同有限级进程间的通讯。每当用户中的某个进程尝试在系统环境中执行操作时,系统就会查询 polkit 。用户发起的会话由授权和身份验证代理构成,授权以系统消息总线上的一个服务是形式来实现,而身份验证代理用于对启动会话的当前用户进行身份认证。

    源码分析

    首先放上源码:

    static gboolean
    polkit_system_bus_name_get_creds_sync (...)
    {
      ...
      g_dbus_connection_call (...
    			  "GetConnectionUnixUser",      /* method */
                              ...
    			  on_retrieved_unix_uid_pid, // callback funtion
    			  &data); // data is passed to the callback function along with the reply from the method
    
      g_dbus_connection_call (...
    			  "GetConnectionUnixProcessID", /* method */
                              ...
    			  on_retrieved_unix_uid_pid, // callback funtion
    			  &data); // data is passed to the callback function along with the reply from the method
    
      while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error)) // block while on_retrieved_unix_uid_pid() is not called yet
        g_main_context_iteration (tmp_context, TRUE);
    
      ...  
    
      if (out_uid) // TRUE
        *out_uid = data.uid; // set it even if there is an error [!]
      if (out_pid) // FALSE
        *out_pid = data.pid; // set it even if there is an error [!]
      ret = TRUE; // return TRUE even if there is an error [!]
     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;
    }
    
    on_retrieved_unix_uid_pid (...)
    {
      ...
      v = g_dbus_connection_call_finish ((GDBusConnection*)src, res,
    				     data->caught_error ? NULL : data->error); // finish and get the reply
      if (!v) // error ??
        {
          data->caught_error = TRUE;
        }
      else
        {
          ...
          if (!data->retrieved_uid) // GetConnectionUnixUser method
    	{
    	  data->retrieved_uid = TRUE;
    	  data->uid = value;
    	}
          else
    	{
    	  g_assert (!data->retrieved_pid); // GetConnectionUnixProcessID method
    	  data->retrieved_pid = TRUE;
    	  data->pid = value;
    	}
        }
    }
    
    

    整个流程大致如下:

    1. polkit_system_bus_name_get_creds_sync() 函数调用方法 GetConnectionUnixUser 和 GetConnectionUnixProcessID 获取发起会话的用户的 UID 和会话进程的 PID,并调用回调函数on_retrieved_unix_uid_pid() 。
    2. 回调函数 on_retrieved_unix_uid_pid() 的作用将获取的 UID 和 PID 写入变量 data->uid 和 data->pid 中。如果 UID 或 PID 获取失败,函数会将 data->caught_error 设置为 TRUE 并返回 polkit_system_bus_name_get_creds_sync() 函数。
    3. polkit_system_bus_name_get_creds_sync() 函数中运用 while 循环等待回调函数结束,当用 UID 和 PID 被找到或者发生错误(data->caught_error = TRUE),函数将继续执行将 *out_uid 和 *out_pid 设置成对应的值并返回 TRUE。

    到这里漏洞的成因就很明显了,当获取 UID 和 PID 失败的时候,程序只是将 data->caught_error 设置为 TRUE 而没有产生报错,会继续运行下去将 *out_uid 设置成 0 。(数据被初始化为 0,而有没有获取到 pid,所以最后返回是 *out_uid 的值为 0)。

    check_authorization_sync (...)
    {
      ...
      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;
    
      /* special case: uid 0, root, is _always_ authorized for anything */
      if (identity_is_root_user (user_of_subject)) // true
        {
          result = polkit_authorization_result_new (TRUE, FALSE, NULL); // authorize the caller
          goto out;
        }
      ...
    )
    

    接下来会进入 check_authorization_sync() 函数,check_authorization_sync() 函数通过 identity_is_root_user 判断 UID 是否为 root (root 的 UID 为 0),而之前已经将 UID 设置为 0,所以将会返回 TRUE 并对这次会话行为进行授权。

    利用思路

    我们可以利用这个漏洞来创建一个 root 权限的用户从而实现提权。首先开启一个会话使得系统调用 polkit 来进行身份认证和授权,发出信息后马上退出会话,这样在会话进程 PID 就不存在了,polkit_system_bus_name_get_creds_sync() 函数中的 GetConnectionUnixUser 和 GetConnectionUnixProcessID 方法将返回错误,然后在 on_retrieved_unix_uid_pid() 回调函数中data.caught_error 被设置为 TRUE,最后导致 UID 被设置为 0 绕过 identity_is_root_user 检测从而授权这次会话行为,这样我们就成功创建了一个 root 权限用户(polkit 调用过程中会多次请求 UID ,并不能确保进入 check_authorization_sync() 函数时 uid 还是我们 fake 的 uid(0),所以需要循环不断尝试直至绕过判断)。
    复现演示:

    内容参考

    github_CVE-2021-3560

  • 相关阅读:
    JAVA学习总结-基础语法
    git stash save -a 遇到的坑 , 弹出匿藏错误
    TP5模型belongsTo和hasOne这两个方法的区别
    phpstorm设置的快捷键突然失效了,提示: IdeaVim ...
    layui 时间选择器 不要秒的选项
    SQL 判断表是否存在 数据表不存在是致命错误
    layui 第三方组件 eleTree 树组件 树形选择器
    tp5 ThinkPHP5 自定义异常处理类
    TP5隐藏url中的index.php
    phpstorm断点调试 php.ini 文件中 Xdebug 配置
  • 原文地址:https://www.cnblogs.com/luoleqi/p/15207295.html
Copyright © 2011-2022 走看看