欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创] CVE-2017-7533 漏洞利用

[原创] 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.c

static 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


对于短文件名,如果两个线程同时跑,可能出现以下的执行顺序,在strcpy(event->name, file_name)会发生heap overflow。


由于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。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功