Android组件渗透¶
Android 四大组件¶
Android 四大组件分别是 Activity、Content Provider、Broadcast Receiver 以及 Service
AndroidManifest.xml描述了应用程序的每个组件,以及他们如何交互。
| 组件名称 | 具体用途 |
|---|---|
| Activity | 展示组件,用于向用户直接展示一个界面,而且可以接受用户的输入信息从而进行交互 |
| Content Provider | 数据共享组件,用于向其他组件乃至其他应用共享数据 |
| Broadcast Receiver | 通讯组件,用于在不同组件甚至不同应用中传递消息 |
| Service | 能在后台执行长期运行操作的组件,它没有UI界面,运行在宿主进程的主线程中,因此执行耗时的后台计算任务需要在单独的线程中去完成 |

Activities¶
一个活动标识一个具有用户界面的单一屏幕。举个例子,一个邮件应用程序可以包含一个活动用于显示新邮件列表,另一个活动用来编写邮件,再一个活动来阅读邮件。当应用程序拥有多于一个活动,其中的一个会被标记为当应用程序启动的时候显示。
一个活动是**Activity**类的一个子类,如下所示:
一个应用程序可以有1个或多个活动,而没有任何限制。每个为应用程序所定义的活动都需要在AndroidManifest.xml中声明。应用的主要活动需要在清单中声明,且意图过滤器标签中需要包含 MAIN 动作和 LAUNCHER 类别。如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.helloworld"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="22" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
不论是 MAIN 动作还是 LAUNCHER 类别没有在活动中声明,那么应用程序的图标将不会出现在主屏幕的应用列表中。
Services¶
服务是运行在后台,执行长时间操作的组件。举个例子,服务可以是用户在使用不同的程序时在后台播放音乐,或者在活动中通过网络获取数据但不阻塞用户交互。
一个服务是**Service**类的子类,如下所示:
Broadcast Receivers¶
广播接收器简单地响应从其他应用程序或者系统发来的广播消息。举个例子,应用程序可以发起广播来让其他应用程序知道一些数据已经被下载到设备,并且可以供他们使用。因此广播接收器会拦截这些通信并采取适当的行动。
广播接收器是**BroadcastReceiver**类的一个子类,每个消息以**Intent**对象的形式来广播。
Content Providers¶
内容提供者组件通过请求从一个应用程序到另一个应用程序提供数据。这些请求由**ContentResolver**类的方法来处理。这些数据可以是存储在文件系统、数据库或者其他其他地方。
内容提供者是**ContentProvider**类的子类,并实现一套标准的API,以便其他应用程序来执行事务。
我们将在独立的章节中通过这些标签的细节来涵盖应用程序组件。
附件组件¶
有一些附件的组件用于以上提到的实体、他们之间逻辑、及他们之间连线的构造。这些组件如下:
| 组件 | 描述 |
|---|---|
| Fragments | 代表活动中的一个行为或者一部分用户界面。 |
| Views | 绘制在屏幕上的UI元素,包括按钮,列表等。 |
| Layouts | 控制屏幕格式,展示视图外观的View的继承。 |
| Intents | 组件间的消息连线。 |
| Resources | 外部元素,例如字符串资源、常量资源及图片资源等。 |
| Manifest | 应用程序的配置文件。 |
组件的安全问题¶
四大组件的安全问题很大一部分是由于组件设置成导出状态(android:exported=true)引起的。
Android 四大组件中,Activity,Service,BroadcastReceiver广播接收器之间都可以通过Intent进行通信,所以他们都存在由Intent传输数据引发的本地拒绝服务漏洞。
- 背景知识:Android系统中的Intent机制负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,系统则根据此Intent的描述,负责找到对应的组件,将Intent传递给调用的组件,并完成组件的调用。
- 产生原理:Android应用本地拒绝服务漏洞源于程序处理
Intent.getXXXExtra()获取的数据时没有进行异常捕获,从而导致攻击者可通过向受害者应用发送此类空数据、异常或者畸形数据来达到使该应用崩溃的目的。 -
危害:
-
导致安全防护等应用的防护功能被绕过或失效(如杀毒应用、安全卫士、防盗锁屏等)。
-
被竞争方应用利用并攻击,使得己方应用崩溃,造成不同程度的经济利益损失。
-
防护:
-
将不必要的组件设置为不导出,在
AndroidMenifest.xml文件中,将相应组件的android:exported属性设置为false,防止引起拒绝服务,尤其是杀毒、安全防护、锁屏防盗等安全应用。 -
处理通过
Intent.getXXXExtra()获取的数据时进行以下判断,同时用try-catch方式捕获所有异常,以防止应用出现拒绝服务漏洞:空指针异常、类型转换异常、数组越界访问异常、类未定义异常、其他异常。 -
攻击代码示例:包括NullPointerException异常、ClassCastException异常、IndexOutOfBoundsException异常、ClassNotFoundException异常。

