压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

CVE-2017-13253: 多個Android DRM服務中的緩沖區溢出

@tamir_zb最近披露了一個緩沖區溢出漏洞,影響到谷歌的多種Android DRM服務。 Google將其分類為高嚴重性,將其指定為CVE-2017-13253,并在3月份的安全更新中對其進行了修補。

在這篇博文中,我們將介紹該漏洞的詳細信息。 首先,我們將介紹相關的背景信息,從一般的Android機制到與漏洞相關的特定機制。 我們將重點介紹最近推出的Project Treble,以及它的變化究竟意味著什么。 然后,我們將分析這個漏洞及其影響。 我們將研究如何利用某些設備上的其他故障來解決此漏洞,以實現root權限。 最后,我們將討論該漏洞的起源以及它如何被阻止。 盡管Google聲稱Project Treble有利于安全性,但我們仍將看到它的反面。

Android的Binder和安全

在許多操作系統(包括Android)中使用的一種非常常見的安全模型圍繞進程間通信(IPC)展開。 在非特權進程中運行的不可信代碼可以與特權進程(服務)進行通信,并要求它們執行操作系統允許的特定操作。 該模型依賴于服務(和IPC機制本身)來正確驗證從非特權進程發送的每個輸入。 反過來,這意味著這些服務中的錯誤,尤其是輸入驗證部分中的錯誤很容易導致漏洞。

例如,Rani Idan在Zimperium發現的最新iOS漏洞依賴于這種方法。 IPC輸入驗證中的錯誤允許攻擊者從非特權應用程序執行具有更高權限的代碼。

在Android的情況下,IPC機制被稱為Binder。 Android Binder服務的安全性對于漏洞研究來說當然非常有趣。 Binder具有許多有用的功能,例如,它允許進程在彼此之間傳輸復雜對象,例如文件描述符或對其他Binder服務的引用。 為了保持簡單性和良好性能,Binder將每個事務限制為1MB的最大大小。 在進程需要傳輸大量數據的情況下,他們可以使用共享內存來快速共享數據。

Binder’s C++ 目錄

Android的Binder庫(libbinder)為依賴于Binder的C ++代碼提供了許多抽象。 它允許你調用C ++類的遠程實例的方法,就好像它們不駐留在另一個進程中一樣。

每個使用這種機制的對象都在預定義的結構中實現了幾個類:

  • 一個接口類,它定義了可以通過Binder調用的對象的方法。 以“I”為前綴。
  • 負責序列化輸入和反序列化輸出的“客戶端”類。 以“Bp”為前綴。
  • 負責反序列化輸入和序列化輸出的“服務器端”類。 前綴為“Bn”。

最終,在使用該對象時,幾乎總是使用接口類型。 這允許您以相同的方式處理對象,無論它處于同一個進程還是處于不同的進程中。

在ICrypto接口中使用libbinder的示例

代碼中的“服務器端”部分傳統上位于特權服務內部(盡管在某些情況下角色是相反的),所以它通常負責驗證輸入。 驗證代碼可以從Bn *類開始,并沿著隨后調用的方法繼續。 這顯然是脆弱性研究中最有趣的部分。

ICrypto接口和解密方法

一般來說,在介紹Binder之后,我們來看看與漏洞相關的具體實現。 mediadrmserver服務(毫無疑問,負責DRM媒體)提供了一個加密對象的接口,接口名為ICrypto。 請注意,該對象最近更改為CryptoHal,我們將在稍后討論。 此接口的一般用途是允許非特權應用程序解密需要較高權限解密的DRM數據,如訪問TEE。 加密本身的細節不在本篇博文的范圍之內,再一次,我們對輸入驗證更感興趣。

ICrypto有多種方法,但無疑最重要的方法是解密。

解密的簽名(來源)

解密簽名中最引人注目的事情之一是輸入的復雜程度。 從我們的角度來看,這非常有趣。 復雜的輸入會導致復雜的驗證代碼(每個參數都通過Binder進行傳輸并且需要驗證),這些代碼可能易受漏洞影響。

我們來看看一些參數:

