天天看點

安卓核心UAF漏洞利用探秘

 雷鋒網編者按:8月16日,第三屆中國網際網路安全領袖峰會(css 2017)在北京國家會議中心召開。作為九大分會場之一的騰訊安全探索論壇(tsec)以“安全新探索”為主題,雲集了國際知名廠商及頂尖高校的資深安全專家,探讨全球資訊安全領域前沿技術、研究成果及未來趨勢。來自騰訊安全科恩實驗室的方家弘、申迪分享了面對安卓核心中存在的uaf漏洞數量不斷變小、利用難度逐漸變大的現狀,将如何穩定高效地利用這類漏洞來完成作業系統提權。

方家弘:大家下午好!我是來自騰訊安全科恩實驗室的方家弘,我和我的同僚申迪給大家帶來關于“安卓核心 uaf 漏洞利用探秘”的分享。

今天的分享主要分成四個部分。首先由我介紹在之前的一段時間出現的比較有代表性的安卓核心漏洞。其次要分析像 uaf 這樣一個特定類型的漏洞在利用方面有什麼樣的特點,它涉及到系統元件的基礎知識,友善大家了解後續的内容。之後的時間交給申迪,他會具體分析在 perf 子系統中發現的 uaf 漏洞的具體利用過程。最後,分享一點我們的總結和思考。

首先介紹安卓核心漏洞的簡要曆史。

為什麼要對安卓核心進行攻擊呢?

将安卓系統看做整體,最早系統中以不同的應用,使用不同的 uid ,對應用之間進行隔離。後來發現這并不能阻擋攻擊,又産生了加密檔案系統等等安全措施。

而絕大多數措施的實作都依賴于安卓的核心。攻擊核心本身是非常有助益的工作。在 windows 系統當中,攻擊核心可以直截了當的達到提權的目的。在安卓系統中,也可以繞過系統中設定的種種限制。對于壞人可以實作他不可告人的目的。

按照時間軸,根據我個人的了解列出了比較有代表性的核心漏洞。當然,不包括我們今天要講的漏洞。

安卓核心UAF漏洞利用探秘

2013 年 11 月份,cve-2013-6282 是我們的友商寫出的最早的漏洞,它是邏輯問題。在我們去年攻擊特斯拉的時候,驚訝地發現這個漏洞居然還存在。當時特斯拉比較自信的認為車輛不可能被攻破,是以他們在核心方面沒有做更多的防禦。在漏洞被報告,修複之後,特斯拉在核心方面做了非常激進的更新和修複。加粗的漏洞都屬于通用的安卓 root 漏洞。

2014 年,美國的兩個天才少年發現了 towelroot,這是引起比較大反響的通用 root。這個漏洞類型在當時較新穎,這個漏洞的利用方式也非常創新,引起了不小的波瀾。

2015 年 5 月份,我們發現了 cve-2015-3636。

2016年8月份,我們利用了 cve-2015-1805。

之後就是非常出名的 dirty cow,它也是屬于邏輯的問題。

2017年4月份,由 google 研究員發現的 wifi firmware 的漏洞,這是非常有創新特色的漏洞利用。

之前提到的是幾個有代表性的漏洞,6282 屬于業務邏輯的問題;1805 是屬于越界通路;3153、3636 都屬于 uaf 漏洞。今天我們主要針對 uaf 漏洞進行研究和探讨。

安卓核心UAF漏洞利用探秘

顧名思義,uaf 漏洞先要 after free,而要成功利用這個漏洞,有幾個步驟是不能少掉的。一是如何按照順序去觸發 free 和 use。這看似比較簡單,以 2015-3636 為例,use 的時機和 free 的時機非常可控,我們可以做任意想做的事情,比較好控制被 free 掉的空間。

3636 這個漏洞的品相非常好,但在很多情況下的控制環節上,可能會面臨競态的問題。在這種情況下,如何穩定的控制好 free 和 use 的順序,并且穩定的觸發 use,就是比較困難的問題。對于不同的漏洞來講,需要不同的技巧去處理。我們已經能夠把時序上的問題清楚,之後就是如何改變核心的控制力,如何控制代碼執行。控制出來被 free 的空間方法,不同的漏洞,有不同的方法。