Activity¶
组件导出导致钓鱼欺诈¶
Android为了提高用户的用户体验,对于不同的应用程序之间的切换,基本上是无缝。
他们切换的只是一个Activity,让切换的到前台显示,另一个应用则被覆盖到后台,不可见。
Activity的概念相当于一个与用户交互的界面。而Activity的调度是交由Android系统中的AMS管理的。AMS即ActivityManagerService(Activity管理服务),各个应用想启动或停止一个进程,都是先报告给AMS。当AMS收到要启动或停止Activity的消息时,它先更新内部记录,在通知相应的进程运行或停止指定的Activity。当新的Activity启动,前一个Activity就会停止,这些Activity都保留在系统中年的Activity历史栈中。每有一个Activity启动,它就压入历史栈顶,并在手机上显示。当用户按下back键时,顶部Activity弹出,恢复前一个Activity,栈顶指向当前的Activity。由于Activity的这种特性,如果在启动一个Activity时,给它加入一个标志位FLAGACTIVITYNEW_TASK,就能使它置于栈顶并立马呈现给用户。如果这个Activity是用于盗号的伪装Activity,那么就会产生钓鱼安全事件或者是一个Activity中有webview加载,如果允许加载任意网页也有可能会产生钓鱼事件。
防护¶
如果当前的程序进入后台,则需要提示用户当前进程状态。
隐式启动intent包含敏感数据¶
Intent类型¶
- 显式 Intent:按名称(完全限定类名)指定要启动的组件。 通常,您会在自己的应用中使用显式 Intent 来启动组件,这是因为您知道要启动的 Activity 或服务的类名。例如,启动新 Activity 以响应用户操作,或者启动服务以在后台下载文件。
创建显式 Intent 启动 Activity 或服务时,系统将立即启动 Intent 对象中指定的应用组件。
- 隐式 Intent :不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。 例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。
创建隐式 Intent 时,Android 系统通过将 Intent 的内容与在设备上其他应用的清单文件中声明的 Intent 过滤器进行比较,从而找到要启动的相应组件。 如果 Intent 与 Intent 过滤器匹配,则系统将启动该组件,并向其传递 Intent 对象。 如果多个 Intent 过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。
对于显式Intent,Android不需要去做解析,因为目标组件已经很明确,Android需要解析的是那些隐式Intent,通过解析,将Intent映射给可以处理此Intent的Activity、IntentReceiver或Service。