參數 描述
模式 一個控制加密模式的枚舉。 其中一種模式是kMode_Unencrypted,它表示數據實際上未加密。 這種模式意味著數據只能從一個地方復制到另一個地方,而不涉及任何解密。 這使得這個過程更加簡單,所以從現在開始我們將專注于這個模式。 這也是我們不考慮一些加密相關參數(如密鑰或IV)的原因。
來源/目的地 輸入和輸出緩沖區。 由于數據的大小可能非常大(大于1MB),實際數據通過這些對象所代表的共享內存進行傳輸。
offset 偏移到數據開始的輸入緩沖區。
附屬樣本 子樣本數組,是有關輸入的元數據。 每個子采樣表示多個清零字節,后面跟著一些加密字節。 這使您可以在清除和加密的輸入數據之間切換。 使用kMode_Unencrypted也簡化了這一點,因為您只需使用一個代表所有清除數據的子樣本。

(有關API的更高級別Java一些參數的更多信息,請參閱MediaCodec.CryptoInfo)

現在讓我們仔細看看源和目標參數的類型:

(源)

這里的相關結構成員是mHeapSeqNum和兩個mSharedMemory成員(DestinationBuffer的其余部分是在目標未被存儲為共享內存的情況下,這種情況與此漏洞無關)。 名稱堆在這里用來指代實際的共享內存(這是你運行mmap的內容)。 mHeapSeqNum是一個像這樣的內存標識符,它以前使用稱為setHeap的ICrypto方法共享。 這兩個mSharedMemory成員僅表示堆內緩沖區的偏移量和大小。 這意味著雖然mHeapSeqNum在源結構內部,但它實際上與兩者都相關。

清除數據解密運行的參數示例

值得注意的是,參數結構的某些部分有點奇怪。 mSharedMemory是一個IMemory,它實際上連接到它自己的堆,并且應該表示內部的一個緩沖區,但是這個堆被忽略,偏移量和大小被用于mHeapSeqNum堆。 源結構中還存在mHeapSeqNum,但它與源和目標都有關。 這是所有這些代碼最近發生的變化的結果,這些代碼是作為名為Project Treble的Android框架的重要架構的一部分而創建的。

Treble項目

Project Treble是作為Android 8.0的一部分引入的; 其主要目標是通過在AOSP和供應商之間建立明確的分離來使系統更新更容易。 谷歌還聲稱,Project Treble通過增加更多的隔離功能來使Android安全性受益。

對于像mediadrmserver這樣的服務,Project Treble意味著分離成多個進程。 負責解密的代碼屬于供應商,因此它被分成多個供應商進程,稱為HAL,每個供應商都負責其自己的DRM方案。 mediadrm服務器的作用現在減少到在相關DRM方案的應用程序和HAL進程之間傳輸數據。 mediadrmserver和HAL之間的通信也在Binder之上,但是在不同的域中并使用不同庫的格式 – libhwbinder。 之前提到的從Crypto到CryptoHal的變化是因為現在它是一個不同的類,其唯一目的是將數據轉換為libhwbinder的格式并將其傳遞給HAL。

上圖顯示了Google為什么聲稱Project Treble受益于安全。 權限在不同的進程中分開(每個HAL只能與自己的驅動程序通信),不受信任的應用程序不再直接與高權限進程交互。

請注意,從Android 8.1開始,分離仍然是可選的,取決于供應商。 例如,在Nexus 5X中,HAL都位于mediadrmserver進程中。 數據仍然轉換為HAL格式,但不會轉移到其他進程。

加密插件

我之前提到了不同的DRM方案,在Android術語中,每種DRM方案的處理程序都稱為插件,或者在我們的特定情況下稱為加密插件。 供應商負責提供這些插件,但AOSP中有一些供銷售商使用的有用代碼。 例如,AOSP包含ClearKey DRM方案插件的完整開源實現。 通常,設備將具有開源的ClearKey插件和閉源的Widevine插件(例如Nexus / Pixel設備就是這種情況)。

上述Project Treble變化的問題是現在插件接收HAL格式的數據。 為了簡化轉換,無需更新每個插件以支持這種新格式,默認的Crypto Plugin實現已添加到AOSP供供應商使用。 該實現將數據從HAL格式轉換為傳統格式,并將其傳遞給原始插件代碼。 理想情況下,這個解決方案應該只是暫時的,直到插件更新,否則我們會留下冗余格式轉換(往返于HAL)。