這裡牽涉到最重要的就是 linux 核心的記憶體管理。我們以圖示的形式展示了 uaf 漏洞利用的方式。堆上有 abcd 四個對象。這是一個比較簡單的呈現方式,要做到能夠穩定有效的 free 掉目标對象,并且把我們想要的東西填進去,需要對核心存儲管理有比較深入的了解,也就牽涉到 linux 核心的堆管理器。

安卓核心UAF漏洞利用探秘

在安卓系統當中,我們對常見的堆管理器叫 slub,它替換了之前常見的 slab。從管理結構上來講,slub 是簡化版的,u 就是 unqueued 的意思。queued 是隊列,做堆管理器,總要有隊列,申請的時候從這裡拿。它取消了單獨存在隊列的結構,這樣就使得完全空閑的 slub 被完全釋放掉。它存在 per cpu 的 slab,它的釋放和申請的過程會非常快。為了實作相容性,linux 的管理是抽象到 kmem 層。之前的核心代碼、核心驅動,不需要做任何的修改。如果大家自己編譯過 linux 核心,隻需要選擇什麼樣的堆管理器就可以直接使用,其它代碼都不用更改。

安卓核心UAF漏洞利用探秘

首先看一下 slub 的堆塊長什麼樣子。linux 核心當中實體記憶體的頁面管理是通過 buddy 進行的。要符合實體頁面管理的原則。在 slub 當中,都是二的 n 次方組成頁組成的。它實際上是巧妙的利用了實體頁面描述服的聯合,實作了管理。我配置設定好給這個堆塊的這些頁,第一個頁面的描述符上就會記錄堆塊中可使用的第一個對象。這就是空閑對象清單的頭部。在空閑對象的頭部,又會有一個指針,指到下一個空閑對象。

安卓核心UAF漏洞利用探秘

這個東西沒有額外的中繼資料,所有的中繼資料隻存在于原有的結構體當中,存在于你配置設定給這個堆塊的頁面當中。這也帶來了一些特性,使得它可以幫助我們對特定的漏洞進行利用。

前面提到了 cpu_slub 的概念,配置設定和釋放都是快速的過程。目前配置設定的對象在于 cpu 綁定 slub 上面,就會進入快速配置設定的流程。不管怎麼樣,對于使用堆管理器的使用者來講,肯定會得到空閑的 slub 對象供使用。具體怎麼操作,就由 slub 的堆管理器進行。

安卓核心UAF漏洞利用探秘

為什麼要設定 cpu_slub,大部分情況下在一個排程周期内會有頻繁的對象配置設定操作。釋放也是這樣的情況,目前的對象就是隸屬于目前的 cpu_slub,這就帶來了另外一個非常好的特性。目前 cpu 上釋放的對象,我馬上要申請的,肯定申請到剛剛釋放的對象,這對于填充是非常好的特性。這個特性在其它的漏洞利用當中也會使用到。同時,釋放也存在 slow path,這是不可避免的情況。

安卓核心UAF漏洞利用探秘

這裡對 slub 的特性進行了歸納。按照對象的大小會做一個合并,這會對漏洞利用帶來一些問題,你可能不知道這樣的堆塊當中放的還有其他什麼對象。

安卓核心UAF漏洞利用探秘

接下來看兩個漏洞的案例,這兩個案例充分利用了 slub 堆管理器的特性。

首先是 cve-2015-1805。

安卓核心UAF漏洞利用探秘

iovec 是資料核心中傳遞資料的結構。這個漏洞本身是 overrun,牽涉到我們在核心當中如何申請可控的overrun 數組。在安卓當中,很多 api 是被禁用的。最終我們找到 sendmmsg 的調用,你可以得到内容完全可控的數組。它的壞處是放完以後就被銷毀掉了。

這個對象本身的生命周期不夠長。看似這不是很好的對象,實際上可以回想起之前的一點,在 slub 中一個對象被釋放之後,僅僅是在對象的頭部寫入了指針,這個指針指向下一個可以使用的對象。

安卓核心UAF漏洞利用探秘

2015-1805 的代碼路徑當中,如果 iov 是 0,根本就不會被處理。如果說噴射的夠快,漏洞利用過程夠快,被釋放掉的 iov 本身還是空的對象,或者又被另一個 iov 填上,根本不會對漏洞造成任何影響。我們隻需要控制填進去的第一個 iov 的長度是 0,它就會被忽略掉。即便被釋放,這個結果還是有效的 iov。

