测试方法
使用自己编写的测试APP测试各个扫描平台的扫描能力。这些扫描能力主要分为静态检测能力和动态检测能力。静态检测能力包括检测隐藏dex、过程间分析、较复杂漏洞检测、逆向分析;动态测试主要是指测试拒绝服务漏洞的能力,拒绝服务漏洞又可以划分为:空Intent引起的拒绝服务,强制类型转换引起的拒绝服务以及序列化对象导致的拒绝服务。由于这些检测能力决定了扫描器扫描结果的精度和准度,因此我详细分析了各个扫描平台的扫描能力。
3.2.1 自动化脱壳
目前很多APP通过加壳来防止自己被反编译,而扫描器都是通过在反编译的代码中进行漏洞的扫描。如果扫描器不能自动化地脱去APP加的壳,则根本无法进行有效的漏洞扫描分析。我写了一个包含五个扫描平台都有的全局文件读写漏洞的demo,通过梆梆加固之后,重签名上传到这五个扫描平台,检测结果是:阿里聚安全和百度检测出全局文件读写漏洞,而金刚、AppRisk没有检测出该漏洞。这个demo在360中没有扫描结果,所以360的脱壳能力不得而知。
3.2.2 隐藏Dex检测能力
目前插件化已经在Android开发中越来越普遍。很多APP会将一些独立模块打包成单独的dex文件,并存储到apk的其他目录中,如asset、lib等。如果扫描器没有检测隐藏dex文件的能力,则可能会漏报一些安全风险,造成扫描结果不准确。我编写了一个asset目录包含dex文件的应用程序,分别上传到上述五个扫描器,该dex文件中包含五家扫描器都可以检测的漏洞,结果只有阿里聚安全和百度成功扫描出隐藏dex文件中包含的漏洞。因此,可以推测阿里聚安全和百度具有扫描隐藏dex文件的能力,而360、金刚、百度和AppRisk都没有检测隐藏dex文件的能力。
3.2.3 过程间分析能力
五家扫描器都可以检测全局文件读写漏洞,因此我用该漏洞测试扫描器对过程间分析的能力。
openFileOutput的第二个参数可以指定文件打开的方式,如果以全局可写的方式打开会导致安全风险。这里我构造了两个测试例子。
例一, 直接对openFileOutput的第二参数设置全局可写,因此有漏洞。
例二, 通过函数的参数传递对openFileOutput的第二参数设置全局可写,也应该有漏洞。
测试代码如下:
样本一:函数内设置危险变量Context.MODE_WORLD_WRITEABLE
样本二:函数间设置危险变量Context.MODE_WORLD_WRITEABLE
样本一和样本二可以测试扫描器对过程间分析的检测能力。检测结果如表3-6所示(“√”表示扫描结果正确,“×”表示扫描结果错误。):
表3-6 函数间相互调用检测能力
阿里聚安全 | 360 | 金刚 | 百度 | AppRisk | |
---|---|---|---|---|---|
过程内检测(样本一) | √ | √ | √ | √ | √ |
过程间检测(样本二) | √ | × | × | × | × |
阿里聚安全可以检测出样本一和样本二,而360、金刚、百度和AppRisk都只能检测出样本一。
由此可以推测,360、金刚、百度和AppRisk都只能在过程内进行检测,也就是在函数内进行检测,阿里聚安全可以在过程间进行检测。
3.2.4 逆向分析能力
目前漏洞扫描规则大部分是通过定位关键函数,根据关键函数的参数确定是否会触发漏洞。这是典型的逆向分析问题,可以说逆向分析能力很大程度决定了扫描器检测漏洞的能力。这五家扫描器都有逆向分析的能力,只是逆向分析的能力有些差别。通过扫描器对全局文件读写的代码检测结果分析扫描器逆向分析的能力。
根据全局文件读写漏洞的检测规则,扫描器首先会定位openFileOutput函数,追踪该函数的第二个参数,即打开的模式。打开模式都存储在一个数组中。数组中下标为0的模式没有漏洞,而下标为1的有漏洞。如果扫描结果正确,则说明扫描器的逆向分析能力较强,可以深入到数组等较为复杂的结构中;如果扫描结果有错误,则说明扫描器的逆向分析能力较差,无法逆向追踪到复杂的数据结构中,漏报的可能性较大。
将上述测试代码上传到五家扫描平台,扫描结果如下图所示。“√”表示扫描结果正确,“×”表示扫描结果错误。
表3-7 数组下标敏感性检测结果
阿里聚安全 | 360 | 金刚 | 百度 | AppRisk | |
---|---|---|---|---|---|
样本一 | √ | √ | √ | √ | √ |
样本二 | √ | × | × | × | × |
通过扫描结果可以看到,阿里聚安全正确地扫描出两个样本,而360、金刚、百度和AppRisk都只扫描出样本一。因此可以说阿里聚安全的逆向扫描能力要强于其他四家,当逆向追踪的变量进入一个数组时,阿里聚安全可以继续在数组中进行逆向分析,而其他四家扫描器无法确定数组中各个位置代表的具体值。
我猜测当其他四家扫描器检测全局文件读写漏洞时,首先会定位openFileOutput函数,由于打开方式是由数组中的元素决定,所以360、金刚、百度和AppRisk无法确定该值具体是多少,因此也就无法判断是否存在全局文件读写漏洞。本着减少误报的原则,它们都认为不存在漏洞,所以很幸运,样本一不存在漏洞,它们的检测结果正确;样本二存在漏洞,它们的检测结果错误。
3.2.5检测较复杂漏洞的能力
为了测试扫描器检测是否能检测出由多个条件组合起来判断的漏洞,我选取了Intent Scheme URL漏洞进行对比[6],如果想避免Intent Scheme URL漏洞,parseUri函数得到的Intent必须要设置三个条件(addCategory(“android.intent.category.BROWSABLE”), setComponent(null), setSelector(null) 才能保证漏洞不会发生。
我构造了三个例子进行测试:
例一,三个条件都满足,因此没有漏洞的。
例二,缺少了条件setSelector(null),存在Intent Scheme URL漏洞。
例三,虽然三个条件都满足,但因为没有startActivity所以也不应该被检测出来。
构造如下测试代码:
代码中一共有三个case,其中只有case 2有问题。将上述代码打包成apk,上传到除360和百度之外的三家扫描平台。(360和百度不支持该扫描项,还需要使用另一种漏洞比较360、百度的检测差异)
AppRisk认为三个都有漏洞,通过其扫描报告可以看出,AppRisk只是判断是否有Intent.parseUri函数的调用,如果存在,则就存在Intent Scheme URL漏洞。因此,推测AppRisk的扫描规则仅仅是简单的特征函数匹配,数据流跟踪的能力几乎没有。在该例中仅仅匹配Intent.parseUri,而没有其他条件进行约束,因此误报率比较高。
金刚扫扫描出case 2和case 3,而case 3是没有问题的,所以有一个误报。金刚对该项的扫描比AppRisk要复杂一些,除了匹配parseUri函数外,还检测该Intent是否做了后续的处理,如addCategory、setComponent、setSelector等,如果没有这些函数调用,则认为存在该漏洞。但如果仅仅把Intent构造出来,而没有做任何启动其他组件的操作,如case 3,也是没有漏洞的,所以金刚没有考虑对获取Intent的使用操作,也容易引起误报。
360没有扫描这个漏洞,而其他常见的漏洞漏报也比较多。因此,对它的检测较复杂漏洞的能力不做推测。
当检测百度时,我使用WebView组件系统隐藏接口漏洞作为测试用例。
测试代码如下:
将代码打包成apk上传到百度移动云测试平台,测试百度是否仅仅测试是否有loadUrl函数调用,而不考虑是否启用了JavaScript。从测试代码中可以看出,case 1是有漏洞的,通过调用setJavaScriptEnabled(true)启用了JavaScript,随后调用loadUrl加载页面。Case 2是没有问题的,首先mWebView是一个全局的成员变量,当创建一个WebViewSafeCase的对象时会初始化该WebView,同时显式调用removeJavascriptInterface移除searchBoxJavaBridge,accessibility以及accessibilityTraversal,当外部调用其内部类的方法时,mWebView会启用JavaScript,随后调用loadUrl。如果单从removeFromOutterClassShouldNotFound来看,case 2是有漏洞的,但是实际上mWebView在调用loadUrl之前已经移除隐藏的接口了,如果扫描器没有追踪mWebView这个变量的能力,则很容易误认为case 2是有漏洞的。
百度的扫描结果显示case 1和case 2都包含WebView未移除隐藏接口漏洞,我推测百度没有追踪变量的能力,而仅仅是进行函数匹配。
3.2.6 动态检测能力
一些运行时漏洞,如拒绝服务,只有在程序运行时才有可能触发。如果扫描器没有动态检测的能力,则会漏报一些运行时漏洞。为了检测扫描器是否有动态扫描的能力,我在测试APP中包含4处拒绝服务漏洞的代码,分别是空Intent拒绝服务2个、1个强制类型转换拒绝服务和1个对象序列化拒绝服务。扫描结果如下表所示。
表3-8 动态检测能力扫描结果
阿里聚安全 | 360 | 金刚 | 百度 | AppRisk | |
---|---|---|---|---|---|
空Intent Fuzz | 2 | 0 | 1 | 0 | 0 |
强制类型转换 | 1 | 0 | 1 | 0 | 0 |
对象序列化 | 1 | 0 | 1 | 0 | 0 |
从表3-8中可以看出,阿里聚安全可以扫描出所有的拒绝服务漏洞,金刚可以扫描出3处拒绝服务漏洞,漏报一处拒绝服务代码如下:
而360、百度和AppRisk没有扫描出拒绝服务漏洞。从这个例子我推断除阿里聚安全和金刚外,其他扫描平台没有动态检测能力。
综上所述,阿里聚安全的综合检测能力最高,它不仅可以检测隐藏dex,对数组下标敏感,还可以检测函数相互调用引起的漏洞。除此之外,阿里聚安全还可以追踪变量,记录变量的一系列操作,当变量作为sendMessage的参数被Handler发送出去时,阿里聚安全还可以追踪到相应的处理函数中继续追踪;当变量作为Intent携带的参数跳转到其他组件中时,阿里聚安全还可以到对应的组件中继续追踪该变量。对变量的有效跟踪可以大大提高扫描结果的可靠性,有效降低了扫描结果的误报率。
百度可以检测隐藏的dex文件,但它不能追踪变量,无法处理函数间调用引起的漏洞,对数组下标也不能准确地处理,因此我推测百度的扫描规则是基于危险API所在的函数范围内,一旦超出这个函数,百度的误报率会大大提高。
360扫描结果让人看不明白,分析中所有的应用一旦投入到360,不但扫描时间长,而且结果与其他四家差别很大,所以这里不对360的扫描能力做推测。
金刚和AppRisk的扫描能力相对较差,只能通过简单的特征函数匹配检测漏洞,虽然漏报相对较少,但是误报率比较高。
扫描能力小结
以下表3-9是此次扫描能力的结果:
表3-9 扫描能力总览
阿里聚安全 | 360 | 金刚 | 百度 | AppRisk | |
---|---|---|---|---|---|
自动化脱壳 | √ | 未知 | × | √ | × |
静态-检测隐藏Dex | √ | × | × | √ | × |
静态-过程间分析 | √ | × | × | × | × |
静态-较复杂漏洞 | √ | × | × | × | × |
静态-逆向分析 | √ | √ | √ | √ | √ |
动态-空Intent Fuzz | √ | × | √ | × | √ |
动态-综合静态分析 | √ | × | √ | × | × |
动态-复杂对象Fuzz | √ | × | √ | × | × |
*需要注意的是, 360一直没有测试APP的扫描结果,我只好把每个检测代码打包成APP进行测试,然后进行统计,因此关于360的测试结果可能有误差。
除了扫描能力以外,最后一个维度会以之前的4个第三方APP的测试结果作为对比。为了说明各个扫描平台实际扫描漏洞的能力,我将WiFi万能钥匙、墨迹天气、手机百度以及新浪微博上传到五家扫描平台。最后将以WiFi万能钥匙的扫描结果为例,详细分析一下各个平台的扫描结果的漏报和误报,从而评估其扫描结果的可信性。