Android官方的介绍:应用微件是可以嵌入其他应用(如主屏幕)并接收定期更新的微型应用视图。这些视图称为界面中的微件,您可以使用应用微件提供程序发布微件。能够容纳其他应用微件的应用组件称为应用微件托管应用。
微件是自定义主屏幕的一个重要方面。您可以将微件想象成“一目了然”的视图,它们让最重要的应用数据和功能一览无余,从用户的主屏幕即可进行访问。用户可以在其主屏幕面板间移动微件,如果系统支持,用户还可以调整微件的大小,按照他们的偏好量身定制微件中的信息量。
直观一点,就是桌面上的这些小部件:
AppWidget的几个部分:
AppWidgetProvider:这是一个BroadcastReceiver,它提供资源数据,由应用自己实现。
AppWidgetFramework:用于管理AppWidget,包括AppWidgetManager和AppWidgetService,属于系统服务。
AppWidgetHost:用于承载显示AppWidget,桌面一般会承担这个角色。
值得注意的广播:
AppWidgetProvider实际上是一个BroadcastReceiver,它特殊的地方在于,它不仅有onReceive()来接收广播,还有一些特定的方法接收特定的广播,这些特定的广播由AppWidgetService发出,以管理AppWidget的生命周期。
ACTIONAPPWIDGETUPDATE,对应onUpdate(),对应当需要更新AppWidget 时发送。
ACTIONAPPWIDGETDELETED,对应onDeleted(),当一个 AppWidget 实例被删除时发送。
ACTIONAPPWIDGETENABLED,对应onEnabled(),当第一次 AppWidget的实例被添加时发送。
ACTIONAPPWIDGETDISABLED,对应onDisabled(),在删除此AppWidgetProvider的最后一个 AppWidget 实例时发送。
ACTIONAPPWIDGETOPTIONS_CHANGED,对应onAppWidgetOptionsChanged(),当 AppWidget 的自定义附加功能发生变化时发送。
任意广播(包括自定义广播和前面提到的特定的广播),对应onReceive(),每次接收广播都会在前面提到的每个方法之前调用它。
用户交互:
AppWidget的点击事件,由RemoteViews中设置的PendingIntent实现,调用setOnClickPendingIntent(@IdRes int viewId, PendingIntent pendingIntent)。用户点击后,便会触发PendingIntent中封装的Intent,去实现相应的功能。
想要分析AppWidget的风险点,就需要分析AppWidget的生命周期,其中最重要的是AppWidget的更新。
AppWidget更新有多种情况,定期更新,响应用户交互,响应广播事件。
① 下图包含了三种情况,所以有三个起点:
AppWidgetService发送广播一般是定期更新,或者是管理其他的生命周期;
用户主要是触发了事件,一般是点击了按钮,触发了PendingIntent;
其他应用或者组件也可能会发广播给AppWidgetProvider。
② 在AppWidgetProvider收到广播后,发送AppWidgetId和RemoteViews给AppWidgetFramework,形成对应关系。
③ 然后AppWidgetFramework会通知AppWidgetHost需要更新,将AppWidgetId和RemoteViews发送给AppWidgetHost进行更新。
④ AppWidgetHostView只是一个容器,用来容纳AppWidget的View,通过RemoteViews获得真正的View,然后进行更新显示。
从流程上看,广播和PendingIntent是有风险的。
3.1 接收任意三方发送的广播:
AppWIdgetProvider是一个BroadcastReceiver,它需要用广播来进行操控,有时候我们会用到自定义广播,如果它接收的广播能被任意三方发送,那么就能够控制该AppWidget,尤其是一些特权接口,可能会被恶意利用,AppWidgetService发送的广播都是Android原生广播,三方没有权限发送,我们更需要关注自定义广播。
3.2 不安全的PendingIntent被劫持:
不安全的PendingIntent能够被利用窃取应用的身份和权限来进行越权操作。虽然Android14中已经进行了限制,如果以Android14为目标平台的应用程序创建一个可变的PendingIntent,但未指定组件或包,系统将会抛出异常。
AppWidget利用PendingIntent来实现点击事件,在RemoteViews中设置,而RemoteViews是会被传递给AppWidgetHost的,如果该RemoteViews能被劫持,就能获得它的PendingIntent,导致安全风险出现。
现在问题就是如何能劫持到它的PendingIntent。
AppWidgetHost获得RemoteViews可以通过使用AppWidgetId向AppWidgetService查询。
目前只能由绑定该AppWidget的AppWidgetHost查询,在AppWidgetService中存在对请求者的校验:
所以目前只能自己建一个AppWidgetHost去添加AppWidget,然后获取它的RemoteViews。
获取到RemoteViews后,还要通过反射,来获取其中的PendingIntent,因为并未提供可以获得PendingIntent的公开API。
通过这样的次序逐层获得隐藏的成员变量mPendingIntent:
在添加AppWidget的两种方式中,AppWidgetPickActivity需要用户自己手动选择将要添加的AppWidget,直接绑定指定的AppWidget也会有添加AppWidget的授权弹窗,算是一种局限性,无法做到用户无感知。
使用demo来实践一下:
AppWidgetProvider:com.example.appwidgettest
拉起目标:xxx.yyy.zzz
先写一个有问题的AppWidgetProvider:
然后写一个AppWidgetHost去绑定该AppWidget,
需要先申请权限,
在manifest中添加权限:
在运行时,用户必须显式向应用授予权限,以下代码可以拉起授权的对话框:
然后就可以进行绑定了:
绑定过后,我们就可以通过AppWidgetId去查询RemoteViews了:
PendingIntent就在RemoteViews中,RemoteViews就在返回的reply中,
从reply中获得PendingIntent,通过反射,逐次将PendingIntent剥离出来:
从此就能构造intent,以com.example.appwidgettest的身份去拉起xxx.yyy.zzz。
5.1 对广播做限制
对AppWidgetProvider设置权限保护,权限保护不能低于signature,不能让任意三方发送广播来控制AppWidget;
如果AppWidgetProvider不需要接收外部的广播,也可以将其设置为不可导出。
5.2 对PendingIntent做限制
如非必须允许PendingIntent接收者篡改信息,创建PendingIntent时一定要增加FLAG_IMMUTABLE为flag信息;
使用PendingIntent时,对于封装的Intent必须明确、显式的指定组件信息(以Android14为目标平台的应用已经强制要求可变的PendingIntent必须如此创建,否则会抛出异常);
建议在接受方有使用如data、action或extras内容进行流程控制时,主动使用默认内容占位;
非必要不建议设置FILL_IN相关flag。
[1] appwidget加载流程及其与app的通信:
https://www.jianshu.com/p/8ad60d0286ce
[2] 构建应用微件:
https://developer.android.com/guide/topics/appwidgets?hl=zh-cn
[3] Android中Launcher对于AppWidget处理的分析,AppWidgetHost角色:
https://blog.csdn.net/thl789/article/details/7893292
[4] Android AppWidget:https://juejin.cn/post/6979180597486829576
[5] Android源码:https://cs.android.com/
[6] 广播概览:https://developer.android.com/guide/components/broadcasts?hl=zh-cn#security-and-best-practices
[7] PendingIntent劫持问题分析:https://mp.weixin.qq.com/s/VxGDPzjssALOHv_sMIop8Q
[8] Android AppWidget系统框架:
https://blog.csdn.net/thl789/article/details/7879257
[9] PendingIntent重定向:一种针对安卓系统和流行App的通用提权方法——BlackHat EU 2021议题详解(上):https://mp.weixin.qq.com/s/lB3yV1-VE3X-CmqN2T5KCw
[10] Behavior changes: Apps targeting Android 14 or higher:https://developer.android.com/about/versions/14/behavior-changes-14#security