數據格式轉換的流程

研究源代碼

在介紹ICrypto的解密方法的一般過程之后,我們來仔細看看共享內存緩沖區的驗證代碼。 正如您可能已經猜到的(因為我們正在談論緩沖區溢出),這是發現漏洞的地方。

如前所述,驗證通常從Bn *類開始,在我們的例子中就是ICrypto接口的“服務器端”BnCrypto。

BnCrypto驗證代碼的一部分(源)

  • 首先,代碼檢查子采樣大小的總和是否有效并且不會溢出。 請記住,這是要復制的數據的大小。
  • 它還檢查這個總和是否與totalSize匹配,通過Binder傳遞的另一個參數非常多余(您可以通過子樣本的總和來告訴總大小,代碼明確驗證了這種情況)。
  • 接下來的檢查是數據大小不超過源緩沖區的大小。
  • 最后,它檢查數據大小加上偏移量仍然不超過源緩沖區。

Crypto Hal將數據轉換為HAL格式并將其發送給相關插件; 這里沒有有趣的驗證代碼。

接下來,默認的Crypto Plugin實現(可能會或可能不在不同的進程中)將數據轉換回傳統格式并繼續驗證它。

部分默認加密插件驗證碼(源)

關于這個代碼的一個附注:我覺得它有點混亂。 有多個“dest”和“source”變量,sourceBase和destBase實際上是完全相同的東西(堆),并且根本沒有任何評論可以幫助你。 正如我之前提到的那樣,這部分是全新的,并且僅在Android 8.0中添加,因此它是有道理的。 盡管如此,我還是懷疑這種混亂導致了這個漏洞,因為它使得查看整個驗證代碼和查看是否有缺失更加困難。

  • 這里的第一個檢查是偏移量和緩沖區大小的總和不超過堆大小。 sourceBase是堆,而源是之前的source.mSharedMemory。 如果您對兩個偏移量感到困惑,請記住mSharedMemory包含一個偏移量,并且解密方法也有一個不同的偏移量參數。
  • 其他檢查類似,但在目標緩沖區上執行。 destBuffer是destination.mSharedMemory和destBase與sourceBase相同的堆。 這次不涉及偏移量參數。

最終,每個緩沖區都簡化為一個指向內存的指針; 偏移量現在是指針的一部分,而緩沖區大小被省略。 為了確定數據大小,插件使用subSamples數組。

數據未加密時的ClearKey插件代碼(源代碼)

上面的代碼顯示了最后一部分,以幫助理解流程。 如前所述,當數據未加密時,它只是從一個地方復制到另一個地方。

到目前為止,我已經提供了足夠的信息,可以在理論上發現漏洞。 如果你想嘗試做到這一點,歡迎回去繼續閱讀代碼。 根據我的經驗,在這類博客文章中很難提供足夠的信息來發現它,同時仍然保持實際的挑戰性(尤其是從已經找到它的人的角度來看),所以即使你 無法發現它(或者它可能太簡單了?)。

該漏洞

問題是沒有驗證被復制的數據量沒有超過目標緩沖區。 對源緩沖區只有一個類似的檢查(BnCrypto的第三個檢查檢查并且下一個檢查甚至將額外的偏移量考慮在內)。 與目標緩沖區相關的唯一檢查是默認Crypto Plugin的第二次檢查(它確保緩沖區位于堆內并且不超過它),但這僅僅是不夠的。

我們來看一個例子。 假設要復制的數據的大小是0x1000。 由于這個大小是由subsamples數組表示的,所以我們將在該數組中有一個條目,其中包含0x1000個清晰字節(以及0個加密字節)。 堆也將有0x1000字節,并且源緩沖區將指向整個堆(偏移量= 0,大小= 0x1000)。 目標緩沖區是它變得有趣的地方。 假設偏移量是0x800,大小是0x800。 這仍然適合堆,所以它通過了默認加密插件的檢查。 在這種情況下,會出現溢出; 0x800字節將在堆后寫入。

概念驗證

觸發漏洞的示例的代碼

