PendingIntent重定向:一種針對安卓系統和流行App的通用提權方法——BlackHat EU 2021議題詳解 (下)

OPPO數智技術發表於2022-03-18

以使用者隱私安全為中心,用責任兌付信任,OPPO成立子午網際網路安全實驗室(ZIWU Cyber Security Lab)。實驗室以“保護使用者的安全與隱私,為品牌注入安全基因”為使命,持續關注併發力於業務安全、紅藍對抗、IoT安全、Android安全、資料和隱私保護等領域。

本篇文章源自OPPO子午網際網路安全實驗室。

1 不安全PendingIntent的通用利用方法

1.1 不安全PendingIntent的特徵

至此,我們已經解決了本議題的第一個問題,經過研究表明,Android系統中使用的PendingIntent大都 可以被三方App獲取。

獲取方式包括bind SliceProvider、監聽通知、連線媒體瀏覽器服務或者bind容納 視窗小部件的AppWidgetsProvider。

於是,引入議題研究的第二個關鍵問題:如果這些PendigIntent不安全,如何利用才能造成安全危害?

首先,我們需要辨別什麼樣的PendingIntent是不安全的。前面描述的公開漏洞案例,均為劫持base Intent為空Intent的廣播PendingIntent,說明如下empty base Intent構建的PendingIntent確定存在安全問題。

Android 12之前的開發者文件也對base Intent為隱式Intent的PendingIntent提出了安全警告,但卻沒 有明確告知到底存在何種危害。而且在AOSP程式碼和流行App當中,如下的程式碼模式廣泛存在。這不禁讓 我們思索, Implicit base Intent構建的PendingIntent是否真正存在問題?唯有找到一種確定的針對這 種PendingIntent的漏洞利用方法,才能真正證明安全問題的存在。

1.2 深入Intent fillIn改寫機制

尋找利用方法之前,需要深入探索PendingIntent的改寫機制,這決定了其他App獲得PendingIntent以 後,如何對base Intent進行改寫。這個機制由 Intent.fillIn 函式提供:


在上述程式碼中,this物件指向當前Intent,other為其他Intent。如果當前Intent中的成員變數為空,則可 以被other中相應的成員變數覆蓋。比較特殊的是Intent中的component和selector成員,即使當前Intent中的component和selector為空,也不能被other所改寫,除非PendingIntent
設定了FILL_IN_COMPONENT
或者FILL_IN_SELECTOR標誌。

1.3 PendingIntent重定向攻擊

因此在獲取PendingIntent之後,其base Intent的action、category、data、clidpdata、package、flag、extra等成員都是有可能改寫的,而component和selector無法改寫,如圖所示。特別地,對於base Intent為隱式Intent的這種情況,action已經被設定了,因此也無法被改寫,攻擊者無法如前面安 卓系統broadcastAnyWhere漏洞那樣,通過劫持PendingIntent、在base Intent中重新新增action,隱式開啟一個受保護的元件。

圖 Intent成員

這裡就來到了問題解決的關鍵點,由於package可以指定,回想到以前在Intent Bridge漏洞中的利用方 法,我們可以通過設定intent中的flag來巧妙地解決這個問題。Intent提供了有關臨時授權的標誌:

  • FLAG_GRANT_READ_URI_PERMISSION:Intent攜帶此標誌時,Intent的接收者將獲得Intent所攜 帶data URI以及clipdata URI中的讀許可權
  • FLAG_GRANT_WRITE_URI_PERMISSION:Intent攜帶此標誌時,Intent的接收者將獲得Intent所攜 帶data URI以及clipdata URI中的寫許可權

簡言之,惡意App對PendingIntent進行了指向惡意App自己的重定向,通過對PendingIntent base Intent的部分修改(修改包名、授權標誌和data/clipdata),使其以受害App的許可權開啟惡意App自身, 這樣惡意App在被開啟的瞬間即獲得對受害App私有資料的讀寫許可權。具體的利用方法如圖所示:

圖 PendingIntent重定向攻擊

步驟如下:
1、受害App通過getActivity構建PendingIntent,在通知、SliceProvider、視窗小部件中使用,假定其base Intent為隱式Intent;

2、攻擊App 通過前面探討的各種渠道獲取受害App的PendingIntent;

3、攻擊App修改PendingIntent中的base Intent,由於是隱式Intent,因此action、component和selector都不能修改。但可以做如下修改:

  • 修改data或者clidpdata,使其URI指向受害App的私有ContentProvider;
  • 修改package,指向攻擊App;
  • 新增FLAG_GRANT_READ_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSION 標誌。

同時攻擊App宣告一個Activity支援隱式啟動,其Intent-filter與base Intent中的action一致。

