Metaphor-A real life Stagefright exploit

wyzsk發表於2020-08-19
作者: virustracker · 2016/03/30 17:38

http://exploit-db.com/docs/39527.pdf

0x00 綜述


在本文中,我們研究瞭如何利用安卓系統上最臭名昭著的漏洞之一Stagefright。在此之前,我們一直認為這個漏洞是很難被利用的。在研究中,我們大量參考了Google公佈的文章-exploit-38226和研究報告Google Project Zero: Stagefrightened。

本文中呈現了我們的研究結果,詳細的介紹了這個漏洞的侷限性,提供了一種繞過ASLR的途徑,併為後繼者提出了一些研究建議。

NorthBit團隊利用的這個漏洞,能夠影響到 Android 2.2-4.0和5.0-5.1版本,同時,還能夠繞過Android 5.0-5.1上的ASLR技術(Android 2.2-4.0版本上沒有實現ASLR)。

0x01 Stagefright


Stagefright是Android系統中的一個多媒體庫。直到2015年7月27日,幾個關鍵堆溢位漏洞的曝光,人們才開始注意到了Stagefright。這個漏洞最初是由Zimperium的Joshua Drake發現的,Android版本包括從1.0到5.1都受到了影響。

在下面的部分中,我們會用“libstagefright”代指Stagefright媒體庫,用“stagefright”代指相應的bug。

雖然,多個Android版本的系統中都存在這個bug(接近1,000,000,000臺裝置),但是,很多人都認為這個漏洞無法被利用,主要是因為新版的Android實現了ASLR保護技術。

0x02 Metaphor


Metaphor代指的是我們對Stagefright的利用過程。在文中,我們詳細的研究了libstagefright,並提出了一種可以繞過ASLR的新技術。和Google團隊一樣,我們利用了CVE20153864,因為相較於Joshua Drake發現的CVE20151538漏洞,這個漏洞更容易實現。

0x03 研究目標


我們之所以會持續研究這個媒體庫,是因為其已經被證明了確實存在漏洞(bug和壞程式碼太多),受影響的裝置成千上萬,潛在的攻擊途徑也多種多樣:mms(秘密進行),簡訊(自動進行),web瀏覽器(很少或幾乎不需要使用者互動)等等。

相比於前人的努力,我們希望實現一種更具普適性和可行性的利用方式,而可行性指的就是快速,可靠並且難以發現-理想情況下,只利用現有的漏洞。

總而言之,我們的目標是繞過ASLR。

0x04 MPEG-4檔案


要想理解這個漏洞,首先要理解MPEG-4檔案格式。還好不難:這種檔案就是TLV(型別-長度-值TypeLengthValue)資料塊的集合。在這種編碼方式中,會有一個“type”值指定資料塊型別,一個“length”值指定資料長度,一個“chunk”值指定資料本身。

以MPEG-4為例,首先編碼“length”,然後是“type”,最後是“value”。下面的pseudo-C描述的就是MPEG-4的資料塊格式:

p1

當長度為0時,資料會一直持續到檔案末尾。atom欄位是一個短字串(也叫作FourCC),描述的是資料塊型別。

對於需要超過2^32個位元組的型別,使用的格式也稍有不同:

p2

在採用了樹結構的型別中,子資料塊存在於父資料塊的資料中。

下圖中描述了一個媒體檔案:

p3

Bug-CVE20153864

因為已經有很多文章都介紹過這個bug,所以這裡就不多說了。我們使用了Android 5.1.0的原始碼,如果有特殊情況,我們還會再做說明。

libstagefright中的這個bug出現在解析MPEG-4檔案的過程中,或更具體的說,是解析tx3g atom欄位,這個欄位的作用是向媒體中嵌入字幕。

首先,我們看看這段程式碼的作用:

p4

p5

相當簡單-這段程式碼會收集所有的字幕資料塊,並將其附到一個長緩衝區中。

檢查了size和chunk_size,並且在我們的控制下,允許我們在這裡造成了一個整數緩衝區溢位:

p6

要想實現堆溢位,我們需要至少一個合法的tx3g資料塊,在整數溢位部分和堆溢位中都需要用到:

p7

無論緩衝區實際分配的大小是多少,最終會導致data部分中的size位元組被寫到緩衝區中。

