Detecting and mitigating CVE-2021-4034: “Pwnkit” local privilege escalation

By Jason Avery - JANUARY 28, 2022


CVE-2021-4034 privilege escalation

A new advisory from Qualys discloses a local privilege escalation bug in SUID-set program ‘pkexec’. The flaw has been designated the CVE ID of CVE-2021-4034 and nicknamed “pwnkit” by the vulnerability finders. The CVSSv3 base score is calculated to be a high 7.8 out of 10.0.

The vulnerable program is a part of Polkit, which manages process privileges. Polkit’s pkexec allows for non-privileged processes to communicate with privileged ones, as well as instrumenting legitimate and authorized uses of privilege escalation similar to ‘sudo’.

A memory corruption flaw exists when no argument is passed to the function. By manipulating environment variables, an attacker can trick ‘pkexec’ to load and execute arbitrary code with superuser privileges.

A blogger from 2013 stumbled upon the bug but did not report it as a vulnerability to be patched. So close!

The flaw was introduced nearly 13 years ago with the program’s initial release in 2009. This means that just about every Linux and Unix-like install in the world today may be affected!

Memory Exploitation without Controlling EIP

The vulnerability is interesting, as an attacker can take advantage of memory corruption without needing to control the instruction pointer (EIP in x86 assembly). In a typical memory-based attack like a heap overflow, the instruction pointer is overwritten to direct an exploited process to run shellcode located elsewhere in memory. However, here we can simply tell ‘pkexec’ where the program to run is in our environment variables.

Normally, programs read both explicit arguments and implicit environment variables to know what task they need to perform. In the case of ‘pkexec,’ the expected argument is the program that it should run. If it determines the requesting user is authorized to perform such an action, it will run that program as root. If no argument is given, it simply prints the usage help message and exits. However, due to how ‘pkexec’ processed arguments, it would also silently put environment variables in the argument memory space with an out-of-bounds write. ‘pkexec’ would look for a program to run in that space, but not find any and exit as expected.

So, in order for an attacker to exploit this, they need to put the location of their shellcode in an environment variable for ‘pkexec’ to unexpectedly pass on to root land.

The trick to passing on “unsecured” environment variables lies in how ‘pkexec’ prints error messages. ‘pkexec’ relies on the GNOME GLib library for correctly formatting messages in various charsets (language formats).

You are likely reading this in ASCII or UTF-8 formatting. If you’ve seen a document that contained characters from languages like Arabic or Hebrew, the document was likely in a UTF-16 or UTF-32 format. Wikipedia explains this more in detail if you feel like nerding out more.

By telling Glib to use a charset it doesn’t know about, Glib will attempt to find a library to do the formatting by looking in the GCONV_PATH environment variable. Normally, this is not a variable that is passed on to root, but due to how this vulnerability works, the GCONV_PATH variable can be smuggled in another variable like PATH and gets added back into the superuser’s environment. So, the attacker needs a user-level shell to run ‘pkexec,’ cause an error, use an invalid charset, and set GCONV_PATH to the location of their library-shaped shellcode.

Here’s what we essentially need, as borrowed from a publicly available exploit:

Exploit cve-2021-4034 pwkit

When compiled and run, we get a /bin/sh shell executed via ‘’. We can verify we have root privileges with the ‘whoami’ command.

CVE-2021-4034 exploit works

The impact of CVE-2021-4034

At the time of this writing, both Mitre and NVD have not yet officially published the CVSSv3 score in their advisories. By using the CVSSv3.1 calculator, I estimate the official base score to be a high severity of 7.8. The estimated base vector code is “CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H”.

Essentially, the vulnerability is not remotely exploitable, but any attacker that gets a user shell on a system through other means can use this to easily and reliably gain root privileges.

To learn more about how a vulnerability score is calculated, Are Vulnerability Scores Tricking You? Understanding the severity of CVSS and using them effectively

Since the vulnerability has existed for so long, basically every Unix-like system that uses Polkit is vulnerable. OpenBSD is the exception and already has mitigation in place to not allow the execve system call if argc is empty, thus dashing any hopes to exploit this on the security-focused OS.

Mitigating CVE-2021-4034

Temporary mitigation exists at the expense of pkexec’s capabilities. By removing SUID permissions, the program cannot run processes as root. However, any processes that rely on it for normal operation will be affected. SUID permission can be removed with chmod, as follows:

chmod 0755 /usr/bin/pkexec

Users are advised to apply the latest security patches from their Linux distributions to correct the issue.

Falco, a CNCF incubating project, can help detect anomalous activities in cloud-native environments. The following Falco rule can help you detect if you are impacted by CVE-2021-4034. The rule will also be released in the official Falco rules YAML.

- rule: Polkit Local Privilege Escalation Vulnerability (CVE-2021-4034)
desc: "This rule detects an attempt to exploit a privilege escalation vulnerability in Polkit's pkexec. By running specially crafted code, a local user can leverage this flaw to gain root privileges on a compromised system"
spawned_process and user.uid != 0 and and proc.args = '' and proc.env icontains 'GCONV_PATH='
"Detect Polkit pkexec Local Privilege Escalation Exploit (CVE-2021-4034) (user=%user.loginname uid=%user.loginuid command=%proc.cmdline args=%evt.args)"
priority: CRITICAL
tags: [process, mitre_privilege_escalation]

This rule will detect attempts to exercise the exploit. Specifically, it will look for conditions where no arguments are passed to ‘pkexec’ as a spawned user process and the GCONV_PATH is in the environment variables. This is uncommon and should not detect simply using ‘pkexec’ with no arguments in a non-malicious way. Here is what the detected system call looks like as viewed with the Sysdig tool.

sysdig -r cve-2021-4034.scap -X and evt.type=execve and proc.env contains 'PATH=GCONV_PATH='

Sysdig Falco runtime detection


This CVE just goes to show that even older, trusted software is not always free of vulnerabilities. Undiscovered vulnerabilities can still lurk in the shadows, waiting to be found. And sometimes even small bloggers find good nuggets of information that can be missed.

If you would like to find out more about Falco:

Subscribe and get the latest updates