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数智技术]公众号


OPPO数智技术
612 声望950 粉丝