注意:MemoryBase對象是IMemory libbinder接口的實現。 這是一個使用Binder將引用傳遞給其他Binder對象的例子。 這也是Binder角色顛倒的一個例子。 特權流程是“客戶端”,因此它通過Binder請求信息并負責驗證它。

漏洞的影響

此漏洞允許攻擊者用任意數據覆蓋目標進程中的內存。 由于這是內存頁級別的溢出,因此目前沒有任何緩解措施可以阻止它(例如堆棧溢出堆棧)。 由于缺省Crypto Plugin的檢查,數據必須從共享內存開始,這仍然受到限制。 這意味著只有位于共享內存之后的內存才能被覆蓋。 此外,內存中的許多區域通常是未分配或不可寫入的,因此試圖在其中寫入將導致分段錯誤。

受影響的流程取決于供應商的實施。 如果供應商不將HAL分成不同的進程,則mediadrmserver會受到影響。 如果供應商將它們分開,那么Crypto Plugin的每個HAL服務都會受到影響。 由于默認的Crypto Plugin代碼僅留下指向目標緩沖區的指針,并且大小僅由子采樣確定,供應商代碼無法確定它接收到格式錯誤的數據。 這意味著供應商部分編寫得并不重要,它仍然是脆弱的(理論上,供應商可能會忽略AOSP的默認加密插件代碼,并實現自己的代碼來檢測格式錯誤的數據,但我沒有’ 沒有看到供應商那么做)。

可能的影響

假設攻擊者設法利用此漏洞將特權提升為易受攻擊服務的特權,那么我們來看看他們可以實現的功能。 請注意,這部分大多是推測性的。 我沒有編寫漏洞利用表,但是我對這個漏洞理論上如何被用來達到完全的root權限有一些想法。

這就是Android的SELinux規則發揮作用的地方; 即使易受攻擊的服務擁有更多權限,SELinux仍然會嚴重限制它們。 盡管如此,即使在限制之后,我們仍然留下了一個非常有趣的權限:完全訪問TEE設備。

在這種情況下,Project Treble的額外隔離幾乎沒有幫助。 易受攻擊的進程將是可以訪問TEE設備的進程,無論是否存在分離到多個進程。 在分離的情況下,唯一受保護的過程是中間沒有趣味的媒體服務器。

那么你可以通過完全訪問TEE來做什么? Gal Beniamini的優秀研究表明,許多設備無法正確吊銷舊的易受攻擊的TEE信托。 這意味著,如果您攻擊具有舊的易受攻擊的trustlet的設備,則可以使用TEE設備的訪問權限,加載trustlet并將其用于TEE上的代碼執行。 更重要的是,Gal Benimaini過去也展示了基于Qualcomm的設備上的TEE代碼執行如何導致root權限。

可能的攻擊流向根特權

漏洞的來源

我已經多次提到Project Treble如何對代碼的這個區域進行重大修改。 如果知道這些更改實際上引入了此漏洞(在更改之前,目標緩沖區甚至無法以此格式設置),那么您可能不會感到驚訝。

顯然,你不能僅僅因為使代碼易受攻擊而對其進行重構,因為這意味著代碼重構不應該發生,這是不正確的。 正如我已經指出的那樣,這段代碼的多個部分都是混亂的或冗余的。 雖然這本身并不一定會使代碼易受攻擊,但確實增加了這種可能性,因為它使代碼更難以復審(代碼的某些部分花了我相當長的時間才能理解,而相比之下它們實際上的復雜性 做)。 因此,雖然漏洞有時難以發現,但通常更容易發現雜亂或冗余的代碼。 我知道從評論者的角度來看批評不好的代碼設計比實際編寫好的代碼更容易,但我仍然認為應該改進一些部分。

結論

Google聲稱Project Treble對Android的安全性有好處,但在這個例子中,它卻反其道而行之。 高音項目本身并不一定是壞的,這里的關鍵問題是實施處理得不好。

可以在GitHub(https://github.com/tamirzb/CVE-2017-13253)上找到觸發漏洞的PoC的完整源代碼以及一些額外信息。

原文:https://blog.zimperium.com/cve-2017-13253-buffer-overflow-multiple-android-drm-services/

上一篇:MySQL UDF開發

下一篇:如何在未Root的Android設備上解密數據庫