在構造堆時要注意,我們能夠:

  • 控制size-寫入多少位元組
  • 控制data-寫入多少資料
  • 預測我們的物件會被分配到什麼位置
  • 分配的大小在我們的控制下
  • Android使用了jemalloc作為其堆分配器(稍後再做說明)

考慮到這裡,既然我們能夠控制堆溢位的大小和資料,那麼漏洞利用應該很容易實現。但是,實際中的限制很多,複雜化了漏洞的利用過程。

0x05 漏洞利用


在這一部分,我們介紹了漏洞的利用原理和限制,以及一些與漏洞利用相關的發現。

攻擊途徑

這個漏洞存在於媒體解析過程中,也就是說,受害者的裝置甚至不需要播放媒體,而只需要解析即可。媒體解析過程是為了獲取其後設資料,比如影片長度,藝術家名稱,標題,字幕,註釋等等。

最後,我們選擇的攻擊途徑是透過web瀏覽器,因為需要執行JavaScript,這樣做的優缺點也很明顯。可以使用下面的這些方法來誘使受害者訪問我們的惡意網頁:

  • 攻擊性網站-可以偽裝成“線上觀看全高畫質<最新電影>”
  • 入侵合法網站-頁面看似合法,但植入了隱藏內容(iframe,隱藏標籤…)
  • XSS-可信網站中植入惡意內容
  • 廣告-只存在於<script><iframe>標籤
  • Drive-by攻擊
  • 免費WiFi-自動彈出web瀏覽器,開啟含有惡意內容的門戶
  • 公交站上的二維碼-在等公交車時,掃描下載遊戲

下面的這些攻擊途徑不適用於我們的方法:

  • WEB
  • 廣告-“合法”(或非合法)廣告作為漏洞媒體
  • 部落格或論壇帖子-內嵌媒體
  • MMS-自動獲取和解析
  • 已經在Android 5.1+上禁用
  • 即時資訊
  • WhatsApp, Telegram, Viber, Skype, Facebook Messenger等
  • 交友軟體-攻擊者介紹中存在漏洞媒體

受害者需要在攻擊性網頁上駐留一會兒。社會工程可能會增加這一漏洞的有效性-或其他針對受害者的長期攻擊方法,比如更改主頁。

將vtable重定向到堆

再次檢視存在漏洞的程式碼:

p8

最簡單的利用方法是構建堆,這樣的話,mDataSource物件會在緩衝區溢位後立刻被分配,然後利用這個bug,用我們的vtable(虛擬表)覆蓋mDataSource的vtable,並設定對應的readAt入口點指向我們自己的記憶體。這就是exploit-38226的實現方式。

  • 我們可以完全控制虛擬表-重定向任意方法到任意的程式碼地址
  • 需要知道或猜測我們的虛假vtable地址-Google已經證明了這是可以預測的
  • 要想找到ROP鏈Gadgets,需要知道libc.so函式的地址-即破解ASLR

Heap Shaping

要想更好地理解Metaphor,以及如何繞過ASLR,需要了解Android堆分配器-jemalloc的工作原理。

目前,你只需要知道jemalloc會將大小相似的物件分配到同一個run中。同一個run中會包含多個大小相同的緩衝區,這些緩衝區就叫做regions。稍小於region固定大小的物件會被向上舍入。

具體參見下圖:

p9

Heap Spraying

Heap Spraying是使用pssh atom完成的。當解析器遇到一個pssh資料塊時,解析器會分配一個緩衝區,並將其附到一個緩衝區列表中:

p10

我們控制其大小,我們就可以提供較大的值。這種方法的侷限性在於,媒體檔案中必須要有相應大小的資料,不過接下來我們會說明如何克服這一限制。

Heap Grooming

Heap Grooming不同於Heap Spraying簡單地分配大量的物件。透過控制分配和釋放順序,我們可以採用一種可預見的方式來設計堆物件的順序。在exploit-38226中,這是利用avcC和hvcC資料塊實現的:

p11

(hvcC基本上是一致的)

解析器分配一個可控大小的緩衝區,然後將其傳遞給MetaData::setData。MetaData::setData會將資料複製到一個新的緩衝區,然後刪除先前的緩衝區,當然其大小也在我們的控制下。

這種方法不一定能在所有裝置上應用,或許是因為jemalloc的配置不同,並且需要分配兩個大小相同的緩衝區-其中一個是MPEG4Extractor::p arse3GPPMetaData中的臨時緩衝區,另一個用於內部的MetaData物件。

