当前位置: 首页 > 技术干货 > CVE-2021-4034 pkexec本地提权漏洞分析

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

发表于:2022-02-22 15:08 作者: hope 阅读数(345人)

前言

Qualys 研究团队在 polkit 的 pkexec 中发现了一个内存损坏漏洞,该 SUID 根程序默认安装在每个主要的 Linux 发行版上。这个易于利用的漏洞允许任何非特权用户通过在其默认配置中利用此漏洞来获得易受攻击主机上的完全 root 权限。

pkexec

Polkit(以前称为 PolicyKit)是一个用于在类 Unix 操作系统中控制系统范围权限的组件。它为非特权进程与特权进程通信提供了一种有组织的方式。也可以使用 polkit 执行具有提升权限的命令,使用命令 pkexec 后跟要执行的命令(具有 root 权限)。

运行

image.png 

 

完成认证即可使用root权限执行文件

image.png 

 

漏洞复现

image.png 

 

漏洞分析

变量n初始值被设置为1,循环执行的次数为参数的个数,但是若参数个数为0时,此时变量n仍然为1,并且后面执行的语句会将argv[n]的值取出则造成了数组越界。

image.png 

 

test1.c

int main(int argc,char **argv)
{
printf("argc:%d\n",argc);
for(int i=0;i<argc;i++)
printf("argv[%d]:%s\n",i,argv[i]);
return 0;
}

将test.c编译执行,发现argv[0]为执行文件所在路径。并且参数个数也是为1,不会出现为0的情况。因此在这种情况下pkexec不会出现数组越界的情况。

image.png 

 

test2.c

#include <unistd.h>
int main()
{
char * const args[] = {NULL};
char * const environ[] = {NULL};
execve("./test",args,environ);
return 0;
}

将test2.c编译执行,使用execve函数启动test1文件,发现此时的argc0。

image.png 

 

那么使用execve函数调用pkexec文件就有可能会出现argc0,造成数组越界,下图为调试pkexec时的情况,此时的argc0,但是n1,通过源码可以看到后续会读取argv[n]的值,因此造成了数组越界。

image.png 

 

漏洞利用

pkexec文件会执行validate_environment_variable (key, value)用于检测key所对应的环境变量是否合法。

image.png 

 

若key所对应的环境变量不合法则会采用g_printerr函数打印信息,log_message函数内部也是调用了g_printerr进行信息的打印。

image.png 

 

exp利用g_printerr打印错误信息时特殊的执行流程进行getshell。

当Linux中CHARSET不是设置为UTF-8格式,则会调用iconv,用于将文本从一种编码转化为另一种编码。

在调用iconv之前需要通过使用iconv_open分配转化描述符号。

iconv_open函数受到GCONV_PATH环境变量影响

• 若GCONV_PATH未设置,那么iconv_open会加载系统默认的模块配置的缓存文件。

– 默认的配置文件位于/usr/lib/gconv/gconv-modules

• 若GCONV_PATH被设置,则会优先加载设置路径下的配置文件。

查看默认的配置文件信息gconv-modules,该配置文件指定了编码转换的键值对,并且通过指定的so文件执行转换。

image.png 

 

main_g_printerr.c文件中调用了g_printerr函数,而test3.c则是我们稍后需要编译成.so的文件,尝试利用g_printerr函数执行自行编译的so库。

//main_g_printerr.c
int main()
{
g_printerr("Hello World!\n");
return 0;
}

//test3.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>

void gconv()
{
}

void gconv_init(void *step)
{
printf("Hello test3\n");
exit(0);
}

//配置文件 gconv-modules
//将编码为ABCD转化为UTF-8,具体转化流程根据test3.so文件
module UTF-8// ABCD// test3 1

将main_g_printerr.c编译为main_g_printerr,test3.c编译为test3.so,gconv-modules为配置文件,内容如上。

image.png 

 

设置CHARSET为ABCD,因为配置文件写的是从ABCD转化为UTF-8,然后将环境变量GCONV_PATH设置为当前目录。执行main_g_printerr发现输出的是.so文件中的Hello test3

image.png 

 

pkexec是具有suid特殊权限的文件,因此执行pkexec文件时是具有root权限的。

image.png 

 

linux的动态链接器会在特权程序执行的时候清楚危险的环境变量,因此使用execve启动pkexec时,即使设置了GCONV_PATH也会被连接器清除。如下图所示test4具有suid权限,在test5中使用execve启动test4,并且设置了GCONV_PATH环境变量,但是可以看到test4的环境变量中并没有GCONV_PATH

image.png 

 

因此需要使用pkexec中存在的数组越界漏洞,将GCONV_PATH写入

变量argv与变量envp在内存中是连续的,如下图所示,图片来自于PwnKit: Local Privilege Escalation Vulnerability Discovered in polkit’s pkexec (CVE-2021-4034)

image.png 

 

当我们使用execve启动pkexec时,argv是NULL,因此argc的值为0,但是pkexec会默认将argc的值赋值为1,因此argv[argc] = argv[1] = envp[0],因此envp中的值会被越界读取。

利用数组越界写入GCONV_PATH=.

使用execve启动pkexec时envp[0]的值为FileName:.,并在当前目录下新建名为GCONV_PATH=.的文件夹,该文件夹下新建名为FileName:.的文件。

首先pkexec会取出argv[1](即envp[0])的值,接着通过g_find_program_in_path函数获取文件路径从而构造出GCONV_PATH=.FileName:.,接着该值会重新覆盖envp[0],至此GCONV_PATH被成功写入。

image.png 

 

GDB调试

数组越界读

image.png 

 

环境变量构造

image.png 

 

成功引入环境变量 

 image.png

最后就是使得pkexec使用g_printerr打印错误信息即可。

参考文章

https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034

https://bbs.pediy.com/thread-271423.htm

https://github.com/berdav/CVE-2021-4034

https://man7.org/linux/man-pages/man1/iconv.1.html

https://xz.aliyun.com/t/10870#toc-0