移动APP安全漏洞检测与利用

  • 1. 本地文件存储
    • 1.1. 禁止全局读写
    • 1.2. 数据加密保存
  • 2. 安全通信
    • 2.1. 信任任意整证书
    • 2.2. 解决方法
  • 3. 加密
    • 3.1. 敏感逻辑实现
    • 3.2. 硬编码
  • 4. 组件安全
    • 4.1. Activity导出
    • 4.2. Activity劫持
    • 4.3. Service
    • 4.4. Provider
    • 4.5. Receiver
  • 5. WebView组件
    • 5.1. Webview File域同源策略绕过
    • 5.2. WebView隐藏接口问题
    • 5.3. 远程命令执行攻击
    • 5.4. WebView绕过证书校验漏洞
  • 6. 小结

1. 本地文件存储

eg: 数据库
在程序中如果使用了SharedPreference或者SQLiteDatabase,要查看SharedPreference或SQLiteDatabase是否设置了安全权限

1.1. 禁止全局读写

如下代码是不安全的:

try{
            SQLiteDatabase rdb = penOrCreateDatabase("all_r_db",Context.MODE_WORLD_READABLE,null);
            SQLiteDatabase wdb = penOrCreateDatabase("all_w_db",Context.MODE_WORLD_WRITEABLE,null,null);
}catch(SQLiteException e){
            e.printStackTrace();
}

应改为MODE_PRIVATE模式创建

try{
            SQLiteDatabase rdb = penOrCreateDatabase("all_r_db",Context.MODE_PRIVATE,null);
            SQLiteDatabase wdb = penOrCreateDatabase("all_w_db",Context.MODE_PRIVATE,null,null);
}catch(SQLiteException e){
            e.printStackTrace();
}

1.2. 数据加密保存

敏感数据需要加密后保存,比如身份证号、手机号码、信用卡号、银行卡号等

2. 安全通信

eg:网络通信

2.1. 信任任意整证书

如下代码中程序中有重写TrustManager的证书验证方法,而且checkServerTrusted方法没有真正实现服务器端的证书校验而且代码中这样写:httpsURLConnection.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);,会导致默认信任所有主机。verify方法没有实现校验服务器证书域名是否相符的功能

