CVE-2021-4034 pkexec本地提权漏洞复现与原理分析

这篇文章主要是看看雪上的一篇分析文章之后写的,部分内容从文章中拷贝, 文章链接在这里

漏洞原理

漏洞的原理很简单, 对于命令行程序,通常会有参数, 体现在代码中就是argc 和 argv, argc 代表参数个数,argv 是参数,默认情况下,argv[0]是当前程序的路径。当我们访问argv的偏移大于argc的时候,就会出现越界访问。所以,通常情况下都是先检查下argc个数,然后再去访问的,但是linux中挺多程序写法上有一定问题。这个问题很早就被人发现了,但是由于并不知道怎么利用,也就没有下文了。

问题1 argc 何时为0?

在特殊情况下,如使用execve来调用程序,并给argv传值 NULL,则argc为0。

问题2 越界访问后,访问的是什么内容

argv 后面跟的是env的数据

到这里,你就会发现,不知道后面利用了,毕竟对linux 了解的少,很难会想到别的利用的方法。

漏洞利用

看雪的文章中,已经把整个利用流程都说清楚了, 首先用越界读,获取到pwnkit.so, 然后构造路径让GCONV_PATH=xxx存入到s变量中,然后就构造了一个越界写,
把GCONV_PATH写入到环境变量中, 而这个变量会让g_printerr主动去加载这个so, 后面就是构造一个报错,让g_printerr触发了。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
Breakpoint 1, main (argc=0, argv=0x7ffe2d2d4e58) at pkexec.c:406
406 const gchar *environment_variables_to_save[] = {
Breakpoint 2 at 0x55d64339bed6: file pkexec.c, line 900.
Stepping until end of main @ pkexec.c:386
442 ret = 127;
443 authority = NULL;
444 subject = NULL;
445 details = NULL;
446 result = NULL;
447 action_id = NULL;
448 saved_env = NULL;
449 path = NULL;
450 command_line = NULL;
451 opt_user = NULL;
452 local_agent_handle = NULL;
455 if (geteuid () != 0)
461 original_user_name = g_strdup (g_get_user_name ());
462 if (original_user_name == NULL)
468 if (getcwd (original_cwd, sizeof (original_cwd)) == NULL)
478 opt_show_help = FALSE;
479 opt_show_version = FALSE;
480 opt_disable_internal_agent = FALSE;
481 for (n = 1; n < (guint) argc; n++) //n被赋值为1
512 if (opt_show_help)
518 else if (opt_show_version)
525 if (opt_user == NULL)
--Type <RET> for more, q to quit, c to continue without paging--
526 opt_user = g_strdup ("root");
536 g_assert (argv[argc] == NULL);
537 path = g_strdup (argv[n]); //越界读,path被赋值为 argv[1],即 environ[0],"pwnkit.so:."
538 if (path == NULL)
543 if (path[0] != '/')
546 s = g_find_program_in_path (path); //在环境变量PATH中寻找"pwnkit.so:.",并把路径返回给 s。利用脚本中把PATH设置为"GCONV_PATH=.",且在磁盘上提前生成了名为"GCONV_PATH=."的文件夹,并放置了名为"pwnkit.so:."的程序,因此,s被赋值 "GCONV_PATH=./pwnkit.so:."
547 if (s == NULL)
552 g_free (path);
553 argv[n] = path = s; //越界写,argv[1]被设置为"GCONV_PATH=./pwnkit.so:.",即environ[0] 被修改,重新引入了不安全的环境变量GCONV_PATH,至此完成了至关重要的一步。接下来只要随便构造个错误,使其报错时调用到 g_printerr 即可。
555 if (access (path, F_OK) != 0)
560 command_line = g_strjoinv (" ", argv + n);
561 exec_argv = argv + n;
566 rc = getpwnam_r (opt_user, &pwstruct, pwbuf, sizeof pwbuf, &pw);
567 if (rc == 0 && pw == NULL)
572 else if (pw == NULL)
579 saved_env = g_ptr_array_new ();
580 for (n = 0; environment_variables_to_save[n] != NULL; n++)
582 const gchar *key = environment_variables_to_save[n];
585 value = g_getenv (key);
586 if (value == NULL)
593 if (!validate_environment_variable (key, value)) //key="SHELL", value="/lol/i/do/not/exists",在校验环境变量时报错"The value for the SHELL variable was not found the /etc/shells file",进而调用了 g_printerr ,触发漏洞利用,最终执行pwnkit.so里的execve("/bin/sh", args, environ)得到shell。
process 11852 is executing new program: /usr/bin/dash
Error in re-setting breakpoint 1: Function "main" not defined.
Error in re-setting breakpoint 2: No source file named /home/kali/software/release/polkit-0.105/src/programs/pkexec.c.