一種更具普適性的Heap Grooming方法是使用MPEG-4 atoms,pref,auth和gnre。這些欄位都是在MPEG4Extractor::parse3GPPMetaData: 的內部進行解析:

p12

MetaData::setCString複製了一個從緩衝區+6開始的空終止(null-terminated)字串:

p13

我們可以透過chunk_size控制臨時緩衝區的大小,透過null位元組的位置來控制實際複製哪個緩衝區,這樣我們就可以將臨時物件分配到另一個的run中,並提高漏洞利用的靈活性。

注意,一旦我們向MetaData中新增一個已有的專案,舊專案就會被替換。前面提到的MPEG-4 atoms為我們提供了4個相同的基元素(primitives)用於對堆進行控制。

為了覆蓋mDataSource,我們需要將其移動到堆底的一個位置,在這個位置上我們就可以預測到堆的位置。這一過程和exploit-38226一樣,都使用了stbl atom來重新分配mDataSource:

p14

p15

注意,stbl atom分配了一個新的MPEG4DataSource-因為我們的攻擊途徑是透過web瀏覽器,所以,mDataSource的型別是NuCachedSource2,NuCachedSource2::flags是:

p16

下圖中描述的是透過Heap Grooming讓堆溢位mDataSource的過程:

p17

ssh atoms用於Heap Spraying,這樣新的堆就會按照預定順序執行。然後,使用titl和gnre atoms作為佔位符-首先分配titl,然後分配gnre,釋放了gnre後,我們再使用stbl atom分配一個MPEG4DataSource。

當我們釋放了titl後,這個資料塊就會被釋放,因為在下一次分配時,tx3g atom會替代其位置。

ROP鏈Gadgets

我們稍微修改了谷歌在exploit-38226中提出的ROP鏈。例如,mmap和memcpy被用於了分配shellcode-而實際上,已經有一個緩衝區的地址是已知的了:

p18

我們可以簡單地用mprotect來替換這兩個gadgets。

(注意,並不是所有裝置上都是這一個地址)

Google使用了很複雜的gadgets從棧中彈出大量不必要的引數,導致複雜化了ROP鏈。而我們只是簡單地使用了pop {pc} 和pop {r0, r1, r2, pc} 指令。

和Google Project Zero: Stagefrightened一樣,我們使用了相同的stack pivot gadget:

p19

“這樣可以載入大部分暫存器,包括在r0上的棧指標,這個指標指向了我們控制的資料。此時,可以很簡單地透過利用ROP鏈來分配RWX記憶體,將其複製到shellcode中,然後使用libc.so中的函式和gadgets來跳轉。”(Google Project Zero)

我們的遠端程式碼執行漏洞需要知道下面的四個地址:

  1. Call void函式:

    pop{pc}

  2. 最多有3個引數的Call 函式:

    pop{r0,r1,r2,pc}

  3. 替換棧並呼叫shellcode:

    add r2,r0,#76;0x4c

    ldm r2,{r4,r5,r6,r7,r8,r9,r10,r11,r12,sp,lr}

    ...

    ...

    bx lr

  4. 使用mprotect將一個區域標記為可執行,然後返回:

    ...

    ...

    bx lr

我們已經知道了mDataSource的確切大小-在溢位時,其型別是MPEG4DataSource:

p20

IDA中顯示,readAt在vtable中的偏移是7:

p21

readAt在位元組中的偏移是:

p22

在所有測試過的裝置中,MPEG4DataSource的大小和readAt的偏移都是相同的。

最後的ROP鏈在棧中看起來是這樣的,從中可以看出是哪個暫存器複製了該專案:

p23

其他的程式碼指定漏洞都類似於exploit-38226。

破解ASLR

破解ASLR需要一些裝置資訊,因為不同的裝置會使用不同的配置-可能會修改一些偏移位置或可預測的地址位置。

利用同一個漏洞,也可能獲取到任意的指標讀取,導致web瀏覽器洩露,從而收集破解ASLR需要的資訊。但是,我們讀取記憶體的能力有限,因為這個漏洞存在諸多限制。

JavaScript的功能

