再探CVE-2016-0728

本文深入探讨了CVE-2016-0728,一个Linux平台上的UAF漏洞,由keyrings组件的引用计数问题引起。当引用计数溢出时,会导致对象误释放,进而可能实现内核代码执行。利用过程包括使引用计数溢出、释放keyring对象、覆盖已释放对象以及通过keyctl API控制执行代码。然而,由于Android 5.0设备运行在不受影响的内核版本及SELinux策略限制,此漏洞在这些设备上无法利用。

 

0x0  漏洞信息

 

https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0728

 

0x1  漏洞描述

cve-2016-0728是一个linux平台上的UAF漏洞.漏洞主要的原因是由于keyrings组件当中的引用计数问题导致的.它使用一个32位的无符号整数做引用计数,但是在计数器出现溢出的时候没有进行合理的处理.当对象的引用计数达到最大时会变成0,因此释放对象的内存空间.而此时程序还保留对引用对象的引用,所以形成了UAF漏洞.

 

0x2  代码分析

long join_session_keyring(const char *name)
{
	const struct cred *old;
	struct cred *new;
	struct key *keyring;
	long ret, serial;

	new = prepare_creds();
	if (!new)
		return -ENOMEM;
	old = current_cred();

	/* if no name is provided, install an anonymous keyring */
	if (!name) {
		ret = install_session_keyring_to_cred(new, NULL);
		if (ret < 0)
			goto error;

		serial = new->session_keyring->serial;
		ret = commit_creds(new);
		if (ret == 0)
			ret = serial;
		goto okay;
	}

	/* allow the user to join or create a named keyring */
	mutex_lock(&key_session_mutex);

	/* look for an existing keyring of this name */
	keyring = find_keyring_by_name(name, false);  //key->usage + 1
	if (PTR_ERR(keyring) == -ENOKEY) {
		/* not found - try and create a new one */
		keyring = keyring_alloc(name, old->uid, old->gid, old,
			KEY_ALLOC_IN_QUOTA, NULL);
		if (IS_ERR(keyring)) {
			ret = PTR_ERR(keyring);
			goto error2;
		}
	} else if (IS_ERR(keyring)) {
		ret = PTR_ERR(keyring);
		goto error2;
	} else if (keyring == new->session_keyring) {  //keyname == 当前cred中的key name
		ret = 0;                //直接返回,绕过key_put
		goto error2;
	}

	/* we've got a keyring - now to install it */
	ret = install_session_keyring_to_cred(new, keyring);
	if (ret < 0)
		goto error2;

	commit_creds(new);
	mutex_unlock(&key_session_mutex);

	ret = keyring->serial;
	key_put(keyring);
okay:
	return ret;

error2:
	mutex_unlock(&key_session_mutex);
error:
	abort_creds(new);
	return ret;
}

通过以上代码可以得出只要进程使用当前正在使用的keyring名,程序就会跳过kref_put(keyring),造成引用计数的只增不减.由于引用计数是无符号整形,可以通过循环调用,整数溢出的方法来将它置0.

 

0x3 如何利用

1. 造成引用计数溢出,int所能保存最大值为232 - 1.所以只要进行4294967295次循环,就可以将它清零.

2. 释放引用计数为0的keyring对象

3. 使用slab机制分配内核对象来覆盖之前已经释放的keyring对象

4. 获得内核代码的执行权限.使用keyctl(KEY_REVOKE,key_name)这个API来调用revoke().当我们覆盖keyring对象的时候,我们可以控制指向revoke()的函数指针,让其指向我们准备好的提权代码.

 

0x4  POC问题主要分析

1.   漏洞代码所在的内核版本应该是3.8以后.但是大部分安卓5.0的设备都运行在3.4的内核版本之中.

2.   网上的poc没有成功的原因是因为没有获得commit_creds和prepare_kernel_cred的地址,攻击者需要得到root权限,将kptrstrict标志置为0后,才能通过/proc/kallsyms来查看符号的地址.

3.   在用户空间的提权代码则需要commit_creds(prepare_kernel_cred(0));函数将cred结构置为0,从而获取root权限.

4.   使用ret2usr来在用户态调用提权代码需要考虑pxn的问题.在没有pxn防护的手机中可以在内核空间执行用户态代码.然而在有pxn防护的情况下,则需要考虑使用ROP来寻找内核gadget来绕过保护.

5.   需要调用4294967295次,时间过长有可能导致shell反弹超时.所以很难利用.

6.   出现漏洞的代码存在于linux内核中的keyring服务.但是该服务必须在内核编译时启用CONFIG_KEYS 选项.在AOSP内核的config文件中,并没有发现CONFIG_KEYS被启用.所以说android设备并没有包含漏洞相关的代码,因此就是说android设备没有被该漏洞所影响.

7.   在安卓4.4版本以后,强制开启了SELinux的策略.SELinux减少了Linux内核的攻击面.SELinux的策略也限制了ASOP在设备上通过非授信app调用exp的权限.比如当一个app尝试去执行keyctl系统调用去创建一个keyring的对象时,系统调用会被拒绝. 开源的poc代码使用了SysV IPC (msgget) 来分配内存传递漏洞利用代码.安卓5.0的SELinux策略限制了SysV IPC因此阻止了包含利用的代码.

 

 

0x5  实测

 

1. 首先去掉get_symbol的函数,root掉手机以后,直接将kptrstrict置为0,读取kallsyms的全部信息.获取相关符号地址

 

2. arm版的通过使用syscall来取代keyctl调用.

3. 通过添加一个计算来显示循环的百分比

 

在我的设备上测试的时间为12:00 – 16:46 ,进度显示为0.1%.

 

所以大约要执行166天左右才能跑完…

 

 

参考文章:

http://bobao.360.cn/learning/detail/2576.html

https://www.mulliner.org/blog/blosxom.cgi/security/CVE-2016-0728_vs_android.html

https://github.com/nardholio/cve-2016-0728

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值