polkit 的 pkexec 中的本地权限提升 (CVE-2021-4034)

==================================================== =======================
内容
==================================================== =======================

概括
分析
开发
致谢
时间线


==================================================== =======================
概括
==================================================== =======================

我们发现了本地特权升级(从任何用户到 root)
polkit 的 pkexec,一个 SUID 根程序,默认安装在
每个主要的 Linux 发行版:

“Polkit(以前称为 PolicyKit)是一个用于控制系统范围的组件
类 Unix 操作系统中的特权。它提供了一种有组织的方式
让非特权进程与特权进程通信。[...]
也可以使用 polkit 来执行提升权限的命令
使用命令 pkexec 后跟命令的权限
被执行(有root权限)。”(维基百科)

这个漏洞是攻击者梦想成真:

- pkexec 默认安装在所有主要的 Linux 发行版上(我们
  被利用的 Ubuntu、Debian、Fedora、CentOS 和其他发行版是
  可能也可以利用);

- pkexec 自 2009 年 5 月创建以来就存在漏洞(提交 c8c3d83,
  "添加一个 pkexec(1) 命令");

- 任何非特权本地用户都可以利用此漏洞获取
  完全根权限;

- 虽然这个漏洞在技术上是内存损坏,但它是
  以独立于架构的方式立即、可靠地利用;

- 即使 polkit 守护进程本身没有运行,它也是可利用的。

我们不会立即发布我们的漏洞利用;但是,请注意
这个漏洞很容易被利用,其他研究人员可能
在补丁可用后不久发布他们的漏洞利用。如果不
补丁适用于您的操作系统,您可以删除
来自 pkexec 的 SUID 位作为临时缓解措施;例如:

# chmod 0755 /usr/bin/pkexec

这个漏洞是我们最美丽的发现之一;兑现
它的记忆,我们推荐听 DJ Pone 的“Falken's Maze”(双
双关语)在阅读本公告时。非常感谢你!


==================================================== =======================
分析
==================================================== =======================

pkexec 是一个类似 sudo 的 SUID-root 程序,其描述如下:
手册页:

-------------------------------------------------- ----------------------
名称
       pkexec - 以另一个用户身份执行命令

概要
       pkexec [--version] [--disable-internal-agent] [--help]

       pkexec [--user 用户名] 程序 [参数...]

描述
       pkexec 允许授权用户以另一个用户的身份执行 PROGRAM
       用户。如果未指定 PROGRAM,则将运行默认 shell。
       如果没有指定用户名,则程序将被执行
       作为管理超级用户 root。
-------------------------------------------------- ----------------------

pkexec 的 main() 函数的开头处理命令行
参数(第 534-568 行),并搜索要执行的程序
(如果它的路径不是绝对的)在 PATH 环境的目录中
变量(第 610-640 行):

-------------------------------------------------- ----------------------
 第435章
 第436章
 ...
 534 for (n = 1;n < (guint) argc;n++)
 第535章
 ...
 第568章
 ...
 610 路径 = g_strdup (argv[n]);
 ...
 第629章
 第630章
 ...
 632 秒 = g_find_program_in_path(路径);
 ...
 第639章
 第640章
-------------------------------------------------- ----------------------

不幸的是,如果命令行参数 argc 的数量为 0(如果
我们传递给 execve() 的参数列表 argv 为空,即 {NULL}),那么
argv[0] 为 NULL(参数列表的终止符)并且:

- 在第 534 行,整数 n 永久设置为 1;

- 在第 610 行,从 argv[1] 越界读取指针路径;

- 在第 639 行,指针 s 被越界写入 argv[1]。

但是从这个越界的 argv[1] 中读取和写入的到底是什么?
要回答这个问题,我们必须简短地离题。当我们 execve() 一个新的
程序,内核复制我们的参数和环境字符串和
指向新程序堆栈末尾的指针(argv 和 envp);为了
例子:

|---------+---------+------+------------|---------+ ---------+-----+------------|
| 参数 [0] | 参数 [1] | ... | argv[argc] | 环境 [0] | 环境[1] | ... | envp[envc] |
|----|----+----|----+-----+-----|------|----|----+ ----|----+-----+-----|------|
     VVVVVV
 "程序" "-option" NULL "值" "PATH=name" NULL

显然(因为 argv 和 envp 指针在内存中是连续的),
如果 argc 为 0,则越界 argv[1] 实际上是 envp[0],即
指向我们的第一个环境变量“value”的指针。最后:

- 在第 610 行,读取要执行的程序的路径
  argv[1] 越界(即 envp[0]),并指向“值”;

- 在第 632 行,此路径“值”被传递给 g_find_program_in_path()
  (因为“值”不是以斜线开头,在第 629 行);

- g_find_program_in_path() 搜索名为“value”的可执行文件
  在我们的 PATH 环境变量的目录中;

- 如果找到这样的可执行文件,则返回其完整路径
  pkexec 的 main() 函数(在第 632 行);

- 在第 639 行,此完整路径被越界写入 argv[1]
  (即 envp[0]),从而覆盖我们的第一个环境变量。