因為是透過web瀏覽器來攻擊,所以我們假設能夠執行JavaScript。編碼在媒體檔案中的後設資料可以透過JavaScript使用某些<video>標籤屬性來訪問,比如videoWidth,videoHeight和duration。

我們可以利用堆溢位漏洞用一個指標覆蓋這個後設資料到任意的記憶體位置,這樣,任意的記憶體都可以被髮送回瀏覽器,然後透過JavaScript讀取。

返回後設資料

所有的後設資料都儲存在MetaData類中。這類媒體具有自己的後設資料,叫做mFileMetaData:

p24

並且每個Track都有自己的meta欄位:

p25

只有把mInitCheck設定為OK,後設資料才可以返回到瀏覽器:

p26

只有在解析moov atom時,才可以設定mInitCheck:

p27

今早將“moov”資料塊整合到媒體檔案中,能夠保證後設資料可以返回到web瀏覽器。

注意:這種方法不適用於Android 4.4.4及更低版本。這些版本上的程式碼似乎只會接收包含整個檔案部分的moov資料塊。否則,只要“moov”資料塊結束,則會返回NKNOWN_ERROR,因為缺少DRM內容,MPEG4的“sidx”和“moof” atoms都會終止解析:

p28

所以,這種方法只能適用於Android 5.0-5.1。

溢位後返回後設資料

我們無法重複使用同一個媒體檔案來執行多次溢位-在觸發了tx3g bug後,無法避免ERROR_IO從MPEG4Extractor::parseChunk返回:

p29

返回值會轉換成size_t(32位)並與chunk_size(對比)對比-要想實現整數溢位,這個值要遠遠大於2^32

MPEG4Extractor::parseChunk會接收一個資料塊offset和資料塊depth。這種方法可以解析資料塊並處理超前偏移。

p30

對於某些MPEG-4 atoms,還會遞迴解析內部的資料塊。如果解析成功,偏移會前進到資料塊末尾。

在使用較大的值造成溢位後,我們從tx3g解析返回到這裡:

p31

我們得到了:

p32

p33

所以,如果返回了ERROR_IO,所有的解析都會停止:

p34

也就是說,我們無法使用同一個媒體檔案來執行多次溢位。

繞過程式終止

在使用HTTP來處理影片流時,mDataSource的型別是NuCachedSource2.mDataSource>readAt指定的NuCachedSource2::readAt會觸發呼叫NuCachedSource2::readInternal,如果size過大,mediaserver就會被終止:

p35

在失敗時,CHECK_LE會終止程式,因為要想利用成功,size的值需要很大,每當我們嘗試利用bug時,NuCachedSource2::readInternal檢查總是會失敗。

要想避免程式終止,我們需要繞過NuCachedSource2::readInternal呼叫。透過使用XMLHttpRequest和responseType=‘blob’從JavaScript載入媒體,瀏覽器會把影片快取到檔案系統。使用URL.createObjectURL,我們可以這樣來引用快取檔案:

p36

p37

URL.createObjectURL函式會建立一個URL來引用xhr.response中的data資料塊。

下面是一個物件URL示例:

p38

當Chrome 嘗試讀取“ blob” URLs時,實際是作為本地資源訪問。我們可以看到Chrome快取中的檔案:(“ls -a”顯示隱藏檔案)

p39

mediaserver中確實有一個描述符指向了這裡:(“ls -l”後是連結)

P40

因為這個URL指向了檔案系統,mediaserver把資料來源(在我們這裡是mDataSource)設定到了一個FileSource類的物件,而不是NuCachedSource2 類。這兩個類之間有差別,NuCachedSource2處理的是HTTP流和線上媒體快取,而FileSource能夠在本地檔案上執行查詢和讀取操作。 FileSource::readAt 並沒有使用任何CHECK_xx宏-也就是說我們繞過了程式終止的問題。

洩露資訊

我們前面提到過,mediaserver會解析媒體檔案中的後設資料並將其傳送給web瀏覽器。後設資料儲存在MetaData物件中,所有的資料都儲存在MetaData物件的mltems欄位中,這些欄位實際上是MetaData::typed_data值所對應的FourCC(4字元程式碼)秘鑰:

p41

typed_data也在同一個檔案中:

p42

如果mSize大於4,ext_data會指向記憶體中儲存資料的位置。否則,資料會儲存在儲存器中。注意,這兩者是一個並集,也就是說,ext_data和儲存器共用同一個地址。

