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