再探CVE-2016-0728

//weibo: @少仲


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. 釋放引用計數爲0keyring對象

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

 




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章