KeyedVector物件的資料儲存在mStorage欄位中(由VectorImpl類繼承):

p43

mStorage的內容是一系列的秘鑰和MetaData::typed_data 元素。在GDB中看起來是這樣的:

p44

例如:

p45

透過覆蓋mStorage的內容,我們可以覆蓋後設資料指標,從而指向記憶體中的任意位置,這樣,資訊就可以洩露給web瀏覽器!

注意,只要size大於4,就是一個指標,但是我們可以控制size的大小-我們可以將後設資料欄位的大小設定為4或更小,避免不必要的後設資料欄位使用指標。即使是強制的mime型欄位,我們也可以將其設定成一個size不大於4的空終止字串(null-terminated)。

因為mSize必須大於4,我們只能透過duration欄位來實現記憶體洩露-因為duration是8位元組長度,所以也是一個指標。videoWidth和videoHeight欄位只有4位元組,所以無法用來洩露記憶體。如果把這些欄位設定大於4,那麼就會導致程式終止。

KeyedVector<key, value>使用SortedVector<key_value_pair_t<key,value>>儲存其資料。當一個新的值新增到KeyedVector時,這個值會被插入到排序向量,這樣,元素順序仍然會根據秘鑰排序。

下面是一些原始的KeyedVector 資料,來自一個具有多組後設資料的媒體檔案,其順序就是按照秘鑰排序:

p46

我們需要在崩潰之前,知道插入了多少個後設資料元素。這點做起來不難,因為我們控制了媒體檔案,也就能預測其狀態。在覆蓋元素時,不需要使用相同型別的元素,只需要保證元素順序是根據秘鑰來排序(如上)。

總的來說,我們需要用排序元素覆蓋一些16位元組的機構。

這些元素的型別是:

p47

如上所示。

Heap Grooming也是採用類似的方式完成的,使用相同的MPEG-4 atoms(itl,gnre,auth,pref)溢位mDataSource;並利用同一個堆溢位漏洞CVE20153864來實現覆蓋。

p48

在後設資料返回給瀏覽器之前,duration最終會作為字串返回。下面是最長的duration欄位:

p49

最終,其時長從百分之一秒轉換成了千分之一秒,會造成資料丟失。接著,我們就可以把一個8位元組的整數洩露給瀏覽器,準確率在±500

值得注意的是瀏覽器過濾的最高位會設定為負值,無窮大或NaNs(這些數值還有很多表示方式)。這些值會被忽略掉,而duration欄位會設定為0。

注意,PRId64 是一個有簽名的64位整數。考慮到整個轉換過程,可能的最大有效值是:

p50

更高的值會溢位到簽名位,而瀏覽器則會過濾到這個值,因為負值長度(無窮大/NaN)是沒有意義的。

只要23個最高位填充的都是0,並且由於向上舍入到1000,造成了幾個位的丟失,我們最終可以洩露8個位元組。而根據這個值,我們能獲取到32~35個可用的位。

ASLR的弱點

32位ARM Linux系統上的ASLR演算法很簡單-ASLR會將所有的模組隨機向下移動幾頁,範圍在0~255頁。移動的頁面數量叫做ASLR slide,這個值是在程式啟動是生成的,並且會持續整個程式週期。

p51

然後,這個值會被傳遞到mmap_base:

p52

p53

每個模組都會載入到其首選基址,然後再按照ASLR slide移動。為了進行驗證,我們執行了mediaserver上百次-記錄下了所有模組的可能地址範圍。與ASLR隨機化一致,總共的地址範圍有256頁,與其他模組的距離保持不變。所以說,所有模組的ASLR slide都是一樣的。

因為所有模組都使用同一個ASLR slide,我們只需要知道一個模組的基址就能夠知道所有其他模組的記憶體佈局,和上面提到的一樣,只有256個選項。

我們可以使用先前建立的裝置版本查詢表來獲取gadget的偏移。只要我們知道了一個模組的基址,我們就能夠把這些gadget偏移翻譯成絕對地址。

裝置指紋

比較方便的是,所有必須的gadget都貯存在libc.so中。由於不同裝置使用不同版本的libc.so,所以gadget偏移也會變化。但是,在很多情況下,你可以只根據User-Agent HTTP標頭來獲取裝置指紋-因為標頭中可能會有裝置建立版本和Android版本。

