Associated Vulnerability
Title:polkit 缓冲区错误漏洞 (CVE-2021-4034)Description:A local privilege escalation vulnerability was found on polkit's pkexec utility. The pkexec application is a setuid tool designed to allow unprivileged users to run commands as privileged users according predefined policies. The current version of pkexec doesn't handle the calling parameters count correctly and ends trying to execute environment variables as commands. An attacker can leverage this by crafting environment variables in such a way it'll induce pkexec to execute arbitrary code. When successfully executed the attack can cause a local privilege escalation given unprivileged users administrative rights on the target machine.
Description
Exploit for the PwnKit Vulnerability
Readme
# CVE-2021-4034 Exploit
Root exploit for the PwnKit vulnerability. Check out the original report
[here](https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt).
*Use this exploit with an express permission of the target system's owners.*
## Building
No dependencies needed besides libc. Just run `make`.
## Running
Running without options will execute the exploit:
```
[linux@linux ~]$ ./exploit
-----------------------------------------------------------------------------
__\ / __ __ _ __ _ __ | \ / _ ___
/ V |_ --- _)/ \ _)/| ---|_|/ \__)|_| | V |_) _/|_|
\__ |__ /__\_//__ | |\_/__) | | | \/__| |
-----------------------------------------------------------------------------
sh-5.1# whoami
root
sh-5.1#
```
You can customize the path to `pkexec` as well as the "from" charset:
```
[linux@linux ~]$ ./exploit -h
...
./exploit [-c] [-h] [-f from_charset] [-p /path/to/pkexec]
-----------------------------------------------------------------------------
-c Just teardown - don't exploit
-p <path> Path to pkexec (default: "/usr/bin/pkexec")
-f <from_charset> Custom "from" charset (default: "UTF-8")
-h Display this message
```
## What's the deal with `GIO_USE_VFS`?!
I saw a few people on social media ask why do some exploits fail if the
`GIO_USE_VFS=` is not defined? Why do they work with the older versions?
### The culprit
Commt `daf3d5c2d15466a267221fcb099c59c870098e03` in polkit is the culprit.
Here's the relevant part of the diff:
```
--- a/src/programs/pkexec.c
+++ b/src/programs/pkexec.c
@@ -503,6 +503,9 @@ main (int argc, char *argv[])
opt_user = NULL;
local_agent_handle = NULL;
+ /* Disable remote file access from GIO. */
+ setenv ("GIO_USE_VFS", "local", 1);
+
/* check for correct invocation */
if (geteuid () != 0)
{
```
Versions prior to this commit are exploitable without the need to define the
`GIO_USE_VFS` variable. The versions after - are not exploitable unless this variable is defined.
The purpose of the commit is actually a red herring. It's not what the variable
means, it's *how its presence affects the program environment*. For the truth we must look to libc.
### Looking in libc
The environment of a process in libc is represented by an array of `char *`s,
pointed to by this global variable:
```c
char **environ;
```
`environ` lives on the heap and is occasionally relocated. You might
already know where this is going. Check out this code snippet from
[setenv.c](https://code.woboq.org/userspace/glibc/stdlib/setenv.c.html):
```c
#if !_LIBC
# define __environ environ
# ifndef HAVE_ENVIRON_DECL
extern char **environ;
# endif
#endif
int
__add_to_environ (const char *name, const char *value, const char *combined,
int replace)
{
char **ep;
// ... skipping
ep = __environ;
size = 0;
if (ep != NULL)
{
for (; *ep != NULL; ++ep)
if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
break;
else
++size;
}
if (ep == NULL || __builtin_expect (*ep == NULL, 1))
{
char **new_environ;
/* We allocated this space; we can extend it. */
new_environ = (char **) realloc (last_environ,
(size + 2) * sizeof (char *));
// ... skipping
last_environ = __environ = new_environ;
}
```
`__add_to_environ()` is called by both `setenv(3)` and `putenv(3)` to accomplish
what they promise - set an environment variable. If the environment variable in
question is **not** defined, `environ` has to be reallocated to accomodate
a new entry (a pointer to the new environment `key=value` pair). If it is
defined, the size of the `environ` array has not changed and thus there is no
reason for reallocation. For brevity I've omitted that part of the code - I
encourage you to check it out.
### Tying it all together
Now let's come back to the exploit. If you've gotten this far, you probably
already know the methodology behind this exploit (if not please check out the
[original report](https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt)).
We are trying to sneak in an environment variable by passing an empty program
arguments (`argv`) to `pkexec`. When `argc` is truly empty (not even a program
name), the environment variables, which are adjacent, clash with the arguments.
We abuse this behavior to force `pkexec` to write a canonical path of a
target executable into the environment. However, before we get to this part of the
code this happens:
```c
setenv ("GIO_USE_VFS", "local", 1);
```
If this variable is not present in the environment `environ` will be
reallocated, thus never clashing with `argv`. As a result, the out-of-bounds
write will not be affecting the program's environment, causing the
exploit to fail.
File Snapshot
[4.0K] /data/pocs/5d31ae38d897532776dcc9768a6bce724f71d869
├── [4.0K] exploit.c
├── [ 211] lib.c
├── [ 304] Makefile
└── [4.8K] README.md
0 directories, 4 files
Remarks
1. It is advised to access via the original source first.
2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →