App安全测试指南(二)——组件安全

4、组件安全:

本项主要测试客户端是否包含后台服务、Content Provider、第三方调用和广播等组件,Intent 权限的设置是否安全。应用不同组成部分之间的机密数据传递是否安全。检查客户端是否存在组件劫持风险,查看客户端程序具有导出哪些应用信息的权限。反编译 APK 文件后,检查 AndroidManifest 文件中是否有多余的 android:export 声明,客户端是否存在导出其他应用信息的权限等

应用组件是组成安卓应用的关键部分。每个应用都是由一个或者多个组件组成,并且每个都是单独调用。这里主要分为4个主要的组件:

1、Activity:在屏幕上提供一个区域,提供一个可视化界面供我们点击,访问。
2、Service:服务是一种在后台运行的组件,用于执行长时间运行的操作或为远程进程执行作业。相当于你在听歌的时候,退出界面后,歌还继续放着,这就是服务的作用
3、content provider:简单的来说就是管理数据的一个程序,除了放在SD卡里的数据,手机原本的或者各个程序之间的数据都是很封闭的,但是他们又不能完全封闭,因此使用内容提供程序进行封装,一般用sqlite进行报存数据。以表格的形式把数据展现给外部的应用。换句话来说Content Providers可以被认为是连接两个处理器的之间的接口
4、BroadcastReceiver:其实就是广播,它们可以创建状态栏通知,在发生广播事件时提醒用户,就像你手机里时不时会从屏幕上方发来一条推送,这就是这个组件最重要的功能。广播接收器是一个接受系统级广播的组件,(例如电量低,重启,耳机插入等)尽管大部分广播都是由系统发出,应用本身也可以发起广播

小结一下,安卓系统的其四大组件的主要用途分别为:

  • 活动(Activity):呈现可供用户交互的可视界面,是最常见的组件;
  • 服务(Service):长时间执行后台作业,这些任务可以包括从 HTTP 下载文件到在后台播放音乐的任何内容,常见于监控类应用;
  • 内容供应器(Content Provider):在多个 APP 间共享数据,比如通讯录数据;
  • 广播接收器(Broadcast Receiver):通过 Android 系统或设备中存在的其他应用程序,监听传入的广播消息。一旦它们接收到广播消息,就可以根据预定义的条件触发特定动作。条件可以为收到 SMS,来电呼叫,电量改变等等。注册特定事件,并在其发生时被激活;

打开AndroidManifest.xml,查看对其中声明的各个组件,根据以下规则判断是否可导出:

  1. 显式声明了 android:exported="true",则可导出;
  2. 显示声明了 android:exported="false",则不可导出;
  3. 未显示声明 android:exported:
    a) 若组件不是 Content Provider:
    若组件包含<intent-filter>则可导出,反之不可;
    b) 若组件是 Content Provider:
    若 SDK 版本<17 则可导出,反之不可。

如果 Android API 版本低于 17,则内容供应器的默认属性是始终导出。 这意味着除非开发人员指定权限,否则任何应用程序都可以使用应用程序的内容供应器,来访问和查询数据。 所有内容供应器都需要在AndroidManifest.xml中注册。 因此,我们可以对应用程序使用 Apktool,并通过查看AndroidManifest.xml文件检查内容供应器。

从测试的角度上,只能判断组件是否导出,但能否构成危害需要详细分析源代码后才能
得出结论。一般来说,在测试时尽管写清所有的导出组件,由客户开发侧确认相关组件
是否确实需要导出即可

由于功能需要,启动 Activity 和 Content Provider 大多是导出组件,一般无须理会

并不是说所有导出的组件都是不安全的,如果要确定,必须看代码,对代码逻辑进行分析
一般直接用drozer进行测试即可

  • activity 组件:

主要测试在未经登录的情况下,该 activity 是否被正常显示,且是否会造成越权、信息泄露等风险
如果存在,停止测试,记录漏洞

  • Service

Service 是没有界面且能长时间运行于后台的应用组件.其它应用的组件可以启动一个服务运行于后台,即使用户切换到另一个应用也会继续运行.另外,一个组件可以绑定到一个 service 来进行交互,即使这个交互是进程间通讯也没问题.例如,一个 service 可能处理网络事物,播放音乐,执行文件 I/O,或与一个内容提供者交互,所有这些都在后台进行