透過構建裝置版本查詢表來獲取gadget偏移,我們省去了執行一些高成本操作的需要。最後,要想執行遠端程式碼,還需要先知道libc.so在執行時的基址。

查詢libc.so

透過研究/proc/pid/maps,我們發現模組位置基本上是可以預測的,其最大距離是256頁。假設,有一個可讀記憶體部分大於256頁(1MB),我們可以確定模組的首選基址可能的最大偏移是255頁。

下圖是對這種概念的解釋:

p54

libicui18n.so模組能夠執行,因為其程式碼部分是可讀的,並且大於1MB:

p55

注意,有些模組的相鄰部分(例如,text,data)之間有保護頁面,但是,我們只需要足夠大的連續記憶體區域。在這種情況下,text部分就足夠大了,因為其大小有1,388頁。

ASLR slide是模組首選基址和執行時基址之間的距離:

p56

所以:

p57

(注意,ASLR向下移動)

我們知道ELF標頭必須是頁面對其,很奇怪的是,ELF標頭位於可執行部分的開頭:

p58

從首選基址開始,每次向下移動一頁,我們最後停在了ELF標頭上。但是,我們無法洩露ELF標頭,因為這種方法的最大限制就是8位元組。ELF標頭的前8個位元組是:

p59

下面是洩露限制,最大不超過:

p60

其中有一個欄位很特殊,因為這個欄位的最高位總是0,所以我們可以洩露這個欄位-位於偏移0x88上,第三個程式標頭表的p_memsz和p_flags:

ELF標頭的0x34位元組+前兩個程式標頭表的0x20位元組+欄位偏移的0x14位元組。

p61

下圖中顯示的是ELF檔案格式和感興趣的欄位:

p62

第三個程式標頭表中的memsz(p_memsz) 欄位符合我們的標準-可讀,模組是唯一的,位置固定。

下面的命令可以轉儲ELF標頭值,這樣我們就能夠找到前面提到的值了:

p63

p64

(注意,這個8位元組值同樣有p_flags,但是,這個值看起來很小,不會超過最大值限制)

現在,我們可以建立一個libicui8n.so p_memsz欄位查詢表,每個裝置對應一個專案。

透過這種方法,每當受害者解析一個媒體檔案時,一些資訊就會透過這些欄位洩露。要想找到ELF標頭,修復gadget偏移到絕對地址,受害者必須要下載和解析256個媒體檔案。由於heap spray,用於執行程式碼的媒體檔案可能會比較大才能讓heap spray落在預測地址-大約32MB或更大。

HTTP支援GZIP壓縮的內容。對於一個32MB的媒體檔案而言,其中大多數填充是0,我們總共可以得到33kB的網路流量-小了接近1000倍-這樣漏洞利用才能夠可行。

0x06 彙總


我們的漏洞由幾個模組組成,用於實現自動化和實時生成漏洞。這些模組包括:

  • Crash

生成小的通用媒體檔案

造成mediaserver崩潰,從而重置其狀態

在自動測試和建立查詢表時,檢查是否存在漏洞

  • RCE

根據特定的裝置,生成一個媒體檔案,用於在mediaserver中執行shellcode

查詢表提供了gadget偏移和libc.so首選基址

接收執行時的ASLR slide作為一個引數,並將gadget偏移翻譯成絕對地址

  • Leak

根據特定裝置,生成一個媒體檔案,用於從mediaserver程式中洩露記憶體

接收一個地址作為引數,從這個地址中洩露資料-這個地址可以是ummapped或保護頁面-導致崩潰

需要透過<video>標籤的duration欄位返回資訊

需要web瀏覽器支援blob響應型別的XmlHttpRequest

舊版瀏覽器不支援

從Chrome19開始支援

三星SBrowser基於Chromium-最早版本基於Chromium 18

與ROM關係不大,舊版瀏覽器可能沒有實現ASLR

下面是完整的漏洞利用流程:

p65

0x07 最後要求


本文中提出的這些方法都需要提前瞭解受害者的裝置。即使能觀察到受害者的User-Agent標頭,但是,僅僅憑藉這一點也無法瞭解關鍵的裝置資訊,比如gadget偏移或可預測地址。

查詢表使用了裝置建立版本來查詢漏洞利用所需要的相關資訊。要想建立查詢表,你需要:

  • libc.so