4、攻擊App呼叫PendingIntent.send;

5、由於這個PendingIntent代表了受害App的身份和許可權,因此將以受害App的名義傳送修改後的base Intent,開啟攻擊App的Activity;

6、在攻擊App Activity被開啟的瞬間,即被授權訪問base Intent中攜帶的URI,也就獲得了對受害App私有ContentProvider的讀寫許可權。

上面受害App的私有ContentProvider,需要攜帶屬性 grantUriPermission=true ,不限於受害App自 己的ContentProvider,也包括受害App有許可權訪問的ContentProvider。手機上一個常⻅的具有 grantUriPermission=true 屬性的ContentProvider就是代表通訊錄的Contacts Provider,只要受 害App具有 READ_CONTACTS 許可權,出現這樣一個PendingIntent漏洞後將導致通訊錄洩露。

這樣,我們通過上述6個步驟,就可以成功實現對隱式Intent構建PendingIntent的漏洞利用,讀寫受害App的私有資料,這也就解決了本研究提出的第二個關鍵問題:通過隱式Intent構建的PendingIntent可遭受通用的重定向提權攻擊,也是不安全的。

由於這裡使用了grantUri的技巧,因此並不適用於broadcast PendingIntent,因為廣播接收器是不可以 被grantUri的。另外,從Android 5.0以後,Service不能隱式啟動,因此也很難看到base Intent為隱式Intent的service PendingIntent。所以,這裡的PendingIntent重定向攻擊主要適用於Activity PendingIntent。

2 安卓系統中的真實案例

令人驚訝的是,在Android 12之前的AOSP程式碼以及流行App中,隱式Intent構建的ActivityPendingIntent廣泛存在,以下是我們發現的典型案例,可能導致手機的敏感資訊洩露,甚至以受害app的許可權執行任意程式碼。這些漏洞案例均已被廠商所修復。

圖 不安全PendingIntent典型案例

2.1 CVE-2020-0188

不安全的PendingIntent存在於AOSP SettingsSliceProvider中,
一旦SettingsSliceProvider被blind,
在返回的Slice中將攜帶一個不做任何操作的noOpIntentPendingIntent:

攻擊App通過bind SettingsSliceProvider獲取PendingIntent,修改base Intent並以Settings的許可權傳送,等待自己的Activity被開啟,就可以實現Settings某些私有Content Provider的讀寫。如圖所示:

圖 CVE-202-0188 POC

2.2 CVE-2020-0389

不安全的PendingIntent存在於
AOSP SystemUI RecordingService當中,為使用者錄屏儲存成功後傳送的通知所使用。


惡意App可以實現一個NotificiationListener
Service,修改base Intent,將其clipdata指向ContactsProvider :

由於SystemUI具有READ_CONTACTS許可權,因此惡意App 被開啟時,即可成功讀取通訊錄。

2.3 A-166126300

不安全的PendingIntent存在AOSP BluetoothMediaBrowserService中

惡意App可以連線BluetoothMediaBrowserService ,通過MediaBrowserCompat.ConnectionCallback 獲取PendingIntent。
由於BluetoothMediaBrowserService存在於具有通訊錄許可權Bluetooth應用中,因此通過PendingIntent重定向攻擊可讀取通訊錄

2.4 某流行App

某具有通訊錄許可權的流行App實現了視窗小部件,使用者點選視窗小部件的按鈕實現跳轉,但這個跳轉是通過隱式Intent構建PendingIntent實現的。

對視窗小部件所屬的AppWidgetProvider進行bind,通過反射逐次獲取 RemoteViews->mActions->mResponse->mPendingIntent ,最終可以拿到上述不安全的PendingIntent,進而如法炮製,讀取通訊錄。

2.5 CVE-2020-0294

這些不安全的PendingIntent存在安卓系統服務中,對於某些bind服務,系統提供了PendingIntent,跳轉到服務的管理介面。

這些PendingIntent可以直接通過系統APIActivityManager.getRunningServiceControlPanel 獲得,後面進行PendingIntent重定向,讀取Settings中的保護ContentProvider。

2.6 危害

上述多個案例均可造成通訊錄這類個人敏感資訊洩露,但實際上,由於PendingIntent重定向攻擊還具有寫資料的能力,因此可能造成更大的危害。

例如,很多App都具有熱更新功能,一般將dex/jar/apk/so等檔案放在自己的私有目錄中,如果這些私有目錄可以被 grantUriPermission=true 的ContentProvider所引用,就可以利用PendingIntent重定向攻擊去改寫熱更新檔案,將攻擊者自己的程式碼注入到其中,實現以受害App的許可權執行任意程式碼。