安卓核心UAF漏洞利用探秘

第二個漏洞,應該是 2016-6187。通過這個方法,把一個品相非常差的漏洞,越界寫一個字元節的 0,變成可能導緻代碼執行的情況。最後也是通過 freelist 指針的特性。

接下來的時間交給申迪,由他分析一下在 perf 子系統中發現的漏洞利用。

申迪:下面給大家介紹我在去年發現的兩個 perf 系統的 uaf。在安卓手機上有很多使用者有一鍵 root 的需求。一些 uaf 漏洞的品相并不是特别好,但我至少要寫到達到 90% 的成功率。我主要介紹兩個在去年年初發現,年底報給 google 的漏洞。

安卓核心UAF漏洞利用探秘

第一個是 cve-2016-6787,它是通過 race觸發的漏洞,核心立即崩潰。上周我在 blackhat 講了繞過三星 knox 的防護機制。

第二個漏洞是 cve-2017-0403,這是今年才修複的漏洞。這個漏洞有它自己的難點。

在講漏洞之前,先講一下 perf 是什麼東西,它是系統調用,任何安卓都可以調這個系統調用,攻擊核心。這個系統調用有很多問題,我也發現不止兩個問題,這邊兩個是比較好利用的,單獨拿出來講一下。這個系統調用的參數比較複雜,其中一個是你怎麼定義你想生成的統計事件,你想定義代碼執行的指定周期,或者是真正調用進入核心的時候又調用了哪些調用,它包含了千奇百怪的 perf 事件,可能會進入到分支。

這個系統調用,不管中間發生了什麼,最後總會生成 perf_event。每個都不是孤立的,有可能是一組形式出現的,每個 groun 都有一個 leader,每個程序裡有多個組,又有大的容器包括這個組。每個程序裡有兩個 perf_event_context,把作為的 group leader 串聯起來,這 list 把組内的串聯起來,這三個核心對象有一個比較複雜的連接配接的關系,其實就是因為這些關系搞得很複雜,系統調用裡出了一些 uaf 的問題。

安卓核心UAF漏洞利用探秘

googl 在 2016 年下半年覺得這個系統調用太危險,直接封掉了。去年年初,這個調用還可以被其它的調用調到,我利用這兩個漏洞完成了手機上的 root。

第一個漏洞的問題主要是由于系統調用裡的 move_group。

安卓核心UAF漏洞利用探秘

如果 group leader 是軟體的,你要插入一個硬體的,這個時候會挪動大量清單相連的關系。首先會把 group leader 從指針上提取出來,把每個 event 粘出來。這兩個操作完成之後,又看到紅框裡的代碼,減了 1,這個減 1 沒有考慮到并發操作,把一個組摘除了兩遍。正常情況下,隻減 1,并發操作就要減 2,被多減了一個,引用減到 0,導緻觸發釋放。

安卓核心UAF漏洞利用探秘

主線程先建立一個 group leader,線程 1 觸發 move_group操作。右邊出現了崩潰,實際是因為 perf_event context 被提前釋放。你的程序在第二次進入排程的時候,進入排程器會調入一行,周期執行完了會換出,換出之後再被程序排程器換入。

安卓核心UAF漏洞利用探秘

這個時候就會觸發操作。關鍵是線程排程是很頻繁的,基本不是肉眼可觀測的。漏洞一旦觸發,核心立即就崩潰了。實際上它不是立即崩潰,實際上有一個微笑的操作,線程已經被換出,換入的時候又崩潰了。假如是一個立即的崩潰,對我來說就是毫無意義。這個時候就要集中解決一個問題,怎麼讓核心不立即崩潰,給我争取到足夠的時間去噴堆。

安卓核心UAF漏洞利用探秘

在講這個問題之前,要先講一下 linux 核心的排程知識,包括很多線程,每個線程不可能同時執行。真正執行的是一個,其它都是等待執行。它執行一段時間之後,會有幾種情況。有一種情況就是以後再也不需要執行,它就會被殺掉。如果該想繼續執行,配合的時間片已經被用盡了,這個時候就被線程排程器切換出去。這種時候就會進入到 interruptible 或者是 uninterruptible。

