0
本文作者: 又田 | 2017-08-18 10:35 | 專題:CSS 中國(guó)互聯(lián)網(wǎng)安全領(lǐng)袖峰會(huì) |
雷鋒網(wǎng)編者按:8月16日,第三屆中國(guó)互聯(lián)網(wǎng)安全領(lǐng)袖峰會(huì)(CSS 2017)在北京國(guó)家會(huì)議中心召開(kāi)。作為九大分會(huì)場(chǎng)之一的騰訊安全探索論壇(TSec)以“安全新探索”為主題,云集了國(guó)際知名廠商及頂尖高校的資深安全專家,探討全球信息安全領(lǐng)域前沿技術(shù)、研究成果及未來(lái)趨勢(shì)。來(lái)自騰訊安全科恩實(shí)驗(yàn)室的方家弘、申迪分享了面對(duì)安卓?jī)?nèi)核中存在的UAF漏洞數(shù)量不斷變小、利用難度逐漸變大的現(xiàn)狀,將如何穩(wěn)定高效地利用這類漏洞來(lái)完成操作系統(tǒng)提權(quán)。
方家弘:大家下午好!我是來(lái)自騰訊安全科恩實(shí)驗(yàn)室的方家弘,我和我的同事申迪給大家?guī)?lái)關(guān)于“安卓?jī)?nèi)核 UAF 漏洞利用探秘”的分享。
今天的分享主要分成四個(gè)部分。首先由我介紹在之前的一段時(shí)間出現(xiàn)的比較有代表性的安卓?jī)?nèi)核漏洞。其次要分析像 UAF 這樣一個(gè)特定類型的漏洞在利用方面有什么樣的特點(diǎn),它涉及到系統(tǒng)組件的基礎(chǔ)知識(shí),方便大家理解后續(xù)的內(nèi)容。之后的時(shí)間交給申迪,他會(huì)具體分析在 perf 子系統(tǒng)中發(fā)現(xiàn)的 UAF 漏洞的具體利用過(guò)程。最后,分享一點(diǎn)我們的總結(jié)和思考。
首先介紹安卓?jī)?nèi)核漏洞的簡(jiǎn)要?dú)v史。
為什么要對(duì)安卓?jī)?nèi)核進(jìn)行攻擊呢?
將安卓系統(tǒng)看做整體,最早系統(tǒng)中以不同的應(yīng)用,使用不同的 UID ,對(duì)應(yīng)用之間進(jìn)行隔離。后來(lái)發(fā)現(xiàn)這并不能阻擋攻擊,又產(chǎn)生了加密文件系統(tǒng)等等安全措施。
而絕大多數(shù)措施的實(shí)現(xiàn)都依賴于安卓的內(nèi)核。攻擊內(nèi)核本身是非常有助益的工作。在 Windows 系統(tǒng)當(dāng)中,攻擊內(nèi)核可以直截了當(dāng)?shù)倪_(dá)到提權(quán)的目的。在安卓系統(tǒng)中,也可以繞過(guò)系統(tǒng)中設(shè)置的種種限制。對(duì)于壞人可以實(shí)現(xiàn)他不可告人的目的。
按照時(shí)間軸,根據(jù)我個(gè)人的理解列出了比較有代表性的內(nèi)核漏洞。當(dāng)然,不包括我們今天要講的漏洞。
2013 年 11 月份,CVE-2013-6282 是我們的友商寫(xiě)出的最早的漏洞,它是邏輯問(wèn)題。在我們?nèi)ツ旯籼厮估臅r(shí)候,驚訝地發(fā)現(xiàn)這個(gè)漏洞居然還存在。當(dāng)時(shí)特斯拉比較自信的認(rèn)為車輛不可能被攻破,所以他們?cè)趦?nèi)核方面沒(méi)有做更多的防御。在漏洞被報(bào)告,修復(fù)之后,特斯拉在內(nèi)核方面做了非常激進(jìn)的更新和修復(fù)。加粗的漏洞都屬于通用的安卓 root 漏洞。
2014 年,美國(guó)的兩個(gè)天才少年發(fā)現(xiàn)了 towelroot,這是引起比較大反響的通用 root。這個(gè)漏洞類型在當(dāng)時(shí)較新穎,這個(gè)漏洞的利用方式也非常創(chuàng)新,引起了不小的波瀾。
2015 年 5 月份,我們發(fā)現(xiàn)了 CVE-2015-3636。
2016年8月份,我們利用了 CVE-2015-1805。
之后就是非常出名的 Dirty COW,它也是屬于邏輯的問(wèn)題。
2017年4月份,由 Google 研究員發(fā)現(xiàn)的 wifi Firmware 的漏洞,這是非常有創(chuàng)新特色的漏洞利用。
之前提到的是幾個(gè)有代表性的漏洞,6282 屬于業(yè)務(wù)邏輯的問(wèn)題;1805 是屬于越界訪問(wèn);3153、3636 都屬于 UAF 漏洞。今天我們主要針對(duì) UAF 漏洞進(jìn)行研究和探討。
顧名思義,UAF 漏洞先要 after Free,而要成功利用這個(gè)漏洞,有幾個(gè)步驟是不能少掉的。一是如何按照順序去觸發(fā) free 和 use。這看似比較簡(jiǎn)單,以 2015-3636 為例,use 的時(shí)機(jī)和 free 的時(shí)機(jī)非常可控,我們可以做任意想做的事情,比較好控制被 free 掉的空間。
3636 這個(gè)漏洞的品相非常好,但在很多情況下的控制環(huán)節(jié)上,可能會(huì)面臨競(jìng)態(tài)的問(wèn)題。在這種情況下,如何穩(wěn)定的控制好 free 和 use 的順序,并且穩(wěn)定的觸發(fā) use,就是比較困難的問(wèn)題。對(duì)于不同的漏洞來(lái)講,需要不同的技巧去處理。我們已經(jīng)能夠把時(shí)序上的問(wèn)題清楚,之后就是如何改變內(nèi)核的控制力,如何控制代碼執(zhí)行??刂瞥鰜?lái)被 free 的空間方法,不同的漏洞,有不同的方法。
這里牽涉到最重要的就是 Linux 內(nèi)核的內(nèi)存管理。我們以圖示的形式展示了 UAF 漏洞利用的方式。堆上有 ABCD 四個(gè)對(duì)象。這是一個(gè)比較簡(jiǎn)單的呈現(xiàn)方式,要做到能夠穩(wěn)定有效的 free 掉目標(biāo)對(duì)象,并且把我們想要的東西填進(jìn)去,需要對(duì)內(nèi)核存儲(chǔ)管理有比較深入的了解,也就牽涉到 Linux 內(nèi)核的堆管理器。
在安卓系統(tǒng)當(dāng)中,我們對(duì)常見(jiàn)的堆管理器叫 SLUB,它替換了之前常見(jiàn)的 SLAB。從管理結(jié)構(gòu)上來(lái)講,SLUB 是簡(jiǎn)化版的,U 就是 unqueued 的意思。queued 是隊(duì)列,做堆管理器,總要有隊(duì)列,申請(qǐng)的時(shí)候從這里拿。它取消了單獨(dú)存在隊(duì)列的結(jié)構(gòu),這樣就使得完全空閑的 SLUB 被完全釋放掉。它存在 per cpu 的 slab,它的釋放和申請(qǐng)的過(guò)程會(huì)非常快。為了實(shí)現(xiàn)兼容性,Linux 的管理是抽象到 kmem 層。之前的內(nèi)核代碼、內(nèi)核驅(qū)動(dòng),不需要做任何的修改。如果大家自己編譯過(guò) Linux 內(nèi)核,只需要選擇什么樣的堆管理器就可以直接使用,其它代碼都不用更改。
首先看一下 SLUB 的堆塊長(zhǎng)什么樣子。Linux 內(nèi)核當(dāng)中物理內(nèi)存的頁(yè)面管理是通過(guò) buddy 進(jìn)行的。要符合物理頁(yè)面管理的原則。在 SLUB 當(dāng)中,都是二的 N 次方組成頁(yè)組成的。它實(shí)際上是巧妙的利用了物理頁(yè)面描述服的聯(lián)合,實(shí)現(xiàn)了管理。我分配好給這個(gè)堆塊的這些頁(yè),第一個(gè)頁(yè)面的描述符上就會(huì)記錄堆塊中可使用的第一個(gè)對(duì)象。這就是空閑對(duì)象列表的頭部。在空閑對(duì)象的頭部,又會(huì)有一個(gè)指針,指到下一個(gè)空閑對(duì)象。
這個(gè)東西沒(méi)有額外的元數(shù)據(jù),所有的元數(shù)據(jù)只存在于原有的結(jié)構(gòu)體當(dāng)中,存在于你分配給這個(gè)堆塊的頁(yè)面當(dāng)中。這也帶來(lái)了一些特性,使得它可以幫助我們對(duì)特定的漏洞進(jìn)行利用。
前面提到了 cpu_slub 的概念,分配和釋放都是快速的過(guò)程。當(dāng)前分配的對(duì)象在于 cpu 綁定 slub 上面,就會(huì)進(jìn)入快速分配的流程。不管怎么樣,對(duì)于使用堆管理器的用戶來(lái)講,肯定會(huì)得到空閑的 slub 對(duì)象供使用。具體怎么操作,就由 slub 的堆管理器進(jìn)行。
為什么要設(shè)定 cpu_slub,大部分情況下在一個(gè)調(diào)度周期內(nèi)會(huì)有頻繁的對(duì)象分配操作。釋放也是這樣的情況,目前的對(duì)象就是隸屬于當(dāng)前的 cpu_slub,這就帶來(lái)了另外一個(gè)非常好的特性。當(dāng)前 cpu 上釋放的對(duì)象,我馬上要申請(qǐng)的,肯定申請(qǐng)到剛剛釋放的對(duì)象,這對(duì)于填充是非常好的特性。這個(gè)特性在其它的漏洞利用當(dāng)中也會(huì)使用到。同時(shí),釋放也存在 slow path,這是不可避免的情況。
這里對(duì) SLUB 的特性進(jìn)行了歸納。按照對(duì)象的大小會(huì)做一個(gè)合并,這會(huì)對(duì)漏洞利用帶來(lái)一些問(wèn)題,你可能不知道這樣的堆塊當(dāng)中放的還有其他什么對(duì)象。
接下來(lái)看兩個(gè)漏洞的案例,這兩個(gè)案例充分利用了 slub 堆管理器的特性。
首先是 CVE-2015-1805。
iovec 是數(shù)據(jù)內(nèi)核中傳遞數(shù)據(jù)的結(jié)構(gòu)。這個(gè)漏洞本身是 overrun,牽涉到我們?cè)趦?nèi)核當(dāng)中如何申請(qǐng)可控的overrun 數(shù)組。在安卓當(dāng)中,很多 API 是被禁用的。最終我們找到 sendmmsg 的調(diào)用,你可以得到內(nèi)容完全可控的數(shù)組。它的壞處是放完以后就被銷毀掉了。
這個(gè)對(duì)象本身的生命周期不夠長(zhǎng)。看似這不是很好的對(duì)象,實(shí)際上可以回想起之前的一點(diǎn),在 slub 中一個(gè)對(duì)象被釋放之后,僅僅是在對(duì)象的頭部寫(xiě)入了指針,這個(gè)指針指向下一個(gè)可以使用的對(duì)象。
2015-1805 的代碼路徑當(dāng)中,如果 iov 是 0,根本就不會(huì)被處理。如果說(shuō)噴射的夠快,漏洞利用過(guò)程夠快,被釋放掉的 iov 本身還是空的對(duì)象,或者又被另一個(gè) iov 填上,根本不會(huì)對(duì)漏洞造成任何影響。我們只需要控制填進(jìn)去的第一個(gè) iov 的長(zhǎng)度是 0,它就會(huì)被忽略掉。即便被釋放,這個(gè)結(jié)果還是有效的 iov。
第二個(gè)漏洞,應(yīng)該是 2016-6187。通過(guò)這個(gè)方法,把一個(gè)品相非常差的漏洞,越界寫(xiě)一個(gè)字符節(jié)的 0,變成可能導(dǎo)致代碼執(zhí)行的情況。最后也是通過(guò) freelist 指針的特性。
接下來(lái)的時(shí)間交給申迪,由他分析一下在 perf 子系統(tǒng)中發(fā)現(xiàn)的漏洞利用。
申迪:下面給大家介紹我在去年發(fā)現(xiàn)的兩個(gè) perf 系統(tǒng)的 UAF。在安卓手機(jī)上有很多用戶有一鍵 root 的需求。一些 UAF 漏洞的品相并不是特別好,但我至少要寫(xiě)到達(dá)到 90% 的成功率。我主要介紹兩個(gè)在去年年初發(fā)現(xiàn),年底報(bào)給 Google 的漏洞。
第一個(gè)是 CVE-2016-6787,它是通過(guò) Race觸發(fā)的漏洞,內(nèi)核立即崩潰。上周我在 BlackHat 講了繞過(guò)三星 KNOX 的防護(hù)機(jī)制。
第二個(gè)漏洞是 CVE-2017-0403,這是今年才修復(fù)的漏洞。這個(gè)漏洞有它自己的難點(diǎn)。
在講漏洞之前,先講一下 perf 是什么東西,它是系統(tǒng)調(diào)用,任何安卓都可以調(diào)這個(gè)系統(tǒng)調(diào)用,攻擊內(nèi)核。這個(gè)系統(tǒng)調(diào)用有很多問(wèn)題,我也發(fā)現(xiàn)不止兩個(gè)問(wèn)題,這邊兩個(gè)是比較好利用的,單獨(dú)拿出來(lái)講一下。這個(gè)系統(tǒng)調(diào)用的參數(shù)比較復(fù)雜,其中一個(gè)是你怎么定義你想生成的統(tǒng)計(jì)事件,你想定義代碼執(zhí)行的指定周期,或者是真正調(diào)用進(jìn)入內(nèi)核的時(shí)候又調(diào)用了哪些調(diào)用,它包含了千奇百怪的 perf 事件,可能會(huì)進(jìn)入到分支。
這個(gè)系統(tǒng)調(diào)用,不管中間發(fā)生了什么,最后總會(huì)生成 perf_event。每個(gè)都不是孤立的,有可能是一組形式出現(xiàn)的,每個(gè) groun 都有一個(gè) leader,每個(gè)進(jìn)程里有多個(gè)組,又有大的容器包括這個(gè)組。每個(gè)進(jìn)程里有兩個(gè) perf_event_context,把作為的 group leader 串聯(lián)起來(lái),這 list 把組內(nèi)的串聯(lián)起來(lái),這三個(gè)內(nèi)核對(duì)象有一個(gè)比較復(fù)雜的連接的關(guān)系,其實(shí)就是因?yàn)檫@些關(guān)系搞得很復(fù)雜,系統(tǒng)調(diào)用里出了一些 UAF 的問(wèn)題。
Googl 在 2016 年下半年覺(jué)得這個(gè)系統(tǒng)調(diào)用太危險(xiǎn),直接封掉了。去年年初,這個(gè)調(diào)用還可以被其它的調(diào)用調(diào)到,我利用這兩個(gè)漏洞完成了手機(jī)上的 root。
第一個(gè)漏洞的問(wèn)題主要是由于系統(tǒng)調(diào)用里的 move_group。
如果 group leader 是軟件的,你要插入一個(gè)硬件的,這個(gè)時(shí)候會(huì)挪動(dòng)大量列表相連的關(guān)系。首先會(huì)把 group leader 從指針上提取出來(lái),把每個(gè) event 粘出來(lái)。這兩個(gè)操作完成之后,又看到紅框里的代碼,減了 1,這個(gè)減 1 沒(méi)有考慮到并發(fā)操作,把一個(gè)組摘除了兩遍。正常情況下,只減 1,并發(fā)操作就要減 2,被多減了一個(gè),引用減到 0,導(dǎo)致觸發(fā)釋放。
主線程先創(chuàng)建一個(gè) group leader,線程 1 觸發(fā) move_group操作。右邊出現(xiàn)了崩潰,實(shí)際是因?yàn)?perf_event context 被提前釋放。你的進(jìn)程在第二次進(jìn)入調(diào)度的時(shí)候,進(jìn)入調(diào)度器會(huì)調(diào)入一行,周期執(zhí)行完了會(huì)換出,換出之后再被進(jìn)程調(diào)度器換入。
這個(gè)時(shí)候就會(huì)觸發(fā)操作。關(guān)鍵是線程調(diào)度是很頻繁的,基本不是肉眼可觀測(cè)的。漏洞一旦觸發(fā),內(nèi)核立即就崩潰了。實(shí)際上它不是立即崩潰,實(shí)際上有一個(gè)微笑的操作,線程已經(jīng)被換出,換入的時(shí)候又崩潰了。假如是一個(gè)立即的崩潰,對(duì)我來(lái)說(shuō)就是毫無(wú)意義。這個(gè)時(shí)候就要集中解決一個(gè)問(wèn)題,怎么讓內(nèi)核不立即崩潰,給我爭(zhēng)取到足夠的時(shí)間去噴堆。
在講這個(gè)問(wèn)題之前,要先講一下 Linux 內(nèi)核的調(diào)度知識(shí),包括很多線程,每個(gè)線程不可能同時(shí)執(zhí)行。真正執(zhí)行的是一個(gè),其它都是等待執(zhí)行。它執(zhí)行一段時(shí)間之后,會(huì)有幾種情況。有一種情況就是以后再也不需要執(zhí)行,它就會(huì)被殺掉。如果該想繼續(xù)執(zhí)行,配合的時(shí)間片已經(jīng)被用盡了,這個(gè)時(shí)候就被線程調(diào)度器切換出去。這種時(shí)候就會(huì)進(jìn)入到 interruptible 或者是 uninterruptible。
futex 這個(gè)調(diào)用可以幫助我們完成真正的睡眠。一旦線程進(jìn)入睡眠的狀態(tài),是不會(huì)被任何人主動(dòng)喚醒。這對(duì)我們來(lái)說(shuō)是最理想的狀態(tài)。
有了這個(gè)函數(shù)的幫助,整個(gè)調(diào)用邏輯就變成這樣的,從左到右,從觸發(fā)漏洞,到噴堆、代碼執(zhí)行控制完整的流程圖。還是主線程創(chuàng)建 group leader,直接調(diào) queue_me。線程1和線程2都同時(shí)觸發(fā) perf_event 的工作。調(diào)了 queue_me 之后,三個(gè)線程全部睡眠掉了。這個(gè)時(shí)候我需要再建另外一個(gè)新進(jìn)程,幫助我完成噴堆。噴堆并不是很難,1024 的對(duì)象,你直接噴堆,直接噴物理頁(yè)面,也是可以的??赡苄枰畮酌氲臅r(shí)間,把堆噴滿。這個(gè)時(shí)候我可以喚醒進(jìn)程,或者直接殺掉進(jìn)程,這都無(wú)所謂。線程調(diào)度器會(huì)調(diào) sched_in。disable 可以指向你想控制的任何一個(gè)地址。你可以執(zhí)行內(nèi)核任意代碼,把權(quán)限提到 root。
另外一個(gè) 2017-0403 漏洞。最后一條是被釋放的對(duì)象,每個(gè)方塊都可以代表 0X10 的字節(jié),它沒(méi)有任何指針,你怎么控制 PC 做代碼執(zhí)行呢?
free 之后你能鎖 use 的部分就是紅色的兩個(gè)框,是 0X10 的單位,可以寫(xiě)兩個(gè)指針的地址。A 是對(duì)象地址本身,第二個(gè)是 A+0X20 的位置。你可以用內(nèi)核的其它對(duì)象去噴對(duì),在 0X20 這個(gè)位置正好有一個(gè) buffer,把它的地址覆蓋成 A 地址本身,你真正寫(xiě)的是地址 A 本身,通過(guò)覆蓋地址 A 本身的方法把 UAF寫(xiě)成堆溢出,再覆蓋其它對(duì)象,這個(gè)時(shí)候就比較好寫(xiě)。如果不能控制 PC 指針,UAF 并不好寫(xiě)。
總結(jié)
演講后,科恩實(shí)驗(yàn)室在接受雷鋒網(wǎng)采訪時(shí)提到,Google 是非常重視安全的公司,在他們每月發(fā)的安全公告里面有很多安全漏洞,但絕大部分安全漏洞的利用場(chǎng)景非常困難,甚至是無(wú)法利用的,而其中真正威脅用戶手機(jī)的漏洞是比較少的。
智能手機(jī)剛剛興起時(shí),眾人對(duì)安全問(wèn)題考慮不多。而隨著攻擊途徑和攻擊方向的增加,安全問(wèn)題會(huì)被越來(lái)越重視。越來(lái)越多的人用新的方法去挖掘漏洞,其數(shù)量會(huì)變得越來(lái)越少。一旦發(fā)現(xiàn)新的攻擊方法、新的漏洞模式,可能就會(huì)有一波漏洞攻擊方法被爆發(fā)出來(lái)。隨著慢慢被重視,攻擊路徑被堵住。
因此,面對(duì)越來(lái)越健壯的 Android 內(nèi)核,攻擊面急劇縮緊,只剩下一部分系統(tǒng)調(diào)用可供攻擊。在這其中,品相好的漏洞相對(duì)于品相不好的漏洞并不多。
但科恩實(shí)驗(yàn)室的方家弘告訴雷鋒網(wǎng),一般來(lái)說(shuō)只要是漏洞都需要被修補(bǔ),事實(shí)上品相好的漏洞通常會(huì)更快修補(bǔ),因?yàn)楸焕玫奈kU(xiǎn)更大。這就是為什么優(yōu)秀的安全研究團(tuán)隊(duì),比如 project zero,常常輸出“高質(zhì)量”的漏洞,也就是能夠輸出漏洞和利用思路甚至是完整的利用代碼。漏洞的品相好壞有時(shí)候也很難判斷,除非寫(xiě)出具體的利用代碼。
當(dāng)然,這個(gè)其實(shí)不是安卓的特例。漏洞和很多東西一樣,比如玉石原石類,都有一個(gè)“品相”。漏洞的品相取決于很多因素,比如漏洞所在的攻擊面是否容易訪問(wèn)到、漏洞的觸發(fā)是否容易和穩(wěn)定,等等。所以有無(wú)法利用的漏洞是很正常的。
申迪也總結(jié)了面對(duì)無(wú)法利用的漏洞做法,首先要確定核心問(wèn)題到底是什么,是不是真的不能利用,還是有其它辦法。也可以借助內(nèi)核中的其它特性,或者參考過(guò)往的案例,畢竟有些思想是共通的,可以幫助你有些想法。
即使最后真的發(fā)現(xiàn)無(wú)解,也可以丟開(kāi)一段時(shí)間。他回憶起自己寫(xiě) 2014-0403 那個(gè)漏洞,也是丟開(kāi)了 1、2個(gè)月,突然想到一個(gè)方法寫(xiě)出來(lái)的。
最后,不要盲目報(bào)告一些無(wú)法利用漏洞,沒(méi)有人覺(jué)得你真的厲害。
雷峰網(wǎng)原創(chuàng)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見(jiàn)轉(zhuǎn)載須知。
本專題其他文章