检查有没有 export 的 Service:
检查 AndroidManifest.xml 文件中注册的 Service(MobSF)
检查有没有 export 的Service 查看 service 类,重点关注 onCreate / onStarCommand / onHandleIntent 方法

检测是否有数据泄露的可能:
检测有没有处理不当的方法检索所有类中 startService/bindService 方法及其传递的数据

  • Broadcast Reciever:

广播接收器是一个专注于接收广播通知信息,并做出对应处理的组件。很多广播是源自于系统代码的──比如,通知时区改变、电池电量低、拍摄了一张照片或者用户改变了语言选项。应用程序也可以进行广播──比如说,通知其它应用程序一些数据下载完成并处于可用状态。应用程序可以拥有任意数量的广播接收器以对所有它感兴趣的通知信息予以响应。所有的接收器均继承自 BroadcastReceiver 基类。广播接收器没有用户界面。然而,它们可以启动一个 activity 来响应它们收到的信息,或者用 NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

查找静态 Broadcast Receiver:
检查 AndroidManifest.xml 文件中注册的 Broadcast Receiver

查找动态 Broadcast Receiver:

run app.broadcast.info -a APP 包名

查找发送广播内的信息检索 sendBroadcast 与 sendOrderedBroadcast:
注意检索 setPackage 方法与 receiverPermission 变量

run app.broadcast.send --component com.package.name --action android.intent.action.XXX

检测是否存在信息泄露或拒绝服务的问题
查看 onReceive 方法

测试方法:

1、反编译后检索 registerReceiver(),查找动态广播接收器,或者直接使用drozer:run app.broadcast.info -a APP 包名
2、尝试向应用程序的 receiver 组件发送空值,am broadcast -a MyBroadcast -n
com.isi.vul_broadcastreceiver/.MyBroadCastReceiver,查看是否能够造成应用程序崩溃,
形成拒绝服务

  • Content Provider:

许多应用程序使用内容供应器来存储和查询应用程序中的数据或来自电话的数据。 除非已经定义了内容提供者可以使用权限来访问,否则任何其他应用都可以使用应用所定义的内容供应器,来访问应用的数据。 所有内容供应器具有唯一的统一资源标识符(URI)以便被识别和查询。 内容提供者的 URI 的命名标准惯例是以

Android Content Provider 存在文件目录遍历安全漏洞,该漏洞源于对外暴露 Content Provider 组件的应用,没有对 Content Provider 组件的访问进行权限控制和对访问的目标文件的 Content Query Uri 进行有效判断,攻击者利用该应用暴露的 Content Provider的 openFile()接口进行文件目录遍历以达到访问任意可读文件的目的。在使用 Content Provider 时,将组件导出,提供了 query 接口。由于 query 接口传入的参数直接或间接由接口调用者传入,攻击者构造 sql injection 语句,造成信息的泄漏甚至是应用私有数据的恶意改写和删除。

静态检测:

1、查看 AndroidManifest.xml 文 件 , 定 位 各 Provider , 尤 其 是 设 置 了
android:exported="true"的
2、在反编译后使用关键字:addURI 查找

动态检测:

主要通过drozer测试是否存在URI数据泄露、目录遍历、SQL注入风险

如果能够正确访问到共享资源,并且具备敏感信息,则记录漏洞。

  • .Intent 本地拒绝服务检测:

Android 应用本地拒绝服务漏洞源于程序没有对 Intent.getXXXExtra()获取的异常或者畸形数据处理时没有进行异常捕获,从而导致攻击者可通过向受害者应用发送此类空数据、异常或者畸形数据来达到使该应用 crash 的目的,简单的说就是攻击者通过 intent 发送空数据、异常或畸形数据给受害者应用,导致其崩溃

通过使用 drozer 工具查看对外暴露组件的应用如下:

run app.activity.info -a com.mwr.example.sieve
grep -rn "get*Extra" ./ | more #(在导出组件代码中测试) -检测在获取 intent 数据时是否进行了异常处理

测试方法:

