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

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

1 簡介

1.1 Intent

安卓系統中,Intent是在元件間傳遞的通訊訊息,用於執行開啟Activity、傳送廣播、啟動服務等動作,而Intent物件內部的欄位則規定了Intent傳送的目的元件,以及執行動作的具體內容,包括action、category、data、clipdata、package、flag、extra、component和selector。

其中component和selector用於設定Intent的目的元件,規定Intent傳送給誰。按照是否設定component和selector,Intent可劃分為:

• 顯式Intent:具有component或者selector的Intent
• 隱式Intent:僅設定了action的Intent,註冊對應action的Intent-filter的元件可以接收到Intent。

另外,還有一種特殊的空Intent new Intent(),既沒有設定component,也沒有設定action,甚至沒有設定任何欄位。

1.2 PendingIntent

PendingIntent可以看作Intent的高階版本,實現了一種委託授權傳送Intent進行元件間通訊的機制。

首先,App可以使用getActivity、getBroadcast、getService等API向Android系統申請一個PendingIntent物件,例如在函式getActivity :

中,intent引數構成了所⽣成PendingIntent物件的base Intent,⽽在此特定的getActivity函式中,該base Intent應該⽤於開啟Activity,否則⽆意義。後⾯的flags引數決定了PendingIntent的⾏為,例如FLAG_IMMUTABLE就⽤於規定base Intent不能被改寫。

接下來,這個PendingIntent物件可以傳送給其他App使用,其他App呼叫PendingIntent.send時,就能夠以PendingIntent源App的身份和許可權傳送PendingIntent中的base Intent。其他App甚至還可以提供一個新的Intent,對base Intent進行改寫。

因此, App A將PendingIntent交給App B,就意味著將自己的身份與許可權連同要做的事情委託給了App B, 這個事情由PendingIntent中的base Intent指定。

如果惡意App有能力獲取上述通訊過程中的PendingIntent,就可能以源App的身份和許可權傳送修改後的base Intent,造成非預期的安全後果,這就是PendingIntent面臨的安全風險。

2 歷史研究

以往的研究涉及的實際漏洞案例不多,一個著名的例子是Android系統AccountManagerService中的BroadcastAnyWhere漏洞,涉及到Settings App、System Server與Authenticator App的複雜互動。

在下面Step 2中AccountManagerService通過addAccount回撥函式提供給AppB Authenticator的options bundle物件中,包含了一個PendingIntent,其base Intent為空Intent:


由於這個廣播PendingIntent由system_server AccountManagerService(uid 1000)所建立,代表了系統的身份和許可權,且並未設定base Intent的其他欄位。普通Authenticator App拿到以後,改寫其base Intent,例如設定一個action

android.intent.action.BOOT_COMPLETED ,最後呼叫 PendingIntent.send 以uid 1000的身份傳送特權廣播。

第二個案例來源於CanSecWest2016的分享,即時通訊軟體LINE App啟動服務時洩漏了一個PendingIntent物件,且base Intent為空Intent:

上面啟動服務使用的隱式Intent,因此惡意APP可以註冊一個Intent-filter為
jp.naver.android.npush.intent.action.SUBSCRIBE 的服務,然後獲取上面的PendingIntent,最後以LINE App的身份傳送廣播,造成偽造LINE App推送訊息的危害。

3 PendingIntent使用場景

以往公開的研究僅給出了兩個有關不安全PendingIntent使用的稀有案例,均為攜帶空Intent的廣播PendingIntent,在IPC通訊中洩漏給了惡意App。然而,PendingIntent在Android系統的使用中又如此廣泛,在IPC通訊中的使用僅為冰山一⻆。

PendingIntent還可以廣泛存在於SliceProviders、通知(Notifications)、媒體瀏覽服務(MediaBrowserServices)、視窗小部件(AppWidgets)、定時器管理器(AlarmManager)當中,這就觸及了本議題要解決的第一個問題:Android中這些廣泛使用的PendingIntent是否有可能被App獲取,如何獲取?

經過我們研究發現,安卓系統中廣泛使用的SliceProvider、通知、視窗小部件、媒體瀏覽服務所使用的PendingIntent,都有可能被惡意APP獲取,這就極大地擴充了PendingIntent 的攻擊面。

3.1 SliceProvider

SliceProvider是自Android P開始引入的一種應用程式間共享UI介面的機制,Slice的呈現者(SlicePresenter),可以通過Slice URI和Android系統提供的bindSlice等API來訪問另一個App通過SliceProvider分享出來的Slice。

簡言之,Slice就是可分享的UI介面,包括圖示、文字和動作(SliceAction),Slice通過URI來唯一標識。例如Settings中開啟NFC開關的這個介面,就可以通過SettingsSliceProvider中

