ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[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传播的反射函数 * 将接受反射的组件设置为非导出组件