The exploit is then as simple as gcc $FILE -o exploit; ./exploit
.
This version of the exploit will leave traces in the target’s logs!
Here’s what happens on execution:
-
The exploit creates the path
GCONV_PATH=.
in the current directory and adds an invalid executable file to it. -
The exploit creates a second directory called pwnkit and sets up a malicious shared library designed to be loaded by GLib to translate system messages to the made-up character set “PWNKIT”.
-
The exploit calls
pkexec
with a NULL argument list (this bit is important, since we need the length of the argument list to be 0 — so, not even to contain the name ofpkexec
itself) but with a correctly set up (albeit malicious) environment viaexecve()
. Importantly, the first “variable” in the environment is actually the name of the (invalid) executable in theGCONV_PATH=.
directory. -
Polkit just falls through the loop that it would normally use to walk through the passed-in arguments. This causes what would be pointing to an executable name to instead point to the first environment variable that’s passed into
execve()
, which happens to be string pwnkit. -
Polkit looks up the malicious executable, finds it in
GCONV_PATH=./pwnkit
(because we set the PATH to that directory), and then tries to replace the executable name with this full path. Except that it’s still writing to the first element of the environment, which causes pwnkit to be replaced byGCONV_PATH=./pwnkit
. -
Polkit the proceeds to sanitize its environment. When it comes to the invalid
SHELL
variable this sanitization fails and Polkit throws an error and dies. But! Before dying, Polkit tries to print the error using a GLib function that dutifully attempts to translate the message into the “PWNKIT” character set. To figure out how to do this, modules are loaded fromGCONV_PATH
… And we’ve defined a malicious module to do this that cleans up the exploit files and spawns a root shell (sincepkexec
is SUID root).