對於CVE-2020-0188和CVE-2020-0294這類源於系統uid的PendingIntent,由於在UriGrantsManagerService當中進行了限制,因此在原生系統中的危害很有限,只能讀取特定的幾個Content Provider。

但是由於Android系統的定製化,上述限制可能在OEM廠商中被打破,造成更大的危害。

Google對安卓系統中這類漏洞的修復,起初是將base Intent設定為顯式Intent,指定明確的元件。後來均使用FLAG_IMMUTABLE修復,當使用這個flag時,PendingIntent的base Intent將無法通過Intent.fillIn 函式改寫,例如

3 自動化分析

基於對不安全PendingIntent特徵的掌握,我們編寫了一個自動化掃描工具PendingIntentScan,該工具基於Soot[4]這一Java靜態分析框架對apk進行資料流靜態分析,其體系結構如圖所示。

圖 PendingIntentScan原理

首先,使用Soot將apk的位元組碼轉換為Jimple形式的IR,然後搜尋一系列生成PendingIntent的API,並挑選出沒有使用FLAG_IMMUTABLE的:

然後,通過Soot提供的ForwardFlowAnalysis對PendingIntent的Intent引數進行檢查,檢視是否呼叫下列函式。如果都沒有使用,則認為PendingIntent是不安全的:

這個工具目前開源在
https://github.com/h0rd7/Pend...,可以迅速發現apk中存在的不安全PendingIntent,效果如下。

4 安卓12安全變更

針對我們的研究成果,Google 安卓安全團隊對AOSP程式碼進行了全面排查,幾乎修復了所有的不安全PendingIntent。大部分的修復使用了PendingIntent.FLAG_IMMUTABLE,小部分的修復將base Intent設定為顯式Intent。

而在Android 12大版本中,安卓系統對PendingIntent的行為進行了重大安全變更,引入了一個新的flag:PendingIntent.FLAG_MUTABLE,表示base Intent可以改寫。這與原有的FLAG_IMMUTABLE共同描述PendingIntent 的可變性。

對於Target S+的App,Android系統要求開發者必須明確指定PendingIntent的可變性,FLAG_IMMUTABLE和FLAG_MUTABLE必須使用其一,否則系統會丟擲異常。這就要求開發者對自己PendingIntent的使用有清晰的理解,知道PendingIntent是否會在將來被改寫。

Google也對開發者提出了詳細的安全編碼建議:

  • 儘可能使用FLAG_IMMUTABLE來生成不可改寫的PendingIntent;
  • 如果使用FLAG_MUTABLE來生成可改寫的PendingIntent,base Intent一定要使用顯式Intent,明確指定Intent的元件。

同時AndroidStuido IDE中也引入了一個新的lint檢查外掛PendingIntentMutableFlagDetector,用於檢查PendingIntent是否使用了FLAG_IMMUTABLE。

5 結論

本議題解決了PendingIntent的獲取問題,明確了不安全PendingIntent的特徵,提出了有關不安全PendingIntent的重定向攻擊利用方法,從而揭示了安卓系統和流行app有關PendingIntent使用的一種通用安全⻛險。Google針對議題描述的漏洞均已進行了修復,並在Android 12中引入了緩解此問題的重大安全變更,對開發者提出了詳細的安全編碼建議。

開發者在使用FLAG_IMMUTABLE構建PendingIntent時應格外小心,除了要使用顯式Intent以外,還要保證 base Intent其他沒有填充的欄位不會造成安全影響。例如下面存在問題的程式碼源於一個真實app的案例。這個PendingIntent 已經設定了顯式Intent,在通知中使用,用於啟動內部不匯出的MainActivity

在MainActivity中,
可以對EXTRA_REDIRECT_INTENT 進行處理,最後呼叫startActivity:

這樣,劫持PendingIntent仍然可以設定EXTRA_REDIRECT_INTENT,通過startActivity去開啟應用的任意保護元件。

因此,每一個沒有使用FLAG_IMMUTABLE的PendingIntent均應該仔細審查,這是我們對開發者的最後安全忠告。

6、參考

[1]http://retme.net/index.php/20...

[2]https://www.slideshare.net/Ca...

[3]https://mp.weixin.qq.com/s/SA...

[4] http://soot-oss.github.io/soot/

[5]https://developer.android.com...

作者簡介

heeeeen 安全架構師

畢業於北京航空航天大學,現工作於OPPO子午網際網路安全實驗室,擅長Android框架與APP漏洞挖掘,多次獲得Google安全致謝

獲取更多精彩內容,請掃碼關注[OPPO數智技術]公眾號

相關文章