CVE编号:
CVE-2021-4034
背景
pkexec 应用程序是一个 setuid 工具,旨在允许非特权用户根据预定义的策略以特权用户身份运行命令。由于当前版本的 pkexec 无法正确处理调用参数计数,并最终会尝试将环境变量作为命令执行。 攻击者可以通过控制环境变量,从而诱导 pkexec 执行任意代码。利用成功后,可导致非特权用户获得管理员权限。
漏洞分析
此问题自从pkexec 2009年的一次提交(commit c8c3d83, "Add a pkexec(1) command"),即存在安全风险。
根据pkexec的main函数:
------------------------------------------------------------------------
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,即运行pkexec命令行参数为null的情况:
- line 534, n的值为1; - line 610, argv[n]为argv[1]指针读取越界; - line 639, 指针s被越界写为argv[1].
当内核函数execve()一个新程序时,内核将我们的参数和环境字符串和指针(argv 和 envp)复制到新程序堆栈的末尾,所以argv 和 envp 指针在内存中是连续的。如果 argc 为 0,那么越界 argv[1] 实际上就是envp[0],即指向我们的第一个环境变量的指针。
如何利用?
为了向 stderr 打印错误消息,pkexec调用了GLib的函数g_printerr() 。
------------------------------------------------------------------------
88 log_message (gint level,
89 gboolean print_to_stderr,
90 const gchar *format,
91 ...)
92 {
...
125 if (print_to_stderr)
126 g_printerr ("%s\n", s);
------------------------------------------------------------------------
383 validate_environment_variable (const gchar *key,
384 const gchar *value)
385 {
...
406 log_message (LOG_CRIT, TRUE,
407 "The value for the SHELL variable was not found the /etc/shells file");
408 g_printerr ("\n"
409 "This incident has been reported.\n");
------------------------------------------------------------------------
g_printerr()通常打印 UTF-8错误消息,但如果环境变量 CHARSET 不是UTF-8 ,它可以打印另一个字符集中的消息。要将消息从 UTF-8 转换为另一个字符集,g_printerr()调用glibc的函数iconv_open()。
要将消息从一个字符集转换为另一个字符集,iconv_open() 会执行一个共享库。这个共享库是/usr/lib/gconv/gconv-modules所定义的。或者,环境变量GCONV_PATH 可以强制 iconv_open() 读取另一个配置文件,因为利用以上环境变量覆盖的漏洞,我们可以使得pkexec以root权限执行我们自定义的共享库。
参考POC:
github
修复建议:
升级polkit至安全版本。
或使用以下命令缓解:
chmod 0755 /usr/bin/pkexec