futex 這個調用可以幫助我們完成真正的睡眠。一旦線程進入睡眠的狀态,是不會被任何人主動喚醒。這對我們來說是最理想的狀态。

安卓核心UAF漏洞利用探秘

有了這個函數的幫助,整個調用邏輯就變成這樣的,從左到右,從觸發漏洞,到噴堆、代碼執行控制完整的流程圖。還是主線程建立 group leader,直接調 queue_me。線程1和線程2都同時觸發 perf_event 的工作。調了 queue_me 之後,三個線程全部睡眠掉了。這個時候我需要再建另外一個新程序,幫助我完成噴堆。噴堆并不是很難,1024 的對象,你直接噴堆,直接噴實體頁面,也是可以的。可能需要十幾秒的時間,把堆噴滿。這個時候我可以喚醒程序,或者直接殺掉程序,這都無所謂。線程排程器會調 sched_in。disable 可以指向你想控制的任何一個位址。你可以執行核心任意代碼,把權限提到 root。

安卓核心UAF漏洞利用探秘

另外一個 2017-0403 漏洞。最後一條是被釋放的對象,每個方塊都可以代表 0x10 的位元組,它沒有任何指針,你怎麼控制 pc 做代碼執行呢?

free 之後你能鎖 use 的部分就是紅色的兩個框,是 0x10 的機關,可以寫兩個指針的位址。a 是對象位址本身,第二個是 a+0x20 的位置。你可以用核心的其它對象去噴對,在 0x20 這個位置正好有一個 buffer,把它的位址覆寫成 a 位址本身,你真正寫的是位址 a 本身,通過覆寫位址 a 本身的方法把 uaf寫成堆溢出,再覆寫其它對象,這個時候就比較好寫。如果不能控制 pc 指針,uaf 并不好寫。

安卓核心UAF漏洞利用探秘

總結

演講後,科恩實驗室在接受雷鋒網(公衆号:雷鋒網)采訪時提到,google 是非常重視安全的公司,在他們每月發的安全公告裡面有很多安全漏洞,但絕大部分安全漏洞的利用場景非常困難,甚至是無法利用的,而其中真正威脅使用者手機的漏洞是比較少的。

智能手機剛剛興起時,衆人對安全問題考慮不多。而随着攻擊途徑和攻擊方向的增加,安全問題會被越來越重視。越來越多的人用新的方法去挖掘漏洞,其數量會變得越來越少。一旦發現新的攻擊方法、新的漏洞模式,可能就會有一波漏洞攻擊方法被爆發出來。随着慢慢被重視,攻擊路徑被堵住。

是以,面對越來越健壯的 android 核心,攻擊面急劇縮緊,隻剩下一部分系統調用可供攻擊。在這其中,品相好的漏洞相對于品相不好的漏洞并不多。

但科恩實驗室的方家弘告訴雷鋒網,一般來說隻要是漏洞都需要被修補,事實上品相好的漏洞通常會更快修補,因為被利用的危險更大。這就是為什麼優秀的安全研究團隊,比如 project zero,常常輸出“高品質”的漏洞,也就是能夠輸出漏洞和利用思路甚至是完整的利用代碼。漏洞的品相好壞有時候也很難判斷,除非寫出具體的利用代碼。

當然,這個其實不是安卓的特例。漏洞和很多東西一樣,比如玉石原石類,都有一個“品相”。漏洞的品相取決于很多因素,比如漏洞所在的攻擊面是否容易通路到、漏洞的觸發是否容易和穩定,等等。是以有無法利用的漏洞是很正常的。

申迪也總結了面對無法利用的漏洞做法,首先要确定核心問題到底是什麼,是不是真的不能利用,還是有其它辦法。也可以借助核心中的其它特性,或者參考過往的案例,畢竟有些思想是共通的,可以幫助你有些想法。

即使最後真的發現無解,也可以丢開一段時間。他回憶起自己寫 2014-0403 那個漏洞,也是丢開了 1、2個月,突然想到一個方法寫出來的。

最後,不要盲目報告一些無法利用漏洞,沒有人覺得你真的厲害。

  本文作者:又田

繼續閱讀