@tamir_zb最近披露了一個(gè)緩沖區(qū)溢出漏洞,影響到谷歌的多種Android DRM服務(wù)。 Google將其分類為高嚴(yán)重性,將其指定為CVE-2017-13253,并在3月份的安全更新中對(duì)其進(jìn)行了修補(bǔ)。
在這篇博文中,我們將介紹該漏洞的詳細(xì)信息。 首先,我們將介紹相關(guān)的背景信息,從一般的Android機(jī)制到與漏洞相關(guān)的特定機(jī)制。 我們將重點(diǎn)介紹最近推出的Project Treble,以及它的變化究竟意味著什么。 然后,我們將分析這個(gè)漏洞及其影響。 我們將研究如何利用某些設(shè)備上的其他故障來(lái)解決此漏洞,以實(shí)現(xiàn)root權(quán)限。 最后,我們將討論該漏洞的起源以及它如何被阻止。 盡管Google聲稱Project Treble有利于安全性,但我們?nèi)詫⒖吹剿姆疵妗?/span>
Android的Binder和安全
在許多操作系統(tǒng)(包括Android)中使用的一種非常常見(jiàn)的安全模型圍繞進(jìn)程間通信(IPC)展開(kāi)。 在非特權(quán)進(jìn)程中運(yùn)行的不可信代碼可以與特權(quán)進(jìn)程(服務(wù))進(jìn)行通信,并要求它們執(zhí)行操作系統(tǒng)允許的特定操作。 該模型依賴于服務(wù)(和IPC機(jī)制本身)來(lái)正確驗(yàn)證從非特權(quán)進(jìn)程發(fā)送的每個(gè)輸入。 反過(guò)來(lái),這意味著這些服務(wù)中的錯(cuò)誤,尤其是輸入驗(yàn)證部分中的錯(cuò)誤很容易導(dǎo)致漏洞。
例如,Rani Idan在Zimperium發(fā)現(xiàn)的最新iOS漏洞依賴于這種方法。 IPC輸入驗(yàn)證中的錯(cuò)誤允許攻擊者從非特權(quán)應(yīng)用程序執(zhí)行具有更高權(quán)限的代碼。
在Android的情況下,IPC機(jī)制被稱為Binder。 Android Binder服務(wù)的安全性對(duì)于漏洞研究來(lái)說(shuō)當(dāng)然非常有趣。 Binder具有許多有用的功能,例如,它允許進(jìn)程在彼此之間傳輸復(fù)雜對(duì)象,例如文件描述符或?qū)ζ渌鸅inder服務(wù)的引用。 為了保持簡(jiǎn)單性和良好性能,Binder將每個(gè)事務(wù)限制為1MB的最大大小。 在進(jìn)程需要傳輸大量數(shù)據(jù)的情況下,他們可以使用共享內(nèi)存來(lái)快速共享數(shù)據(jù)。
Binder’s C++ 目錄
Android的Binder庫(kù)(libbinder)為依賴于Binder的C ++代碼提供了許多抽象。 它允許你調(diào)用C ++類的遠(yuǎn)程實(shí)例的方法,就好像它們不駐留在另一個(gè)進(jìn)程中一樣。
每個(gè)使用這種機(jī)制的對(duì)象都在預(yù)定義的結(jié)構(gòu)中實(shí)現(xiàn)了幾個(gè)類:
最終,在使用該對(duì)象時(shí),幾乎總是使用接口類型。 這允許您以相同的方式處理對(duì)象,無(wú)論它處于同一個(gè)進(jìn)程還是處于不同的進(jìn)程中。
在ICrypto接口中使用libbinder的示例
代碼中的“服務(wù)器端”部分傳統(tǒng)上位于特權(quán)服務(wù)內(nèi)部(盡管在某些情況下角色是相反的),所以它通常負(fù)責(zé)驗(yàn)證輸入。 驗(yàn)證代碼可以從Bn *類開(kāi)始,并沿著隨后調(diào)用的方法繼續(xù)。 這顯然是脆弱性研究中最有趣的部分。
ICrypto接口和解密方法
一般來(lái)說(shuō),在介紹Binder之后,我們來(lái)看看與漏洞相關(guān)的具體實(shí)現(xiàn)。 mediadrmserver服務(wù)(毫無(wú)疑問(wèn),負(fù)責(zé)DRM媒體)提供了一個(gè)加密對(duì)象的接口,接口名為ICrypto。 請(qǐng)注意,該對(duì)象最近更改為CryptoHal,我們將在稍后討論。 此接口的一般用途是允許非特權(quán)應(yīng)用程序解密需要較高權(quán)限解密的DRM數(shù)據(jù),如訪問(wèn)TEE。 加密本身的細(xì)節(jié)不在本篇博文的范圍之內(nèi),再一次,我們對(duì)輸入驗(yàn)證更感興趣。
ICrypto有多種方法,但無(wú)疑最重要的方法是解密。
解密的簽名(來(lái)源)
解密簽名中最引人注目的事情之一是輸入的復(fù)雜程度。 從我們的角度來(lái)看,這非常有趣。 復(fù)雜的輸入會(huì)導(dǎo)致復(fù)雜的驗(yàn)證代碼(每個(gè)參數(shù)都通過(guò)Binder進(jìn)行傳輸并且需要驗(yàn)證),這些代碼可能易受漏洞影響。
我們來(lái)看看一些參數(shù):
參數(shù) | 描述 |
---|---|
模式 | 一個(gè)控制加密模式的枚舉。 其中一種模式是kMode_Unencrypted,它表示數(shù)據(jù)實(shí)際上未加密。 這種模式意味著數(shù)據(jù)只能從一個(gè)地方復(fù)制到另一個(gè)地方,而不涉及任何解密。 這使得這個(gè)過(guò)程更加簡(jiǎn)單,所以從現(xiàn)在開(kāi)始我們將專注于這個(gè)模式。 這也是我們不考慮一些加密相關(guān)參數(shù)(如密鑰或IV)的原因。 |
來(lái)源/目的地 | 輸入和輸出緩沖區(qū)。 由于數(shù)據(jù)的大小可能非常大(大于1MB),實(shí)際數(shù)據(jù)通過(guò)這些對(duì)象所代表的共享內(nèi)存進(jìn)行傳輸。 |
offset | 偏移到數(shù)據(jù)開(kāi)始的輸入緩沖區(qū)。 |
附屬樣本 | 子樣本數(shù)組,是有關(guān)輸入的元數(shù)據(jù)。 每個(gè)子采樣表示多個(gè)清零字節(jié),后面跟著一些加密字節(jié)。 這使您可以在清除和加密的輸入數(shù)據(jù)之間切換。 使用kMode_Unencrypted也簡(jiǎn)化了這一點(diǎn),因?yàn)槟恍枋褂靡粋€(gè)代表所有清除數(shù)據(jù)的子樣本。 |
(有關(guān)API的更高級(jí)別Java一些參數(shù)的更多信息,請(qǐng)參閱MediaCodec.CryptoInfo)
現(xiàn)在讓我們仔細(xì)看看源和目標(biāo)參數(shù)的類型:
(源)
這里的相關(guān)結(jié)構(gòu)成員是mHeapSeqNum和兩個(gè)mSharedMemory成員(DestinationBuffer的其余部分是在目標(biāo)未被存儲(chǔ)為共享內(nèi)存的情況下,這種情況與此漏洞無(wú)關(guān))。 名稱堆在這里用來(lái)指代實(shí)際的共享內(nèi)存(這是你運(yùn)行mmap的內(nèi)容)。 mHeapSeqNum是一個(gè)像這樣的內(nèi)存標(biāo)識(shí)符,它以前使用稱為setHeap的ICrypto方法共享。 這兩個(gè)mSharedMemory成員僅表示堆內(nèi)緩沖區(qū)的偏移量和大小。 這意味著雖然mHeapSeqNum在源結(jié)構(gòu)內(nèi)部,但它實(shí)際上與兩者都相關(guān)。
清除數(shù)據(jù)解密運(yùn)行的參數(shù)示例
值得注意的是,參數(shù)結(jié)構(gòu)的某些部分有點(diǎn)奇怪。 mSharedMemory是一個(gè)IMemory,它實(shí)際上連接到它自己的堆,并且應(yīng)該表示內(nèi)部的一個(gè)緩沖區(qū),但是這個(gè)堆被忽略,偏移量和大小被用于mHeapSeqNum堆。 源結(jié)構(gòu)中還存在mHeapSeqNum,但它與源和目標(biāo)都有關(guān)。 這是所有這些代碼最近發(fā)生的變化的結(jié)果,這些代碼是作為名為Project Treble的Android框架的重要架構(gòu)的一部分而創(chuàng)建的。
Treble項(xiàng)目
Project Treble是作為Android 8.0的一部分引入的; 其主要目標(biāo)是通過(guò)在AOSP和供應(yīng)商之間建立明確的分離來(lái)使系統(tǒng)更新更容易。 谷歌還聲稱,Project Treble通過(guò)增加更多的隔離功能來(lái)使Android安全性受益。
對(duì)于像mediadrmserver這樣的服務(wù),Project Treble意味著分離成多個(gè)進(jìn)程。 負(fù)責(zé)解密的代碼屬于供應(yīng)商,因此它被分成多個(gè)供應(yīng)商進(jìn)程,稱為HAL,每個(gè)供應(yīng)商都負(fù)責(zé)其自己的DRM方案。 mediadrm服務(wù)器的作用現(xiàn)在減少到在相關(guān)DRM方案的應(yīng)用程序和HAL進(jìn)程之間傳輸數(shù)據(jù)。 mediadrmserver和HAL之間的通信也在Binder之上,但是在不同的域中并使用不同庫(kù)的格式 – libhwbinder。 之前提到的從Crypto到CryptoHal的變化是因?yàn)楝F(xiàn)在它是一個(gè)不同的類,其唯一目的是將數(shù)據(jù)轉(zhuǎn)換為libhwbinder的格式并將其傳遞給HAL。
上圖顯示了Google為什么聲稱Project Treble受益于安全。 權(quán)限在不同的進(jìn)程中分開(kāi)(每個(gè)HAL只能與自己的驅(qū)動(dòng)程序通信),不受信任的應(yīng)用程序不再直接與高權(quán)限進(jìn)程交互。
請(qǐng)注意,從Android 8.1開(kāi)始,分離仍然是可選的,取決于供應(yīng)商。 例如,在Nexus 5X中,HAL都位于mediadrmserver進(jìn)程中。 數(shù)據(jù)仍然轉(zhuǎn)換為HAL格式,但不會(huì)轉(zhuǎn)移到其他進(jìn)程。
加密插件
我之前提到了不同的DRM方案,在Android術(shù)語(yǔ)中,每種DRM方案的處理程序都稱為插件,或者在我們的特定情況下稱為加密插件。 供應(yīng)商負(fù)責(zé)提供這些插件,但AOSP中有一些供銷售商使用的有用代碼。 例如,AOSP包含ClearKey DRM方案插件的完整開(kāi)源實(shí)現(xiàn)。 通常,設(shè)備將具有開(kāi)源的ClearKey插件和閉源的Widevine插件(例如Nexus / Pixel設(shè)備就是這種情況)。
上述Project Treble變化的問(wèn)題是現(xiàn)在插件接收HAL格式的數(shù)據(jù)。 為了簡(jiǎn)化轉(zhuǎn)換,無(wú)需更新每個(gè)插件以支持這種新格式,默認(rèn)的Crypto Plugin實(shí)現(xiàn)已添加到AOSP供供應(yīng)商使用。 該實(shí)現(xiàn)將數(shù)據(jù)從HAL格式轉(zhuǎn)換為傳統(tǒng)格式,并將其傳遞給原始插件代碼。 理想情況下,這個(gè)解決方案應(yīng)該只是暫時(shí)的,直到插件更新,否則我們會(huì)留下冗余格式轉(zhuǎn)換(往返于HAL)。
數(shù)據(jù)格式轉(zhuǎn)換的流程
研究源代碼
在介紹ICrypto的解密方法的一般過(guò)程之后,我們來(lái)仔細(xì)看看共享內(nèi)存緩沖區(qū)的驗(yàn)證代碼。 正如您可能已經(jīng)猜到的(因?yàn)槲覀冋谡務(wù)摼彌_區(qū)溢出),這是發(fā)現(xiàn)漏洞的地方。
如前所述,驗(yàn)證通常從Bn *類開(kāi)始,在我們的例子中就是ICrypto接口的“服務(wù)器端”BnCrypto。
BnCrypto驗(yàn)證代碼的一部分(源)
Crypto Hal將數(shù)據(jù)轉(zhuǎn)換為HAL格式并將其發(fā)送給相關(guān)插件; 這里沒(méi)有有趣的驗(yàn)證代碼。
接下來(lái),默認(rèn)的Crypto Plugin實(shí)現(xiàn)(可能會(huì)或可能不在不同的進(jìn)程中)將數(shù)據(jù)轉(zhuǎn)換回傳統(tǒng)格式并繼續(xù)驗(yàn)證它。
部分默認(rèn)加密插件驗(yàn)證碼(源)
關(guān)于這個(gè)代碼的一個(gè)附注:我覺(jué)得它有點(diǎn)混亂。 有多個(gè)“dest”和“source”變量,sourceBase和destBase實(shí)際上是完全相同的東西(堆),并且根本沒(méi)有任何評(píng)論可以幫助你。 正如我之前提到的那樣,這部分是全新的,并且僅在Android 8.0中添加,因此它是有道理的。 盡管如此,我還是懷疑這種混亂導(dǎo)致了這個(gè)漏洞,因?yàn)樗沟貌榭凑麄€(gè)驗(yàn)證代碼和查看是否有缺失更加困難。
最終,每個(gè)緩沖區(qū)都簡(jiǎn)化為一個(gè)指向內(nèi)存的指針; 偏移量現(xiàn)在是指針的一部分,而緩沖區(qū)大小被省略。 為了確定數(shù)據(jù)大小,插件使用subSamples數(shù)組。
數(shù)據(jù)未加密時(shí)的ClearKey插件代碼(源代碼)
上面的代碼顯示了最后一部分,以幫助理解流程。 如前所述,當(dāng)數(shù)據(jù)未加密時(shí),它只是從一個(gè)地方復(fù)制到另一個(gè)地方。
到目前為止,我已經(jīng)提供了足夠的信息,可以在理論上發(fā)現(xiàn)漏洞。 如果你想嘗試做到這一點(diǎn),歡迎回去繼續(xù)閱讀代碼。 根據(jù)我的經(jīng)驗(yàn),在這類博客文章中很難提供足夠的信息來(lái)發(fā)現(xiàn)它,同時(shí)仍然保持實(shí)際的挑戰(zhàn)性(尤其是從已經(jīng)找到它的人的角度來(lái)看),所以即使你 無(wú)法發(fā)現(xiàn)它(或者它可能太簡(jiǎn)單了?)。
該漏洞
問(wèn)題是沒(méi)有驗(yàn)證被復(fù)制的數(shù)據(jù)量沒(méi)有超過(guò)目標(biāo)緩沖區(qū)。 對(duì)源緩沖區(qū)只有一個(gè)類似的檢查(BnCrypto的第三個(gè)檢查檢查并且下一個(gè)檢查甚至將額外的偏移量考慮在內(nèi))。 與目標(biāo)緩沖區(qū)相關(guān)的唯一檢查是默認(rèn)Crypto Plugin的第二次檢查(它確保緩沖區(qū)位于堆內(nèi)并且不超過(guò)它),但這僅僅是不夠的。
我們來(lái)看一個(gè)例子。 假設(shè)要復(fù)制的數(shù)據(jù)的大小是0x1000。 由于這個(gè)大小是由subsamples數(shù)組表示的,所以我們將在該數(shù)組中有一個(gè)條目,其中包含0x1000個(gè)清晰字節(jié)(以及0個(gè)加密字節(jié))。 堆也將有0x1000字節(jié),并且源緩沖區(qū)將指向整個(gè)堆(偏移量= 0,大小= 0x1000)。 目標(biāo)緩沖區(qū)是它變得有趣的地方。 假設(shè)偏移量是0x800,大小是0x800。 這仍然適合堆,所以它通過(guò)了默認(rèn)加密插件的檢查。 在這種情況下,會(huì)出現(xiàn)溢出; 0x800字節(jié)將在堆后寫(xiě)入。
概念驗(yàn)證
觸發(fā)漏洞的示例的代碼
注意:MemoryBase對(duì)象是IMemory libbinder接口的實(shí)現(xiàn)。 這是一個(gè)使用Binder將引用傳遞給其他Binder對(duì)象的例子。 這也是Binder角色顛倒的一個(gè)例子。 特權(quán)流程是“客戶端”,因此它通過(guò)Binder請(qǐng)求信息并負(fù)責(zé)驗(yàn)證它。
漏洞的影響
此漏洞允許攻擊者用任意數(shù)據(jù)覆蓋目標(biāo)進(jìn)程中的內(nèi)存。 由于這是內(nèi)存頁(yè)級(jí)別的溢出,因此目前沒(méi)有任何緩解措施可以阻止它(例如堆棧溢出堆棧)。 由于缺省Crypto Plugin的檢查,數(shù)據(jù)必須從共享內(nèi)存開(kāi)始,這仍然受到限制。 這意味著只有位于共享內(nèi)存之后的內(nèi)存才能被覆蓋。 此外,內(nèi)存中的許多區(qū)域通常是未分配或不可寫(xiě)入的,因此試圖在其中寫(xiě)入將導(dǎo)致分段錯(cuò)誤。
受影響的流程取決于供應(yīng)商的實(shí)施。 如果供應(yīng)商不將HAL分成不同的進(jìn)程,則mediadrmserver會(huì)受到影響。 如果供應(yīng)商將它們分開(kāi),那么Crypto Plugin的每個(gè)HAL服務(wù)都會(huì)受到影響。 由于默認(rèn)的Crypto Plugin代碼僅留下指向目標(biāo)緩沖區(qū)的指針,并且大小僅由子采樣確定,供應(yīng)商代碼無(wú)法確定它接收到格式錯(cuò)誤的數(shù)據(jù)。 這意味著供應(yīng)商部分編寫(xiě)得并不重要,它仍然是脆弱的(理論上,供應(yīng)商可能會(huì)忽略AOSP的默認(rèn)加密插件代碼,并實(shí)現(xiàn)自己的代碼來(lái)檢測(cè)格式錯(cuò)誤的數(shù)據(jù),但我沒(méi)有’ 沒(méi)有看到供應(yīng)商那么做)。
可能的影響
假設(shè)攻擊者設(shè)法利用此漏洞將特權(quán)提升為易受攻擊服務(wù)的特權(quán),那么我們來(lái)看看他們可以實(shí)現(xiàn)的功能。 請(qǐng)注意,這部分大多是推測(cè)性的。 我沒(méi)有編寫(xiě)漏洞利用表,但是我對(duì)這個(gè)漏洞理論上如何被用來(lái)達(dá)到完全的root權(quán)限有一些想法。
這就是Android的SELinux規(guī)則發(fā)揮作用的地方; 即使易受攻擊的服務(wù)擁有更多權(quán)限,SELinux仍然會(huì)嚴(yán)重限制它們。 盡管如此,即使在限制之后,我們?nèi)匀涣粝铝艘粋€(gè)非常有趣的權(quán)限:完全訪問(wèn)TEE設(shè)備。
在這種情況下,Project Treble的額外隔離幾乎沒(méi)有幫助。 易受攻擊的進(jìn)程將是可以訪問(wèn)TEE設(shè)備的進(jìn)程,無(wú)論是否存在分離到多個(gè)進(jìn)程。 在分離的情況下,唯一受保護(hù)的過(guò)程是中間沒(méi)有趣味的媒體服務(wù)器。
那么你可以通過(guò)完全訪問(wèn)TEE來(lái)做什么? Gal Beniamini的優(yōu)秀研究表明,許多設(shè)備無(wú)法正確吊銷舊的易受攻擊的TEE信托。 這意味著,如果您攻擊具有舊的易受攻擊的trustlet的設(shè)備,則可以使用TEE設(shè)備的訪問(wèn)權(quán)限,加載trustlet并將其用于TEE上的代碼執(zhí)行。 更重要的是,Gal Benimaini過(guò)去也展示了基于Qualcomm的設(shè)備上的TEE代碼執(zhí)行如何導(dǎo)致root權(quán)限。
可能的攻擊流向根特權(quán)
漏洞的來(lái)源
我已經(jīng)多次提到Project Treble如何對(duì)代碼的這個(gè)區(qū)域進(jìn)行重大修改。 如果知道這些更改實(shí)際上引入了此漏洞(在更改之前,目標(biāo)緩沖區(qū)甚至無(wú)法以此格式設(shè)置),那么您可能不會(huì)感到驚訝。
顯然,你不能僅僅因?yàn)槭勾a易受攻擊而對(duì)其進(jìn)行重構(gòu),因?yàn)檫@意味著代碼重構(gòu)不應(yīng)該發(fā)生,這是不正確的。 正如我已經(jīng)指出的那樣,這段代碼的多個(gè)部分都是混亂的或冗余的。 雖然這本身并不一定會(huì)使代碼易受攻擊,但確實(shí)增加了這種可能性,因?yàn)樗勾a更難以復(fù)審(代碼的某些部分花了我相當(dāng)長(zhǎng)的時(shí)間才能理解,而相比之下它們實(shí)際上的復(fù)雜性 做)。 因此,雖然漏洞有時(shí)難以發(fā)現(xiàn),但通常更容易發(fā)現(xiàn)雜亂或冗余的代碼。 我知道從評(píng)論者的角度來(lái)看批評(píng)不好的代碼設(shè)計(jì)比實(shí)際編寫(xiě)好的代碼更容易,但我仍然認(rèn)為應(yīng)該改進(jìn)一些部分。
結(jié)論
Google聲稱Project Treble對(duì)Android的安全性有好處,但在這個(gè)例子中,它卻反其道而行之。 高音項(xiàng)目本身并不一定是壞的,這里的關(guān)鍵問(wèn)題是實(shí)施處理得不好。
可以在GitHub(https://github.com/tamirzb/CVE-2017-13253)上找到觸發(fā)漏洞的PoC的完整源代碼以及一些額外信息。
原文:https://blog.zimperium.com/cve-2017-13253-buffer-overflow-multiple-android-drm-services/