1、使用反编译工具打开应用,反编译出应用源码
2、在源码中查找以下示例源码(主要查找getAction):

Intent i = new Intent();
if (i.getAction().equals("TestForNullPointerException")) {
 Log.d("TAG", "Test for Android Refuse Service Bug");
}

3、如出现像以上代码,getIntent()的intent附带空数据、异常或畸形数据,而且处理
getXXXExtra()获取的数据时没有进行异常捕获,便存在风险。
4、如果服务端出现崩溃界面,则可以证明漏洞存在。记录漏洞,停止测试

同样可以造成本地拒绝服务的有:

ClassNotFoundException异常导致的拒绝服务

源于程序没有无法找到从 getSerializableExtra ()获取到的序列化类对象的类定义,因此发生类未定义的异常而导致应用崩溃。
漏洞应用代码片段:

Intent i = getIntent();
getSerializableExtra("serializable_key");

攻击应用代码片段:

Intent i = new Intent();
i.setClassName("com.alibaba.jaq.pocforrefuseservice", 
"com.alibaba.jaq.pocforrefuseservice.MainActivity");
i.putExtra("serializable_key", BigInteger.valueOf(1));
startActivity(i);

IndexOutOfBoundsException异常导致的拒绝服务:

源于程序没有对 getIntegerArrayListExtra()等获取到的数据数组元素大小的判断,从而导致数组访问越界而导致应用崩溃
漏洞应用代码片段:

Intent intent = getIntent();
ArrayList<Integer> intArray = intent.getIntegerArrayListExtra("user_id");
if (intArray != null) {
 for (int i = 0; i < USER_NUM; i++) {
 intArray.get(i);
 } }

攻击应用代码片段:

Intent intent = new Intent();
intent.setClassName("com.alibaba.jaq.pocforrefuseservice", 
"com.alibaba.jaq.pocforrefuseservice.MainActivity");
ArrayList<Integer> user_id = new ArrayList<Integer>();
intent.putExtra("user_id", user_id);
startActivity(intent);
) 

ClassNotFoundException异常导致的拒绝服务

源于程序没有无法找到从 getSerializableExtra ()获取到的序列化类对象的类定义,因此发生类未定义的异常而导致应用崩溃。
漏洞应用代码片段:

Intent i = getIntent();
i.getSerializableExtra("serializable_key");

攻击应用代码片段:

public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 Intent i = new Intent();
 i.setClassName("com.alibaba.jaq.pocforrefuseservice", 
"com.alibaba.jaq.pocforrefuseservice.MainActivity");
 i.putExtra("serializable_key", new SelfSerializableData());
 startActivity(i);
}
static class SelfSerializableData implements Serializable {
 private static final long serialVersionUID = 42L;
 public SelfSerializableData() {
 super();
 } }

java我几乎不了解,这里先作记录,以后接触java代码审计的时候再来填坑

  • webview 组件安全:

WebView是一种 Android 视图,用于在应用程序中显示 Web 内容。 它使用 WebKit 渲染引擎,以便使用file://和data://协议显示网页和其他内容,可以用于从文件系统加载文件和数据内容。WebView也用于各种 Android 应用程序,例如提供注册和登录功能的应用程序。它通过在应用程序的布局中构建其移动网站,来显示应用程序中的 Web 内容。

  • 禁用对 JavaScript 的支持:

如果没有理由在 WebView 中支持 JavaScript,则应禁用它。Android WebSettings 类可用于通过公共方法 setJavaScriptEnabled 禁用对 JavaScript 的支持。

# 默认情况下这是禁用的
webview = new WebView(this);
webview.getSettings().setJavaScriptEnabled(false);
  • 禁用对插件的支持:

如果不需要支持插件(例如 Flash),则禁用对插件的支持,因为这些插件可以用作利用您的应用程序进程的载体。Android WebSettings 类可用于通过已弃用的公共方法 setPluginsEnabled 禁用对 JavaScript 的支持。

# 默认情况下这是禁用的
webview = new WebView(this);
webview.getSettings().setPluginsEnabled(false);
# 或者
webview = new WebView(this);
webview.getSettings().setPluginState(PluginState.OFF);
  • 禁用文件系统访问:

如果攻击者以某种方式发现自己能够将脚本注入到 WebView 中,那么他们就可以利用这个机会访问本地资源。这可以通过禁用本地文件系统访问来防止。它默认启用。Android WebSettings 类可用于通过公共方法 setAllowFileAccess 禁用本地文件系统访问。

webview = new WebView(this);
webview.getSettings().setAllowFileAccess(false);

这限制了 WebView 从 file:///android_asset(资产)和 file:///android_res(资源)加载本地资源。res/raw 目录中的原始资源和资产之间的区别在于资产的行为类似于文件系统;它们可以像文件一样被列出、迭代和发现。对于原始资源,您需要资源 ID,并且是应用程序包中的自包含资源。

  • 资源检查:

如果有必要在 WebView 中支持 JavaScript 和/或要求允许本地文件系统访问,那么我们需要在如何实现 WebView 以及如何保护它免受攻击者方面具有创造性。除了确保使用加密精明的通道加载远程资源并强制执行服务器证书检查(同时不允许用户覆盖)之外,没有很多可以做来防止中间人攻击。

但是,我们可以尝试将第 3 方内容/资源的加载限制为获得授权的内容/资源。例如,可以通过覆盖本机方法来检查对内容和/或资源的请求。

在面向对象编程中,方法覆盖是一种语言特性,它允许子类或子类提供已由其超类或父类之一提供的方法的特定实现。子类中的实现通过提供与父类中的方法具有相同名称、相同参数或签名以及相同返回类型的方法来覆盖(替换)超类中的实现。用于调用它的对象将确定执行的方法的版本。如果使用父类的对象调用方法,则执行父类中的版本,如果使用子类的对象调用方法,则执行子类中的版本。

如果用户以交互方式从 WebView 中请求资源,则可以通过使用 WebViewClient 类的 shouldOverrideUrlLoading 方法来拦截请求。示例代码如下所示。

private class MyWebViewClient extends WebViewClient {
  @Override
  public boolean shouldOverrideUrlLoading(WebView view, String url) {
    private static final String LOG_TAG = "MWRLabs";
    Log.d(LOG_TAG, "[x] getHost: " + Uri.parse(url).getHost());
    Log.d(LOG_TAG, "[x] getScheme: " + Uri.parse(url).getScheme());
    Log.d(LOG_TAG, "[x] getPath: " + Uri.parse(url).getPath());
    if (Uri.parse(url).getHost().equals("labs.mwrinfosecurity.com")){return true;}
    return false;
  }
}

当一个新的URL即将被加载到当前的 WebView 中时,该方法使宿主应用程序有机会接管控制。返回值 true 表示宿主应用程序处理URL,而 return false 表示当前 WebView 处理URL。上面的代码阻止从主机“labs.mwrinfosecurity.com”加载资源。
但是,该方法不会拦截从内部加载的资源,例如来自HTML或SCRIPT标签内的IFRAME或 src 属性。此外 XmlHttpRequests 也不会被拦截。为了拦截这些请求,您可以使用 WebViewClient shouldInterceptRequest 方法。示例代码如下所示。

@Override
  public WebResourceResponse shouldInterceptRequest (final WebView view, String url) {
    if (url.contains(".js")){return getWebResourceResponseFromString();}
    else {return super.shouldInterceptRequest(view, url);}
  }
  private WebResourceResponse getWebResourceResponseFromString(){
    return getUtf8EncodedWebResourceResponse(new StringBufferInputStream("alert('!NO!')"));
  }
  private WebResourceResponse getUtf8EncodedWebResourceResponse(InputStream data){
    return new WebResourceResponse("text/css", "UTF-8", data);
  }

该方法将资源请求通知主机应用程序并允许应用程序返回数据。如果返回值为空,WebView 将继续像往常一样加载资源。否则,将使用返回响应和数据。上面的代码拦截对 JavaScript 资源 (.js) 的请求并返回警报而不是请求的资源。这是首选方法。

URI可用于解析URL对象并根据各个组件做出决策。使用模式匹配来检查请求并禁止那些匹配黑名单的资源是可行的。还可以确保 WebView 只能加载内容并从白名单中的资源发出请求。