Service¶
概述¶
Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行,Service基本上分为两种形式:
- 启动状态
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。
- 绑定状态
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
危害¶
如果一个导出的 Service没有做严格的限制,任何应用可以去启动并且绑定到这个 Service上,取决于被暴露的功能,这有可能使得一个应用去**执行未授权的行为**,获取敏感信息或者是污染修改内部应用的状态造成威胁。
Broadcast receiver¶
概述¶
BroadcastReceiver是 Android的四大组件之一,这个组件涉及两个概念:广播发送者和广播接收者。这里的广播实际上指的就是 intent。当发送一个广播时,系统会将发送的广播 (intent)与系统中所有注册的符合条件的接收者的 IntentFilter进行匹配,若匹配成功,则执行相应接收者的 onReceive函数。可以通过两种方式注册广播接收器,一种是在Manifest.xml文件中通过标签静态注册,另一种是通过Context.registerReceiver()动态注册,指定相应的intentfilter参数。而动态注册的广播是默认导出的。
危害¶
发送广播时如果处理不当,恶意应用便可以**嗅探**、拦截广播,致使**敏感数据泄露**等;
- 原理:发送的intent没有明确指定接收者,而是简单的通过action匹配。恶意应用可注册一个广播接收者嗅探拦截这个广播。
- 防护:使用
LocalBroadcastManager.sendBroadcast()发出的广播只能被app自身广播器接收。
如果接收广播时处理不当,便可导致**拒绝服务攻击**、伪造消息、**越权操作**等。
Content Provider¶
概述¶
Content Provider负责进行**数据交互&共享**,即跨进程通信

危害¶
信息泄露漏洞¶
如果对Content Provider的权限没有做好控制,就有可能导致恶意程序通过构造Content URI读取App的敏感数据。
SQL注入漏洞¶
对Content Provider进行增删改查操作时,程序没有对用户的输入进行过滤,未采用参数化查询的方式,可能导致sql注入攻击。
目录遍历漏洞¶
对外暴露的Content Provider组件实现了openFile()接口,并且没有对Content Provider组件的访问进行权限控制,也没有对访问的目标文件的Uri进行有效判断,第三方应用程序可以利用该接口进行文件目录遍历,访问任意可读文件。
四大组件的安全防护¶
Activity¶
-
谨慎处理接收的Intent以及其携带的信息;
-
私有Activity不应被其他应用启动且应该确保相对是安全的;
-
当Activity返回数据时候需注意目标Activity是否有泄露信息的风险,同时谨慎处理Activity返回的数据,目的Activity返回的数据有可能是恶意应用伪造的;
-
目标Activity十分明确时尽量使用显式Intent;
-
验证目标Activity是否属于恶意app,以免受到Intent欺骗,可用hash签名验证;
-
尽可能的不发送敏感信息,应考虑到启动public Activity中Intent的信息均有可能被恶意应用窃取的风险;
-
不需要被外部程序调用的组件应该添加
android:exported="false"属性,这个属性说明它是私有的,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该组件; -
对于希望Activity能够被特定的外部程序访问,可以为其设置访问权限,具体做法有以下两种:
①组件添加android:permission属性;
android:permission:"android.perrmission.SEND SMS"
②protectionLevel权限声明;
android:protectionLevel="dangerous"
Service¶
- 私有的service尽量不定义intent-filter并且设置exported属性为false;
- 尽量用显式的方式启动service;
- 合作service需对合作方的app签名做校验;
- Service接收到的数据需要谨慎处理;
- 内部service需使用签名级别的protectionLevel来判断是否为内部应用调用;
- Service不应在onCreate时决定是否提供服务,应在onStartCommand/onBind/onHandleIntent等方法被调用时做判断;
- 当service有返回数据时,应判断接收数据的组件是否有信息泄露的风险;
- 尽量不发送敏感信息;
Content Provider¶
- 如果不需要与其他应用程序进行数据共享,就应该在manifest文件中设置
android:exported="false"; - 注意,在API Level低于8时,即使显式地声明了
android:exported="false",其它应用程序仍然可以访问对应的Content Provider,所以尽量避免使用Level低于8的API; - 需要向外部提供数据的Content Provider需设置访问权限;
- 传递给ContentProvider的参数应该被视为不可信的输入,不应该在没有经过任何处理的情况下直接用于SQL查询;
- 避免使用SQLiteDatabase对象的execSQL()方法;
Broadcast Receiver¶
- 私有广播接收器设置
android:exported="false",尽量不配置intent-filter; - 私有广播尽量使用LocalBroadcastManager动态注册和使用;
- 暴露的广播接收器需要对数据来源进行权限控制和身份验证;
- 广播接收器对于接收的数据要谨慎地使用多种异常来控制数据处理;
- 发送广播时如果包含敏感数据则需要显示意图,并通过setPackage()指定接收者包名;