提取首選基址

提取4個必要gadget(在ROP Chain Gadgets 章節提到過)

pop {pc}

pop {r0,r1,r2,pc}

stack pivot gadget地址

mprotect地址
  • libicui8n.so

提取首選基址

計算與libc.so之間的距離

ELF標頭模組標示符

  • jemalloc 配置

jemalloc區域的大小

可以從libc.so提取

可以在裝置上執行測試程式來獲取這些值

  • 可預測的heap spray地址

不同裝置上的最優heap spray地址是不同的,在現實中,不同裝置可能使用同一個地址

最佳選擇是多次測試裝置

透過進一步的研究,有可能可以不使用查詢表,從而讓漏洞利用更有普適性。

注意,要想找到某些值,最好透過真實的裝置來獲取。libc.so和libicui8n.so模組,以及jemalloc配置都可以從ROM的系統映象中提取,而可以猜測可預測的heap spray地址-但是對於某些裝置來說,這不是最佳做法。

0x08 總結


這次研究證明了這個漏洞是可以被利用的。但是,首先要提前瞭解相關的裝置資訊,因為需要根據不同的ROM構建查詢表。

我們的漏洞在使用舊版ROM的Nexus 5上發揮最好。另外,我們還測試了HTC One,LG G3和Samsung S5,但是,在針對不同的廠商時,漏洞利用也稍有不同,需要作出一些修改。

很重要的一點是,這是一個遠端程式碼執行漏洞,可能還需要提升mediaserver程式的許可權,因為不同的廠商會賦予mediaserver不同的許可權(參考/init.rc)。

透過libicui8n.so模組實現利用時,利用時間最少幾秒鐘,最多2分鐘。

在下一章節,我們提出了一種更復雜的方法,這種方法能夠減少利用時間-差不多下降了4倍。

注意:

  • 23.5%的安卓裝置執行著Adroid 5.0-5.1-大約235,000,000 臺裝置
  • 4.0%的安卓裝置執行著沒有ASLR的Adroid 2.x-大約40,000,000 臺裝置

但是舊裝置上已經存在了大量的漏洞

透過觀察這些數字,很難確定有多少臺裝置是面臨威脅的。

資料來自:http://www.statista.com/statistics/271774/share­of­android­platforms­on­mobile­devices­with­android­os/

這些數字中包括了大量的Android平板,TV和手錶,但是,所有這些裝置上都存在這些漏洞。

0x09 彩蛋


提升Heap Spray的效果

在exploit-38226中,透過使用stbl atom來封裝spray資料,可以成倍加強heap spray的效果。在NCC Group文章中,也提到了進一步改善其效果的方法。利用這種方法,可以大幅縮小遠端程式碼執行漏洞的體積。

減少利用耗時

透過從ELF標頭中洩露不同的資訊,可以從很大程度上減少需要的洩露次數。除了洩露ELF標頭,我們可以從任意一個包含多個模組的記憶體區域中選擇任意一個地址來洩露。

下面的例子是一個728頁的記憶體區域,包含有24個ELF標頭,只有5個不可訪問的漏洞。

p66

(在不同裝置上,這些值也是不同的-這裡只是一個例子)

我們可以從這個區域中隨機選擇地址:

  • 只有5個漏洞-在解析每個媒體檔案時,只有0.69%的機率會導致崩潰
  • 我們能夠識別57頁-在解析每個媒體檔案時,有7.83%的機率能找到ASLR slide

對比來看,如果靠猜測的話,要想從256頁中猜到正確的ASLR,只有0.39%的機率。

在這種方法下,利用耗時在250毫秒和30秒之間,平均5到10秒,取決於標示符數量,不同的裝置,執行負擔,網路穩定性,最重要是嘗試的洩露次數。

0x0A 研究建議


這裡提到的資訊洩露方法無法應用在SBrowser-這一瀏覽器似乎能阻止響應型別是blob的XmlHttpRequest物件載入影片;我們不清楚這是一種安全機制,還是瀏覽器不支援這種功能。

你可以繞過NuCachedSource2::readInternal method CHECK_LE macro:

p67

透過x-cache-config HTTP 標頭來提供比較大的HighwaterThresholdBytes 值:

p68

要想利用Android 4.4.4上的洩露方法,需要研究DRM內容。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章