如果没有仔细审查模式匹配实现,并且实现了黑名单,那么攻击者可能会寻找一种方法来通过简单地攻击实现的正则表达式来绕过任何检查来突破限制。最终,在实施此类解决方案时需要非常小心和注意细节。

  • 添加Javascript接口:

开发人员通常喜欢直接从本机代码通过 JavaScript 与他们的 WebView 交互,为此可以实现 Java 到 JavaScript 的桥接。WebKit WebView 类中的addJavascriptInterface函数可用于绑定对象,以便可以从 JavaScript 访问这些方法。这允许从在 WebView 中执行的 JavaScript 调用本机代码,并允许本机 Java 方法在 WebView 中注入/调用 JavaScript 函数。应避免使用它,因为同源策略 ( SOP ) 不适用于网桥,因此即使在另一个域上运行时,也可以从子IFRAME调用任何公开的方法。

至此针对WebKit WebView,总结了这些要点:

  • 不要通过未加密的频道远程加载内容。
  • 确保使用强密码协商SSL / TLS连接。
  • 确保用户无法覆盖和信任自签名或无效的SSL证书。
  • 禁止本地文件系统访问。
  • 禁用 JavaScript 和插件。
  • 拦截尝试加载远程资源并匹配白名单,以试图禁止将未经授权的内容加载到 WebKit WebView 中。
  • 如果可以避免,不要将本机方法暴露给 WebKit WebView。

Android 4.2 版本以下的 webview 组件存在安全漏洞(CVE-2012-6636)。检测客户端是否采取措施避免漏洞被利用。检查应用 AndroidManifest.xml 中的 targetSdkVersion
是否大于等于 17

WebView 代码执行检测:
android系统通过WebView.addJavascriptInterface方法注册可供JavaScript调用的Java对象,以用于增强 JavaScript 的功能。但是系统并没有对注册 Java 类的方法调用的限制。导致攻击者可以利用反射机制调用未注册的其它任何 Java 类,最终导致 JavaScript 能力的无限增强。攻击者利用该漏洞可以根据客户端执行任意代码。

Webview 代 码 执 行 漏 洞 出 现 在 安 卓 2.1~4.3.1 版 本 , 检 查 targetSdkVersion 、minSdkVersion(MobSF)
若 targetsdkVersion>=19 或通过 minSdkVersion 进行限制则无此问题,否则在低版本上测试,(可使用相关检测代码),检查代码中是否使用 addJavascriptInterface()

drozer测试:

run scanner.misc.checkjavascriptbridge -a 包名

模块安装参考:drozer模块的编写及模块动态加载问题研究

测试流程:

1、使用反编译工具打开应用,反编译出应用源码。

2、在源码中搜索类似写法:
settings.setJavaScriptEnabled(true);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
mWebView.addJavascriptInterface(newJSInvokeClass(), "js2java");
3、那么该处就可能存在 Web 组件远程代码执行的风险。
4、如果存在该风险,将会在改页面中显示出存在问题的接口,记录漏洞,停止测试

WebView 不校验证书检测:
Android WebView组件加载网页发生证书认证错误时,会调用WebViewClient类的onReceivedSslError方法,如果该方法实现调用了handler.proceed()来忽略该证书错误,则会受到中间人攻击的威胁,可能导致隐私泄露

调用了 android/webkit/SslErrorHandler 类的 proceed 方法,可能导致 WebView 忽略校验证书的步骤。

全局搜索“onReceivedSslError”,看是否调用了 handle.proceed()方法(在 webview 组件代码中测试)

WebView 密码明文保存检测:
在使用 WebView 的过程中忽略了 WebView setSavePassword,当用户选择保存在WebView 中 输 入 的 用 户 名 和 密 码 , 则 会 被 明 文 保 存 到 应 用 数 据 目 录 的databases/webview.db 中。如果手机被 root 就可以获取明文保存的密码,造成用户的个人敏感数据泄露

搜”setSavePassword”,看是否显式设置为 false(在 webview 组件代码中测试)

修复建议:

使用WebView.getSettings().setSavePassword(false)来禁止保存密码

参考如下:


Adventures with Android WebViews
android app 渗透测试方法大全.pdf
drozer模块的编写及模块动态加载问题研究

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357