Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
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.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
WHS4_CVE-2021-4034 — Educational PoC and analysis of CVE-2021-4034 (PwnKit) local privilege escalation vulnerability in polkit's pkexec, with a Docker-based lab for hands-on exploitation and defense practice. | Kitploit
Tools/GitHubGitHub/krleejihyeong/whs4_cve-2021-4034
Privilege EscalationVulnerability AnalysisExploitationCTFPenetration TestingLearning & EducationBinary ExploitationLabs & Practice

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
GitHub
krleejihyeong/whs4_cve-2021-4034

WHS4_CVE-2021-4034

Educational PoC and analysis of CVE-2021-4034 (PwnKit) local privilege escalation vulnerability in polkit's pkexec, with a Docker-based lab for hands-on exploitation and defense practice.

View Repository
142 months agoNot yet reviewed

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.

# 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.

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.

// 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

    CHARSET=PWNKIT
    
  2. Search for converter definition in gconv-modules file

    module UTF-8// PWNKIT// pwnkit 1
    
  3. Load .so file from GCONV_PATH

    GCONV_PATH=. → search for pwnkit.so in current directory
    
  4. Initialization function in .so runs automatically

    // 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

┌─────────────────────────────────────┐
│ 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

# 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
Download Tool