content://android.settings.slices/action/toggle_nfc 這個URI共享給別的應用使用,使用者不必開啟Settings,就可以在其他應用介面中對NFC開關進行操作。如圖所示。

Slice中的動作SliceAction,實質是通過PendingIntent實現的。

如圖,按照SliceProvider的設計,作為SlicePresenter的App可以通過呼叫系統APISliceVIewManager.bindSlice 去bind一個特定URI的SliceProvider,或者直接使用更加底層的SliceProvider call函式去獲得一個Slice,進而獲得Slice中的PendingIntent,文章具體描述了使用call函式獲取SliceProvider中Slice PendingIntent的方法,從Slice物件中獲取PendingIntent需要層層剝絲抽繭:

3.2 Notifications

通知在Android系統中應用極為廣泛,用於在狀態列中對使用者進行提示,幾乎為每個App所使用,是安卓開發者最常使用PendingIntent的地方,如下為傳送通知的示例程式碼

通過 setContentIntent 方法對通知設定了一個contentIntent PendingIntent,使使用者在點選通知的正文時,觸發PendingIntent,跳轉到AlertDetails。除了contentIntent外,通知中還可以設定其他按鈕,通過actionIntent進行設定,下面的通知示例表明了通知中的各種PendingIntent。另外,通知還可能包括另外一個deleteIntent PendingIntent,在被使用者刪除通知時觸發。

對於通知中PendingIntent的獲取,安卓系統提供了通知監聽服務NotificationListenerService,

任何三方App都可以實現該服務對通知進行監聽。

經過使用者授權後,通知監聽服務就可以獲得通知物件,進而獲得物件中的PendingIntent。如下,可以在onNotificationPosted回撥函式中獲取通知及其content Intent PendingIntent。

3.3 MediaBrowserService

媒體瀏覽器服務MediaBrowserService與音樂播放有關,可以讓其他App發現、瀏覽媒體內容並控制播放。媒體瀏覽服務中也可能使用PendingIntent來作為回撥。那麼其他App可以實現媒體瀏覽器MediaBrowser來連線MediaBrowserService,進而獲取PendingIntent。

MediaBrowser與MediaBrowserService的實現架構如圖所示。MediaBrowser作為客戶端,實現了UI、媒體控制和媒體瀏覽的功能,連線MediaBrowserService,獲得媒體內容層次結構的表示,如播放列表、媒體庫等等,並獲得有關播放狀態的回撥資訊。

3.4 AppWidgets

AppWidgets就是可以在Android桌面上新增的視窗小部件。按照開發者文件,AppWidgets上可以在其他應用(如桌面Launcher)中顯示,並接受週期性更新的迷你程式檢視。

AppWidget可以通過AppWidgetProvider釋出,容納AppWidiget的應用稱為AppWidgetHost。

AppWidgetHost通過安卓系統提供的AppWidgetFramewor
訪問(bind)AppWidgetProvider,並接受有關的AppWidget的變化資訊,如圖所示。

使用者可以在桌面中看到的視窗小部件,實質是通過RemoteViews實現。RemoteView將本屬於AppWidgetProvider應用中的View,跨程式傳輸,在AppWidgetHost應用中顯示。注意到RemoteView存在下面一個有關PendingIntent的API,用來設定其中按鈕被點選後的行為。

通過實現AppWidgetHost,訪問AppWidgetProvider中的AppWidgets,獲取RemoteView,進而有可能獲取RemoteView中的PendingIntent。

RemoteViews可以通過AppWidgetServiceImpl中的下列AIDL介面拿到

但是拿到RemoteViews以後再獲取PendingIntent卻頗費周折,因為RemoteViews並未提供公開API來獲取其中的PendingIntent。但經過分析,我們發現可以通過反射,按照如下順序逐次獲取隱藏的成員變數mPendingIntent。

至此,我們已經解決了本議題的第一個問題,經過研究表明,Android系統中使用的PendingIntent大都可以被三方App獲取,獲取方式包括bind SliceProvider、監聽通知、連線媒體瀏覽器服務或者bind容納視窗小部件的AppWidgetsProvider。

注:本文是對OPPO安全子午實驗室發表在Blackhat EU 2021會議議題:Re-route Your Intent forPrivilege Escalation: A Universal Way to Exploit Android PendingIntents in High-profile and SystemApps的技術文稿整理。

感謝OPPO安全與子午實驗室對議題發表的大力支援!感謝前同事陳文波和香港中文大學吳道遠兩位同作者對本議題的貢獻!

下篇我們將繼續探討第二個關鍵問題:如果這些PendigIntent不安全,如何利用才能造成安全危害?敬請關注!

4、參考

[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 安全架構師

畢業於北京航空航天大學,擅長Android框架與APP漏洞挖掘,多次獲得Google安全致謝。

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

相關文章