更确切地说:

- 如果我们的 PATH 环境变量是“PATH=name”,并且如果目录
  “名称”存在(在当前工作目录中)并包含一个
  名为“value”的可执行文件,然后是指向字符串的指针
  “名称/值”越界写入 envp[0];

- 或者,如果我们的 PATH 是“PATH=name=.”,并且目录是“name=.” 存在
  并包含一个名为“value”的可执行文件,然后是一个指向
  字符串“name=./value”被越界写入 envp[0]。

换句话说,这种越界写入允许我们重新引入一个
将“不安全”环境变量(例如 LD_PRELOAD)放入 pkexec 的
环境; 这些“不安全”变量通常会被删除(通过 ld.so)
从 main() 函数之前的 SUID 程序环境
叫。我们将在下面利用这个强大的原语
部分。

最后一分钟说明:polkit 还支持非 Linux 操作系统,例如
作为 Solaris 和 *BSD,但我们尚未调查它们的可利用性;
然而,我们注意到 OpenBSD 是不可利用的,因为它的内核
如果 argc 为 0,则拒绝 execve() 程序。


==================================================== =======================
开发
==================================================== =======================

我们的问题是:要成功利用这个漏洞,
我们应该将“不安全”变量重新引入 pkexec 的环境吗?
我们的选择是有限的,因为在越界后不久写
(在第 639 行),pkexec 完全清除其环境(在第 702 行):

-------------------------------------------------- ----------------------
 第639章
 ...
 657 for (n = 0; environment_variables_to_save[n] != NULL; n++)
 第658章
 第659章
 ...
 662 值 = g_getenv(键);
 ...
 第670章
 ...
 第675章
 ...
 第702章
-------------------------------------------------- ----------------------

我们的问题的答案来自于 pkexec 的复杂性:打印一个
到 stderr 的错误消息,pkexec 调用 GLib 的函数 g_printerr()
(注意:GLib 是 GNOME 库,而不是 GNU C 库,即 glibc);
例如,函数 validate_environment_variable() 和
log_message() 调用 g_printerr() (在第 126 和 408-409 行):

-------------------------------------------------- ----------------------
  88 log_message(gint级别,
  89 gboolean print_to_stderr,
  90 const gchar *格式,
  91 ...)
  92 {
 ...
 125 如果(打印到标准错误)
 126 g_printerr ("%s\n", s);
-------------------------------------------------- ----------------------
 第383章
 第384章
 第385章
 ...
 第406章
 407 "在 /etc/shells 文件中找不到 SHELL 变量的值");
 408 g_printerr ("\n"
 409 "此事件已报告。\n");
-------------------------------------------------- ----------------------

g_printerr() 通常打印 UTF-8 错误信息,但它可以打印
如果环境变量 CHARSET 不是另一个字符集中的消息
UTF-8(注意:CHARSET 不是安全敏感的,它不是“不安全的”
环境变量)。将消息从 UTF-8 转换为另一种
charset,g_printerr() 调用glibc 的函数iconv_open()。

要将消息从一个字符集转换为另一个, iconv_open() 执行
小型共享库;通常,这些三元组(“from”字符集,“to”
字符集和库名称)从默认配置文件中读取,
/usr/lib/gconv/gconv 模块。或者,环境变量
GCONV_PATH 可以强制 iconv_open() 读取另一个配置文件;
自然, GCONV_PATH 是“不安全”的环境变量之一
(因为它会导致任意库的执行),并且是
因此被 ld.so 从 SUID 程序的环境中删除。

不幸的是,CVE-2021-4034 允许我们将 GCONV_PATH 重新引入
pkexec 的环境,并以 root 身份执行我们自己的共享库。

重要提示:这种利用技术会在日志中留下痕迹(无论是
“在 /etc/shells 文件中找不到 SHELL 变量的值”或
“环境变量的值 [...] 包含可疑内容”)。
但是,请注意,此漏洞也可以在没有的情况下被利用
在日志中留下任何痕迹,但这留作练习
感兴趣的读者。

有关 pkexec、GLib 和 GCONV_PATH 的进一步讨论,请参阅
Tavis Ormandy、Jakub Wilk 和 Yuki Koike 的以下帖子:

https://www.openwall.com/lists/oss-security/2014/07/14/1
https://www.openwall.com/lists/oss-security/2017/06/23/8
https://hugeh0ge.github.io/2019/11/04/Getting-Arbitrary-Code-Execution-from-fopen-s-2nd-Argument/


==================================================== =======================
致谢
==================================================== =======================

我们感谢 polkit 的作者、红帽产品安全和
distros@openwall 为披露此内容提供了宝贵的帮助
脆弱性。我们也感谢 Birdy Nam Nam 的鼓舞人心的工作。


==================================================== =======================
时间线
==================================================== =======================

2021-11-18:咨询发送至 secalert@redhat。

2022-01-11:咨询和补丁发送到 distros@openwall。

2022-01-25:协调发布日期(世界标准时间下午 5:00)。

来源:https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值