上一篇中通过对阿里聚安全[1]、360App 漏洞扫描[2]、腾讯金刚审计系统[3]、百度移动云测试中心[4]以及AppRisk Scanner[5] 在收费情况、样本测试后的扫描时间对比和漏洞项专业对比后,本篇将以各个厂商的扫描能力作为分析维度展开。
使用自己编写的测试 APP 测试各个扫描平台的扫描能力。这些扫描能力主要分为静态检测能力和动态检测能力。静态检测能力包括检测隐藏 dex 、过程间分析、较复杂漏洞检测正向分析、逆向分析;动态测试主要是指测试拒绝服务漏洞的能力,拒绝服务漏洞又可以划分为:空 Intent 引起的拒绝服务,强制类型转换引起的拒绝服务以及序列化对象导致的拒绝服务。由于这些检测能力决定了扫描器扫描结果的精度和准度,因此我详细分析了各个扫描平台的扫描能力。
目前很多 APP 通过加壳来防止自己被反编译反汇编,而扫描器都是通过在反编译反汇编的代码中进行漏洞的扫描。如果扫描器不能自动化地脱去 APP 加的壳,则根本无法进行有效的漏洞扫描分析。我写了一个包含五个扫描平台都有的全局文件读写漏洞的 demo ,通过梆梆加固之后,重签名上传到这五个扫描平台,检测结果是:阿里聚安全和百度检测出全局文件读写漏洞,而金刚、 AppRisk 没有检测出该漏洞。这个 demo 在 360 中没有扫描结果,所以 360 的脱壳能力不得而知。
目前插件化已经在 Android 开发中越来越普遍。很多 APP 会将一些独立模块打包成单独的 dex 文件一些病毒会将恶意的行为打包成 dex 文件,并存储到 apk 的其他目录中,如 asset 、 lib 等。如果扫描器没有检测隐藏 dex 文件的能力,则可能会漏报一些安全风险恶意行为,造成扫描结果不准确。我编写了一个 asset 目录包含 dex 文件的应用程序,分别上传到上述五个扫描器,该 dex 文件中包含五家扫描器都可以检测的漏洞,结果只有阿里聚安全和百度成功扫描出隐藏 dex 文件中包含的漏洞。因此,可以推测阿里聚安全和百度具有扫描隐藏 dex 文件的能力,而 360 、金刚、百度和 AppRisk 都没有检测隐藏 dex 文件的能力。
五家扫描器都可以检测全局文件读写漏洞,因此我用该漏洞测试扫描器对过程间分析的能力。
openFileOutput 的第二个参数可以指定文件打开的方式,如果以全局可写的方式打开会导致安全风险。这里我构造了两个测试例子。
例一, 直接对 openFileOutput 的第二参数设置全局可写,因此有漏洞。
例二, 通过函数的参数传递对 openFileOutput 的第二参数设置全局可写,也应该有漏洞。
测试代码如下:
样本一:函数内设置危险变量 Context.MODE_WORLD_WRITEABLE
样本二:函数间设置危险变量 Context.MODE_WORLD_WRITEABLE
样本一和样本二可以测试扫描器对过程间分析的检测能力。
检测结果如表 3-6 所示(“√”表示扫描结果正确,“×”表示扫描结果错误)
阿里聚安全可以检测出样本一和样本二,而 360 、金刚、百度和 AppRisk 都只能检测出样本一。
由此可以推测, 360 、金刚、百度和 AppRisk 都只能在过程内进行检测,也就是在函数内进行检测,阿里聚安全可以在过程间进行检测。
目前漏洞扫描规则大部分是通过定位关键函数,根据关键函数的参数确定是否会触发漏洞。这是典型的逆向分析问题,可以说逆向分析能力很大程度决定了扫描器检测漏洞的能力。这五家扫描器都有逆向分析的能力,只是逆向分析的能力有些差别。通过扫描器对全局文件读写的代码检测结果分析扫描器逆向分析的能力。
根据全局文件读写漏洞的检测规则,扫描器首先会定位 openFileOutput 函数,追踪该函数的第二个参数,即打开的模式。打开模式都存储在一个数组中。数组中下标为 0 的模式没有漏洞,而下标为 1 的有漏洞。如果扫描结果正确,则说明扫描器的逆向分析能力较强,可以深入到数组等较为复杂的结构中;如果扫描结果有错误,则说明扫描器的逆向分析能力较差,无法逆向追踪到复杂的数据结构中,漏报的可能性较大。
将上述测试代码上传到五家扫描平台,扫描结果如下图所示。“√”表示扫描结果正确,“×”表示扫描结果错误。
通过扫描结果可以看到,阿里聚安全正确地扫描出两个样本,而 360 、金刚、百度和 AppRisk 都只扫描出样本一。因此可以说阿里聚安全的逆向扫描能力要强于其他四家,当逆向追踪的变量进入一个数组时,阿里聚安全可以继续在数组中进行逆向分析,而其他四家扫描器无法确定数组中各个位置代表的具体值。
我猜测当其他四家扫描器检测全局文件读写漏洞时,首先会定位 openFileOutput 函数,由于打开方式是由数组中的元素决定,所以 360 、金刚、百度和 AppRisk 无法确定该值具体是多少,因此也就无法判断是否存在全局文件读写漏洞。本着减少误报的原则,它们都认为不存在漏洞,所以很幸运,样本一不存在漏洞,它们的检测结果正确;样本二存在漏洞,它们的检测结果错误。
为了测试扫描器检测是否能检测出由多个条件组合起来判断的漏洞为了测试扫描器的正向分析能力,我选取了通过 Intent Scheme URL 漏洞进行对比[ 61 ],如果想避免 Intent Scheme URL 漏洞, parseUri 函数得到的 Intent 必须要设置三个条件(addCategory("android.intent.category.BROWSABLE"), setComponent(null), setSelector(null) 才能保证漏洞不会发生。
我构造了三个例子进行测试。
例一,三个条件都满足,因此没有漏洞的。
例二,缺少了条件 setSelector(null),存在 Intent Scheme URL 漏洞。
例三,虽然三个条件都满足,但因为没有 startActivity 所以也不应该被检测出来。
但由于 360 和百度不支持该扫描项,还需要使用另一种漏洞比较 360 、百度在正向分析能力上的差异。 构造如下测试代码:
代码中一共有三个 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 未移除隐藏接口漏洞,我我们推测百度没有追踪变量的能力,而仅仅是进行函数匹配。而阿里聚安全可以追踪 mWebView 这个变量,并记录这个变量所做的操作,在该例中可以记录 mWebView 显式的移除了三个可能引起远程代码执行的接口,当 mWebView 调用了 loadUrl 时,阿里聚安全可以确定该调用不存在隐藏接口未移除的漏洞。
一些运行时漏洞,如拒绝服务,只有在程序运行时才有可能触发。如果扫描器没有动态检测的能力,则会漏报一些运行时漏洞。为了检测扫描器是否有动态扫描的能力,我在测试 APP 中包含 4 处拒绝服务漏洞的代码,分别是空 Intent 拒绝服务 2 个、 1 个强制类型转换拒绝服务和 1 个对象序列化拒绝服务。扫描结果如下表所示。
从表 3-8 中可以看出,阿里聚安全可以扫描出所有的拒绝服务漏洞,金刚可以扫描出 3 处拒绝服务漏洞,漏报一处拒绝服务代码如下:
而 360 、百度和 AppRisk 没有扫描出拒绝服务漏洞。从这个例子我我们推断除阿里聚安全和金刚外,其他扫描平台没有动态检测能力。
综上所述,阿里聚安全的综合检测能力最高,它不仅可以检测隐藏 dex ,对数组下标敏感,还可以检测函数相互调用引起的漏洞。除此之外,阿里聚安全还可以追踪变量,记录变量的一系列操作,当变量作为 sendMessage 的参数被 Handler 发送出去时,阿里聚安全还可以追踪到相应的处理函数中继续追踪;当变量作为 Intent 携带的参数跳转到其他组件中时,阿里聚安全还可以到对应的组件中继续追踪该变量。对变量的有效跟踪可以大大提高扫描结果的可靠性,有效降低了扫描结果的误报率。
百度可以检测隐藏的 dex 文件,但它不能追踪变量,无法处理函数间调用引起的漏洞,对数组下标也不能准确地处理,因此我们推测百度的扫描规则是基于危险 API 所在的函数范围内,一旦超出这个函数,百度的误报率会大大提高。
360 扫描结果让人看不明白,分析中所有的应用一旦投入到 360 ,不但扫描时间长,而且结果与其他四家差别很大,所以这里不对 360 的扫描能力做推测。
金刚和 AppRisk 的扫描能力相对较差,只能通过简单的特征函数匹配检测漏洞,虽然漏报相对较少,但是误报率比较高。
以下表 3-9 是此次扫描能力的结果
需要注意的是, 360 一直没有测试 APP 的扫描结果,我只好把每个检测代码打包成 APP 进行测试,然后进行统计,因此关于 360 的测试结果可能有误差。
除了扫描能力以外,最后一个维度会以之前的 4 个第三方 APP 的测试结果作为对比。为了说明各个扫描平台实际扫描漏洞的能力,我将 WiFi 万能钥匙、墨迹天气、手机百度以及新浪微博上传到五家扫描平台。最后将以 WiFi 万能钥匙的扫描结果为例,详细分析一下各个平台的扫描结果的漏报和误报,从而评估其扫描结果的可信性。 这部分内容将单独作为下篇进行连载,敬请期待。
[ 1 ] 阿里聚安全 http://jaq.alibaba.com/
[ 2 ] 360APP 漏洞扫描 http://dev.360.cn/mod/vulscan/
[ 3 ] 腾讯金刚审计系统 http://service.security.tencent.com/kingkong
[ 4 ] 百度移动云测试中心 http://mtc.baidu.com/startTest/safe
[ 5 ] AppRisk Scanner https://apprisk.newskysecurity.com
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.