public static String getUnSafeFromServer (){
    String response="";
    final String https_url ="https://XXXX/";
    new AsyncTask(){
        @Override
        protected Boolean doInBackground (String ... Param){
            try{
                TrustManager [] trustAllCerts = new TrustManager[] {
                    new X509TrustManager (){
                        @Override
                        public void checkClientTrusted (X509Certificate[] X509Certificates,String s) throws CertificateException {
                            
                        }
                        @Override
                        public void checkServerTrusted (X509Certificate[] X509Certificates,String s) throws CertificateException{
                        }
                        @Override
                        public X509Certificate[] getAcceptedIssuers(){
                            return new X509Certificate[0];
                        }
                    }
                };
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null,trustAllCerts,null);
                
                URL url = new URL(https_url);
                HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
                httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
                httpsURLConnection.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
                final HostnameVerifier hostnameVerifier = new HostnameVerifier(){
                    @Override
                    public boolean verify(String s ,SSLSession sslsession){
                        return true;
                    }
                };
.......   
}

2.2. 解决方法

  @Override
              public void checkServerTrusted(X509Certificate[] chain,
                      String authType)
                      throws CertificateException {
                  for (X509Certificate cert : chain) {

                      // Make sure that it hasn't expired.
                      cert.checkValidity();

                      // Verify the certificate's public key chain.
                      try {
                          cert.verify(((X509Certificate) ca).getPublicKey());
                      } catch (NoSuchAlgorithmException e) {
                          e.printStackTrace();
                      } catch (InvalidKeyException e) {
                          e.printStackTrace();
                      } catch (NoSuchProviderException e) {
                          e.printStackTrace();
                      } catch (SignatureException e) {
                          e.printStackTrace();
                      }
                  }
              }

3. 加密

3.1. 敏感逻辑实现

设计要求,需查看工程中是否有使用Native实现的关键敏感逻辑程序

3.2. 硬编码

加密密钥写在了代码里,如果应用被反编译了,很容易导致加密被破解,造成敏感信息泄露

4. 组件安全

安卓平台四大组件Activity,Service,Provider,Receiver均有exported属性设置。

4.1. Activity导出

对于Activity组件,如果有intent filter设置的话,exported默认为true,必须关闭。如果没有则可以不关注是否设置了该Activity组件exported为false,因为默认是false。如果Activity组件有Intent-filter配置,例如XML中







则该Activity组件的相关配置是不安全的,如下配置才是安全的:







举个栗子:2014年的时候华为网盘android客户端本地密码绕过:
启动设置本地的密码的activity:com.huawei.dbank.v7.ui.setting.LocalPassWordInitActivity,即可重置本地密码

4.2. Activity劫持

用户打开安卓手机上的某一应用,进入到登陆页面,这时恶意软件侦测到用户的这一动作,立即弹出一个与该应用界面相同的Activity,覆盖掉了合法的Activity
在建立的正常Activity的登陆界面(也就是MainActivity)中重写onKeyDown方法和onPause方法,这样一来,当其被覆盖时,就能够弹出警示信息

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        //判断程序进入后台是否是用户自身造成的(触摸返回键或HOME键),是则无需弹出警示。
        if((keyCode==KeyEvent.KEYCODE_BACK || keyCode==KeyEvent.KEYCODE_HOME) && event.getRepeatCount()==0){
            needAlarm = false;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onPause() {
       //若程序进入后台不是用户自身造成的,则需要弹出警示
        if(needAlarm) {
            //弹出警示信息
            Toast.makeText(getApplicationContext(), "您的登陆界面被覆盖,请确认登陆环境是否安全", Toast.LENGTH_SHORT).show();
            //启动我们的AlarmService,用于给出覆盖了正常Activity的类名
            Intent intent = new Intent(this, AlarmService.class);
            startService(intent);
        }
        super.onPause();
    }

4.3. Service

对于Service组件,与Activity组件类似,要检查如果有intent filter设置的话,exported默认为true,必须关闭。具体代码示例与验证规则与Activity一致。

4.4. Provider

对于Provider组件,根据使用的Android SDK版本而定,如果版本为16(安卓4.1)或以下版本,则必须有exported的属性设置为false,如果是17(安卓4.2)以上版本则不必须

4.5. Receiver

对于Receiver组件,与Activity组件一致,主要查看是否有Intent filter,如果有则必须检查是否设置该属性为false

5. WebView组件

5.1. Webview File域同源策略绕过

可造成cookie等敏感信息泄露,黑客可利用该漏洞获取FireFox的所有文件。低版本的系统Webview组件也存在同样的漏洞,应用程序使用Webview并支持File域,就会受到该漏洞的攻击
如果程序中有使用WebView,在设置WebView建议关闭file协议:

mWebView.setAllowFileAccess(false)
setAllowFileAccessFromFileURLs(false)
setAllowUniversalAccessFromFileURLs(false)

如果必须使用,则应该禁止调用javascript

webView.getSettings. setJavaScriptEnabled(false);

eg:WebView允许js访问本地文件,并且允许js运行,则可以上传一个含有js代码的文件(代码内容为延迟读取自身)并将目标WebView启动让其读取上传的文件,然后将文件删掉并创建一个软链接到目标APP私有文件,则在延迟时间过后,WebView就会显示读取到的私有文,达成了攻击目的

5.2. WebView隐藏接口问题

如果使用Android4.4之前版本程序使用WebView,需要移除searchBoxJavaBridge_、accessibility、accessibilityTraversal危险接口。这三个接口存在远程代码执行的威胁

5.3. 远程命令执行攻击

13年下半年,Webview曝出了比较严重的远程命令执行漏洞。在webView 下有一个非常特殊的接口函数addJavascriptInterface。能实现本地java和js的交互。利用addJavascriptInterface这个接口函数可实现反射穿透webkit控制android 本机,比如向本机写入文件。技术细节可参考如下文章:《WebView中接口隐患与手机挂马利用》
处理方法
1、不要将不必要组件导出。
2、如需导出,禁止使用File域。
3、如需使用File协议,禁止js执行:setJavaScriptEnabled(False)

5.4. WebView绕过证书校验漏洞

禁止使用proceed()函数忽略证书错误,应该抛给系统进行安全警告。当SSL错误处理时,使用了proceed方法。造成假如SSL证书错误仍然可以访问


示例

6. 小结

  • 本地文件存储:
    对于敏感数据本地存储的要求,其实可以总结为:能不存就不存,非要存的话一定要加密。加密密钥不要硬编码到代码中,最好是获取每个终端的特有信息,如IMEI、mac之类
  • 安全通信
    1.真正实现TrustManager
    2.禁止使用AllowAllHostnameVerifier和ALLOWALLHOSTNAMEVERIFIER实例化,应该使用STRICTHOSTNAME_VERIFIER严格校验。
    3.禁止使用SSLCertificateSocketFactory;->getInsecure()函数禁用所有SSL安全校验。
    4.禁止使用proceed()函数忽略证书错误,应该抛给系统进行安全警告。
  • 安卓四大组件:
    如非必要,exported可以都显示的设置为false,在应用进入后台运行时要弹出提示信息(目前看来这个都有做了,基本上所有银行都会有这个功能),不要在本地做密码验证
  • WebView:
    1、禁止使用addJavascriptInterface方法
    2、设置WebView建议关闭file协议,mWebView.setAllowFileAccess(false)setAllowFileAccessFromFileURLs(false),setAllowUniversalAccessFromFileURLs(false)
    3、移除危险方法removeJavascriptInterface("searchBoxJavaBridge_")、removeJavascriptInterface("accessibility")、removeJavascriptInterface("accessibilityTraversal")
    4、使用handler.cancel()

你可能感兴趣的:(移动APP安全漏洞检测与利用)