[TOC]
## 1 Activity组件导出检测 # 05001
Activity组件对外暴露会导致数据泄露和恶意的dos攻击。
风险等级: `提醒`
检测规则:
1. 先检查组件的android:exported属性的值,若为true或者未设置改属性的值,则继续进行第2步;若为false,则该组件不具有导出性质,是安全的。
2. 若组件的含有<intent-filter>标签,即使未设置exported属性,默认也会设置为true,若组件不含有<intent-filter>标签,则未设置exported属性值,默认会设置为true。根据该规则,可以确认exported是true还是false。如果是false则是安全的,若是true,则继续进行第3步。
3. 判断android:permission属性的值,空、normal、dangerous是不安全的,signature、signatureOrSystem是安全的。
建议:
* 最小化组件暴露。对不会参与跨应用调用的组件添加`android:exported=false`属性
* 设置组件访问权限。对跨应用间调用的组件或者公开的receiver、service、activity和activity-alias设置权限,同时将权限的protectionLevel设置为`signature`或`signatureOrSystem`
* 组件传输数据验证。对组件之间,特别是跨应用的组件之间的数据传入与返回做验证和增加异常处理,防止恶意调试数据传入,更要防止敏感数据返回
## 2 Service组件导出检测 # 05002
Service组件对外暴露会导致数据泄露和恶意的dos攻击。
风险等级: `提醒`
检测规则:
规则同1
建议:
* 最小化组件暴露。对不会参与跨应用调用的组件添加`android:exported=false`属性
* 设置组件访问权限。对跨应用间调用的组件或者公开的receiver、service、activity和activity-alias设置权限,同时将权限的protectionLevel设置为`signature`或`signatureOrSystem`
* 组件传输数据验证。对组件之间,特别是跨应用的组件之间的数据传入与返回做验证和增加异常处理,防止恶意调试数据传入,更要防止敏感数据返回
## 3 Receiver组件导出检测 # 05003
Receiver组件对外暴露会导致数据泄露和恶意的dos攻击。
风险等级: `提醒`
检测规则:
规则同1
建议:
* 最小化组件暴露。对不会参与跨应用调用的组件添加`android:exported=false`属性
* 设置组件访问权限。对跨应用间调用的组件或者公开的receiver、service、activity和activity-alias设置权限,同时将权限的protectionLevel设置为`signature`或`signatureOrSystem`
* 组件传输数据验证。对组件之间,特别是跨应用的组件之间的数据传入与返回做验证和增加异常处理,防止恶意调试数据传入,更要防止敏感数据返回
## 4 Provider组件导出检测 # 05004
provider组件导出可能会带来信息泄露隐患。API Level在17以下的所有应用的`android:exported`属性默认值为true,17及以上默认值为false。
风险等级:`提醒`
检测规则:
规则同1
建议:
* 最小化组件暴露。对不会参与跨应用调用的组件添加`android:exported=false`属性
* 设置组件访问权限。对导出的provider组件设置权限,同时将权限的protectionLevel设置为`signature`或`signatureOrSystem`
* 由于contentprovider无法在android2.2(API-8)申明为私有。故建议将`minSdkVersion`设为8以上。
## (未实现)ContentProvider目录遍历漏洞检测
该漏洞由于Content Provider组件暴露,没有对Content Provider组件访问权限进行限制且对Uri路径没有进行过滤,攻击者通过Content Provider实现的OpenFile接口进行攻击,如通过`../`的方式访问任意的目录文件,造成隐私泄露。
![ContentProvider目录遍历漏洞检测-说明](https://wiki-1252789527.cos.ap-shanghai.myqcloud.com/scan_model/image027.jpg)
风险等级:`提醒`
检测方法:
找出导出的provider组件
使用Android安全分析框架*Drozer*动态分析APK,需要安装安卓模拟器(安装了*Genymotion*,基于X86 + ARM支持包)
使用adb进行端口转发,转发到Drozer使用的端口31415
adb forward tcp:31415 tcp:31415
在Android设备上开启Drozer Agent
在PC上启动Drozer
drozer console connect
在Drozer控制台中使用命令,分析目标APK是否存在目录遍历漏洞
run scanner.provider.traversal -a <package-name>
建议:
* 将不必要导出的Content Provider设置为不导出
* 由于Android组件Content Provider无法在Android 2.2(即API Level 8)系统上设为不导出,因此如果应用的Content Provider不必要导出,**阿里聚安全**建议声明最低SDK版本为8以上版本;由于API level 在17以下的所有应用的`android:exported`属性默认值都为true,因此如果应用的Content Provider不必要导出,阿里聚安全建议显示设置注册的Content Provider组件的`android:exported`属性为false;
* 去除没有必要的`openFile()`接口
* 如果应用的Content Provider组件没有必要实现`openFile()`接口,阿里聚安全建议移除该Content Provider的不必要的`openFile()`接口
* 过滤限制跨域访问,对访问的目标文件的路径进行有效判断
* 使用`Uri.decode()`先对Content Query Uri进行解码后,再过滤如可通过`../`实现任意可读文件的访问的Uri字符串
* 设置权限来进行内部应用通过Content Provider的数据共享
* 使用签名验证来控制Content Provider共享数据的访问权限,如设置`protectionLevel=signature或signatureOrSystem`
* 公开的content provider确保不存储敏感数据
* 提供asset文件时注意权限保护
## 5 Provider:grant-uri-permission属性检测 # 05005
`grant-uri-permission`若设置为true,可被其它程序员通过uri访问到content provider的内容,容易造成信息泄露。默认是false。
风险等级:`提醒`
问题示例:
```
<provider
android:grantUriPermissions="true">
...
</provider>
```
建议:
如无需对外提供数据,则将content provider的`android:grantUriPermissions`设置为false。
```
<provider
android:grantUriPermissions="false">
...
</provider>
```
查阅更多:
* https://security.tencent.com/index.php/blog/msg/6
## 6 Intent-Based攻击检测 # 05006
在AndroidManifest文件中定义了android.intent.category.BROWSABLE属性的组件,可以通过浏览器唤起,这会导致远程命令执行漏洞攻击。
风险等级:`低危`
问题示例:
Activity只有配置了category filter才有被android.intent.category.BROWSABLE通过这种方式在浏览器中打开
通过扫描Minifest中的所有组件,检测出所有组件有中intent-filter带有`<category android:name="android.intent.category.BROWSABLE"/>`属性的,将其标注为问题代码代码段,并报出低危风险的提示。
```
<activity android:name=".MainActivity">
<intent-filter>
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
```
建议:
* APP中任何接收外部输入数据的地方都是潜在的攻击点,**检查并过滤**来自网页的参数
* 不要通过网页传输敏感信息,有的网站为了引导已经登录的用户到APP上使用,会使用脚本动态的生成URL Scheme的参数,其中包括了用户名、密码或者登录态token等敏感信息,让用户打开APP直接就登录了。恶意应用也可以注册相同的URL Sechme来截取这些敏感信息。Android系统会让用户选择使用哪个应用打开链接,但是如果用户不注意,就会使用恶意应用打开,导致敏感信息泄露或者其他风险。
## 7 Intent Scheme URL漏洞攻击检测 # 05007
Intent Scheme URI是一种特殊的URL格式,用来通过Web页面启动已安装应用的Activity组件,大多数主流浏览器都支持此功能。
Android Browser的攻击手段——Intent Scheme URLs攻击。这种攻击方式利用了浏览器保护措施的不足,通过浏览器作为桥梁间接实现Intend-Based攻击。相比于普通Intend-Based攻击,这种方式极具隐蔽性,
如果在app中,没有检查获取到的load_url的值,攻击者可以构造钓鱼网站,诱导用户点击加载,就可以盗取用户信息。所以,对Intent URI的处理不当时,就会导致基于Intent的攻击。
风险等级:`高危`
问题示例:
如果浏览器支持Intent Scheme URI语法,一般会分三个步骤进行处理:
1. 利用Intent.parseUri解析uri,获取原始的intent对象
2. 对intent对象设置过滤规则
3. 通过Context.startActivityIfNeeded或者Context.startActivity发送intent;其中步骤2起关键作用,过滤规则缺失或者存在缺陷都会导致Intent Schem URL攻击
关键在于Intent.parseUri函数,比较安全的使用Intent Scheme URI方法是,如果使用了Intent.parseUri函数,获取的intent必须严格过滤,intent至少包含以下三个策略:
* addCategory("android.intent.category.BROWSABLE")
* setComponent(null)
* setSelector(null)
通过扫描出所有调用了Intent.parseUri方法的路径,并检测是否使用上述的三个策略。若三者均使用则认为安全,否则需要标注为`高危`
~~~
// convert intent scheme URL to intent object
Intent intent = Intent.parseUri(uri);
// forbid launching activities without BROWSABLE category
intent.addCategory("android.intent.category.BROWSABLE");
// forbid explicit call
intent.setComponent(null);
// forbid intent with selector intent
intent.setSelector(null);
// start the activity by the intent
context.startActivityIfNeeded(intent, -1)
Intent intent_2 = Intent.parseUri(uri);
~~~
建议:
Intent.parseUri函数返回的Intent对象需要按照以下方式进行实现,才可以认为是安全的。
```
// 将intent的URI(intent scheme URL)转换为intent对象
Intent intent = Intent.parseUri(uri);
// 禁止在没有设置可浏览的目录(BROWSABLE category)的时候启动活动
intent.addCategory("android.intent.category.BROWSABLE");
// 禁止显式调用(explicit call)
intent.setComponent(null);
// 禁止intent的选择器(selector)
intent.setSelector(null);
// 通过intent启动活动
context.startActivityIfNeeded(intent, -1)
```
## 8 应用本地拒绝服务漏洞检测 # 05008
Android系统提供了Activity、Service和Broadcast Receiver等组件,并提供了Intent机制来协助应用间的交互与通讯,Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android系统则根据此Intent的描述,负责找到对应的组件,将Intent传递给调用的组件,并完成组件的调用。
Android应用本地拒绝服务漏洞源于程序没有对Intent.GetXXXExtra()获取的异常或者畸形数据处理时没有进行异常捕获,从而导致攻击者可通过向受害者应用发送此类空数据、异常或者畸形数据来达到使该应用Crash的目的,简单的说就是攻击者通过Intent发送空数据、异常或畸形数据给受害者应用,导致其崩溃。
风险等级:`低危`
问题示例:
~~~
//源于程序没有对getAction()等获取到的数据进行空指针判断,从而导致了空指针异常导致应用崩溃
Intent i = new Intent();
try {
if (i.getAction().equals("TestForNullPointerException")) {
Log.d("TAG", "Test for Android Refuse Service Bug");
}
} catch Exception {
}
Intent abc = new Intent();
Intent kkk = new Intent();
if (abc.getAction().equals("TestForNullPointerException")) {
Log.d("TAG", "Test for Android Refuse Service Bug");
}
//源于程序没有对getSerializableExtra()等获取到的数据进行类型判断而进行强制类型转换,从而导致类型转换异常导致拒绝服务漏洞
Intent a = getIntent();
try {
String test = (String) a.getSerializableExtra("serializable_key");
} catch Exception {
//针对异常进行操作
}
//源于程序没有对getIntegerArrayListExtra()等获取到的数据数组元素大小判断,导致数组访问越界而造成拒绝服务漏洞
Intent intent = getIntent();
ArrayList<Integer> intArray = intent.getIntegerArrayListExtra("user_id");
if (intArray != null) {
for (int i = 0; i < 10; i++) {
intArray.get(i);
}
}
//源于程序没有找到从getSerializableExtra()获取到的序列化对象的类定义,因此导致发生类未定义的异常导致拒绝服务漏洞
a.getSerializableExtra("key");
~~~
## 9 manifest中定义组件未实现检测 # 05009
在manifest文件中定义的组件导出,且没有代码实现,则攻击者可以通过ddos攻击导致app奔溃。
风险等级:`中危`
问题示例:
首先获取app源码中所有的类路径(包名+类名),然后检测manifest中声明的所有组件是否存在于类路径中即可。
建议:
删除manifest文件中无效的导出组件
## 10 Debug或Test敏感测试组件泄露检测 # 05010
一些app在正式发布前,为了方便调试app,都会在app里集成一些调试或测试界面。这些测试界面可能包含敏感的信息。
风险等级:`低危`或`中危`
问题示例:
遍历manifest文件中的所有组件名称,找出所有带有debug或test等测试字样的关键字组件,并根据组件的intent-filter属性构造intent发送让组件弹出进行检测
建议:
在正式发布前移除所有的测试组件
## 11 Intent不安全反射风险检测 # 05011
通过Intent接收的Extra参数来构造反射对象会导致从不受信任的源加载类。攻击者可以通过巧妙地构造达到加载其它类的目的。
风险等级:`低危`
问题示例:
Step1:检测出导出的组件
Step2:在导出的组件下,检测两个关键函数,分别是:getIntent()和Class.forName("....")
```
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
//这里的intent使用了getStringExtra方法加载了一个名称为className的类
Intent intent = getIntent();
String className = intent.getStringExtra("className");
String methodName = intent.getStringExtra("methodName");
try {
Class<?> clz = null;
//尝试以反射的方式构造className的实例
clz = Class.forName(className);
Date object = (Date) clz.newInstance();
Method method = clz.getMethod(methodName);
Toast.makeText(getApplicationContext(), method.invoke(object, null) + "======", Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
逆向后对应的smali代码如下:
```
...
invoke-virtual {p0}, Lcom/bug/intent/reflection/SecondActivity;->getIntent()Landroid/content/Intent;
...
invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class;
...
```
建议:
* 不要通过Intent接收的Extra传播的反射函数
* 将接受反射的组件设置为非导出组件