[原创] CVE-2017-7533 漏洞利用
一、测试环境:
Android模拟器Nexus_6P_API_25@kernel 3.18
二、漏洞介绍
int inotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
struct fsnotify_mark *inode_mark,
struct fsnotify_mark *vfsmount_mark,
u32 mask, void *data, int data_type,
const unsigned char *file_name, u32 cookie)
{
struct inotify_inode_mark *i_mark;
struct inotify_event_info *event;
struct fsnotify_event *fsn_event;
int ret;
int len = 0;
int alloc_len = sizeof(struct inotify_event_info);
BUG_ON(vfsmount_mark);
if ((inode_mark->mask & FS_EXCL_UNLINK) &&
(data_type == FSNOTIFY_EVENT_PATH)) {
struct path *path = data;
if (d_unlinked(path->dentry))
return 0;
}
if (file_name) {
len = strlen(file_name);
alloc_len += len + 1;
}
pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode,
mask);
i_mark = container_of(inode_mark, struct inotify_inode_mark,
fsn_mark);
event = kmalloc(alloc_len, GFP_KERNEL); //[1]分配sizeof(struct inotify_event_info)+strlen(file_name)+1内存
if (unlikely(!event))
return -ENOMEM;
fsn_event = &event->fse;
fsnotify_init_event(fsn_event, inode, mask);
event->wd = i_mark->wd;
event->sync_cookie = cookie;
event->name_len = len;
if (len)
strcpy(event->name, file_name);//[2]将file_name拷贝到刚刚分配的内存event->name里面
ret = fsnotify_add_event(group, fsn_event, inotify_merge);
if (ret) {
/* Our event wasn't used in the end. Free it. */
fsnotify_destroy_event(group, fsn_event);
}
if (inode_mark->mask & IN_ONESHOT)
fsnotify_destroy_mark(inode_mark, group);
return 0;
}代码位于fs/notify/inotify/inotify_fsnotify.cstatic void copy_name(struct dentry *dentry, struct dentry *target)
{
struct external_name *old_name = NULL;
if (unlikely(dname_external(dentry)))
old_name = external_name(dentry);
if (unlikely(dname_external(target))) {
atomic_inc(&external_name(target)->u.count);//[3]长文件名走这里
dentry->d_name = target->d_name;
} else {
memcpy(dentry->d_iname, target->d_name.name, //[4]短文件名走这里
target->d_name.len + 1);
dentry->d_name.name = dentry->d_iname;
dentry->d_name.hash_len = target->d_name.hash_len;
}
if (old_name && likely(atomic_dec_and_test(&old_name->u.count)))
kfree_rcu(old_name, u.head);//[5]长文件名走这里,释放old_name
}代码位于fs/dcache.c

由于dentry->d_name.name和filename是同样的值,在注释[1]和注释[2]之间,如果执行了注释[4],在strcpy时会导致heap overflow。
具体poc可以参考心许雪的这篇文章《[原创](Android Root)CVE-2017-7533 漏洞分析和复现》。
对于长文件名,两个线程同时跑,由于竞争,在strcpy(event->name, file_name)也会发生heap overflow。

在实际的调试中,file_name的值大于old_name的值0x10字节,两者地址有重叠。如果old_name被堆喷射重新占位后的大小过大,在strcpy时会发生heap overflow。
三、如何利用
由于android 8.0已经引入了PAN机制,内核态不能访问用户态的数据,所以传统的通过覆盖ptmx结构体指针,从而实现任意地址写,变的不可能。或者通过挟持指针指向kernel_setsockopt,修改addr_limit,从而通过管道实现任意地址写,也变的不可能。因为他们都需要从用户态读取数据。
参考ThomasKing在BlackHat的文章asia-18-WANG-KSMA-Breaking-Android-kernel-isolation-and-Rooting-with-ARM-MMU-features。使用内核镜像攻击,可以绕过PAN机制。原理可以参考Geneblue《KSMA -- Android 通用 Root 技术》,简单的说需要利用地址写漏洞,向内核的L1页表写入一个block,这个block可以映射内核物理地址开始到内核地址+1G的地址范围。
对于我的操作系统,内存布局如下:
Virtual kernel memory layout: vmalloc : 0xffffff8000000000 - 0xffffffbdffff0000 ( 247 GB) vmemmap : 0xffffffbe00000000 - 0xffffffbfc0000000 ( 7 GB maximum) 0xffffffbe00000000 - 0xffffffbe01500000 ( 21 MB actual) PCI I/O : 0xffffffbffa000000 - 0xffffffbffb000000 ( 16 MB) fixed : 0xffffffbffbdfb000 - 0xffffffbffbdff000 ( 16 KB) modules : 0xffffffbffc000000 - 0xffffffc000000000 ( 64 MB) memory : 0xffffffc000000000 - 0xffffffc060000000 ( 1536 MB) .init : 0xffffffc00074d000 - 0xffffffc000789000 ( 240 KB) .text : 0xffffffc000080000 - 0xffffffc0005dfcb0 ( 5504 KB) .data : 0xffffffc00079a000 - 0xffffffc0007eca00 ( 331 KB)
注意:上传附件及图片大小不得大于30M。
⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。
