Polkit 是一个应用程序级别的工具集,通过定义和审核权限规则,实现不同优先级进程间的通讯:控制决策集中在统一的框架之中,决定低优先级进程是否有权访问高优先级进程。
Polkit 在系统层级进行权限控制,提供了一个低优先级进程和高优先级进程进行通讯的系统。和 sudo 等程序不同,Polkit 并没有赋予进程完全的 root 权限,而是通过一个集中的策略系统进行更精细的授权。
这个漏洞是本地触发,只有在获得有限权限的前提下提升至 root。请大家综合考虑自己的业务影响做好升级工作。
PwnKit 漏洞的潜在影响
成功利用此漏洞允许任何非特权用户在易受攻击的主机上获得 root 特权。开发漏洞利用, 获得完整的 root 权限。这个漏洞已经隐藏了 12 年多,并影响自 2009 年 5 月第一个版本以来的所有 pkexec 版本(commit c8c3d83,“Add a pkexec(1) command”)。
漏洞披露时间表
- 2021-11-18:咨询发送至 secalert@redhat。
- 2022-01-11:咨询和补丁发送到 distros@openwall。
- 2022-01-25:协调发布日期(世界标准时间下午 5:00)。
PwnKit 漏洞的技术细节
pkexec 的 main() 函数的开头处理命令行参数(第 534-568 行),并在 PATH 环境变量的目录中搜索要执行的程序(如果其路径不是绝对路径)(第 610-640 行) ):
435 main (int argc, char *argv[]) 436 { ... 534 for (n = 1; n < (guint) argc; n++) 535 { ... 568 } ... 610 path = g_strdup (argv[n]); ... 629 if (path[0] != '/') 630 { ... 632 s = g_find_program_in_path (path); ... 639 argv[n] = path = s; 640 }
不幸的是,如果命令行参数 argc 的数量为 0——这意味着如果我们传递给 execve() 的参数列表 argv 为空,即 {NULL}——那么 argv[0] 为 NULL。这是参数列表的终止符。所以:
- 在第 534 行,整数 n 永久设置为 1;
- 在第 610 行,从 argv[1] 越界读取指针路径;
- 在第 639 行,指针 s 被越界写入 argv[1]。
但是从这个越界的 argv[1] 中读取和写入的到底是什么?
要回答这个问题,我们必须简短地离题。当我们 execve() 一个新程序时,内核将我们的参数、环境字符串和指针(argv 和 envp)复制到新程序堆栈的末尾;例如:
|---------+---------+-----+------------|---------+---------+-----+------------| | 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
显然,因为 argv 和 envp 指针在内存中是连续的,如果 argc 为 0,那么越界 argv[1] 实际上是 envp[0],指向我们的第一个环境变量“value”的指针。最后:
- 在第610行,将要执行的程序的路径从argv[1](即envp[0])中越界读取,并指向“value”;
- 在第 632 行,这个路径“value”被传递给 g_find_program_in_path()(因为“value”不是以斜线开头,在第 629 行);
- 然后,g_find_program_in_path() 在我们的 PATH 环境变量的目录中搜索一个名为“value”的可执行文件;
- 如果找到这样的可执行文件,则将其完整路径返回给 pkexec 的 main() 函数(在第 632 行);
- 最后,在第 639 行,这个完整路径被越界写入 argv[1](即 envp[0]),从而覆盖了我们的第一个环境变量。
所以,更准确地说:
- 如果我们的 PATH 环境变量是“PATH=name”,并且如果目录“name”存在(在当前工作目录中)并且包含一个名为“value”的可执行文件,则写入一个指向字符串“name/value”的指针越界到 envp[0];
或者
- 如果我们的 PATH 是“PATH=name=.”,并且目录是“name=.” 存在并包含一个名为“value”的可执行文件,然后将指向字符串“name=./value”的指针越界写入 envp[0]。
换句话说,这种越界写入允许我们将一个“不安全”的环境变量(例如,LD_PRELOAD)重新引入 pkexec 的环境。这些“不安全”变量通常在调用 main() 函数之前从 SUID 程序的环境中删除(通过 ld.so)。我们将在下一节中利用这个强大的原语。
最后一分钟说明:polkit 还支持非 Linux 操作系统,例如 Solaris 和 *BSD,但我们尚未调查它们的可利用性。然而,我们注意到 OpenBSD 是不可利用的,因为如果 argc 为 0,它的内核拒绝执行程序。
漏洞复现
环境:kali
使用exp 直接提权到root权限
image-20220126183114153
修复方案
建议用户立即针对此漏洞应用补丁程序