地方商业银行APP安全性分析


发布人:admin分类:网络安全浏览量:52发布时间:2017-12-12

本文原创作者:bt0sea

0×00、业务需求

国内133家地方商业银行作为商业领域国外IT厂商和商家必争之地,无论是IT基础设施建设、容灾备份系统建设、还是信息安全建设等,各家银行都做的如火如荼,当然,目前只针对企业级市场,很少有银行愿意接受安全众测,原因很简单,测试过程不可控。

但是IT环境变化的加速度在不断的增快,面临目前严重的安全态势,希望银行也能接受众测这种商业模式,或者由众测小伙伴把自己的测试经验写成PoC代码的形式share给厂商。

测试对象:133家商业银行list 

0×01、测试流程方法

针对银行的业务接口,为了适应目前移动互联网业务需求,各个地方商业都相继推出了自家的银行App。但是对于这种新的商业模式和业务接口,你们的App安全做好接受国内上万白帽子的众测的准备了么?

对于银行类目前有相关业务审核标准。银联的移动终端支付应用软件安全规范应该算是其中之一。

那么,针对上述宽泛的规定,需要具体化App测试方向。

下面我以北京银行android客户端为例进行分析。

No.1、源代码安全

(1)、反编译源码

首先通过Android killer反编译 bob.apk

在编译的过程中出现多次报错。同时,android killer没有识别到是哪家APP加固程序(以前分析其他工程的时候 可以识别梆梆安全、爱加密)所以,只能考虑其他办法识别,那么,加固的核心是处理dex文件,那好,进入虚拟机中找到com.rytong.bankbj的libDexhelper.so文件,看看有什么特征关键字没?

通过nox_adb.exe pull /data/app-lib/com.rytong.bankbj-1/libDexHelper.so 导入到本地IDA Pro中查看。

很明显是阿里云的APP加固(aliyun)。 目前没有很好的脱壳工具,,只能是看有限的java和smali代码。来分析银行业务流程,当然,你可以直接调用这个lib中的方法直接解原始dex,不过需要研究其参数等,还有人家存在的一些防盗机制。(随着分析的深入,发现反编译的代码虽然经过混淆,但是基本上了解其原理够用)

(2)、硬编码查询

思路:查询AndroidManifest.xml

1.1、AndroidManifest.xml

高德地图API调用

<meta-data android:name="com.amap.api.v2.apikey"
android:value="261b11c16677e0738b32cfcf3c4d3bc8" />

1.2、asset相关资料

asset\config.txt 暴露和安全相关的信息

开发模式下联网获取css、lua和image的接口GET_RESFILE_URI  /test_s/get_page? //启动时,通过联网下载最新的xhtml文件到手机本地,通过lua加载。SESSION_KEY  phone/get_keys? //在后面分析通讯秘钥中,产生session key时使用,通过手机号产生session key

加密信道当前版本tls_version  1.3 //在后面分析通讯秘钥中,判断tls版本用,对https验证要求很高。

dynamickey  /user_s/dynamickey? 一字一密接口,EULA.txt:Beijing RYTong Information Technology Co.LTD http://rytong.net/ 融易通开发的。

1.3、APPKEY/APP_KEY/apikey

(1)XMPP Apikey

package com.rytong.push;

    Log.i(a, "apiKey=" + this.f);
    Log.i(a, "xmppHost=" + this.g);
    Log.i(a, "xmppPort=" + this.h);

通过logcat查询到:

I/Androidpn_s( 2508): Callback Activity...
I/Androidpn_s( 2508): apiKey=bjpush
I/Androidpn_s( 2508): xmppHost=61.135.232.215
I/Androidpn_s( 2508): xmppPort=5222

(2)session apikey

package com.rytong.track.control;
  static
  {
    APPLICATION_CONTEXT = null;
    SEND_REPORT = null;
    APIKEY = "26981JUU";
    TRACK_ADDRESS = "http://192.168.64.25:4005/stats/collect";
    TRACK_DB_NAME = "trackdatabase.db";
    TRACK_DB_VERSION = 1;
    DB_MANAGER = null;
    SESSION_TIMEOUT = 5;
    SEND_TIME_INTERVAL = 30;
    OUTPUT_ERROR = true;
    OUTPUT_DEBUG = true;
    SERVICE_NAME = "TrackService";
    APP_INFO = "";
    LOCATION_TIME = 5000;
  }
}

(3)第三方SDK

androidpn做消息推送 是基于XMPP协议的推送,建议更改成最新的MQTT协议。因为XMPP推送协议本身没有很好的SSL安全机制,需要自己在上层实现,复杂。当然也可以使用国内互联网厂商的构建的推送服务,例如:极光推送、小米推送等,安全问题有推送服务厂商托管。

No.2、数据存储安全

这部分主要是关注Shared_prefs\sqlite database\Cookies webview会话\Token本地存储。运行夜神android模拟器。

(1)查看Shared_prefs相关内容

这里我要吐个槽了,北京银行怎么还用xmpp协议做消息推送呢?让我想起来前段时间被破解的京东智能家电的洗衣机的破解案例,协议本身没有任何加密。而且服务地址暴露,很容易被入侵,然后列举出来有多少客户端DeviceID在xmpp服务器上,并且可以伪造钓鱼推送消息。(后来查找源代码,发现XMPP使用Smack API调用,同时又证书调用,暂时是安全的)当然在这里没有发现有价值的泄露数据。

不过结合阅读混淆的反编译源代码发现:

源代码位置:com.rytong.push.u.class.  xmpp_password使用DESCipher做的加密,那也可以DESCipher解密函数实现解密。

(2)查看Sqlite database相关内容

通过nox_adb.exe pull /data/data/com.rytong.bankbj/database/database.sql 导入到本地SQLite Database browser中查看

这个就有点说不过去了,你不能把中国银行的账号明文存储吧?要不加密sqlite数据库,要么加密存储在数据库中的字段。

(3)Cookies webview会话

在windows上建立drozer环境检查,>nox_adb.exe forward tcp:31415 tcp:31415 //在PC上使用adb进行端口转发,转发到Drozer使用的端口31415,选择Drozer>Embedded Server>Enabled //在Android设备上开启Drozer Agent >C:\Program Files\drozer>Drozer.bat console connect //在PC上开启Drozer console

运行 app.package.attacksurface com.rytong.bankbj

运行 app.activity.info -a com.rytong.bankbj

看到这里,大家都明白了,已经没有利用的可能性,PASS。

No.3、数据传输安全

(1)通讯秘钥分析

先访问一下https://ebank.bankofbeijing.com.cn/perbank/eAccount.do  查看一下网站的CA证书。

没有坏到使用自签名证书。

但是,经过Fiddler代理抓包发现无法得到HTTPS的流量。

然后在反编译源代码中搜索 X509TrustManager 、HostnameVerifier 、 setHostnameVerifier、ALLOW_ALL_HOSTNAME_VERIFIER等关键字搞清楚其生成证书的流程:

研究一:Say Hello, 验证服务端签名

package com.rytong.tools.clienthello;

public ClientHello(Activity paramActivity, String paramString1, String paramString2, String paramString3, String paramString4, String paramString5, String paramString6, String paramString7)
  {
    this.c = "/user/make_cert?"; //服务器端生成证书的URI
    this.d = "sc.dat";
    this.g = "serverTowwaySign";
    a(paramActivity);
    b(paramActivity);
    jdField_a_of_type_AndroidAppActivity = paramActivity;
    jdField_e_of_type_JavaLangString = paramString1;
    f = paramString2;
    CERTIFICATEPATH = paramString6; //证书路径:/data/data/com.rytong.bankbj/files
    CLIENT_HELLO = paramString3;
    CLIENT_KEY_EXCHANGE = paramString4;
    CLIENT_FACILITY_HELLO = paramString5;
    byte[] arrayOfByte1 = readServerRandom2(paramActivity);
    byte[] arrayOfByte2 = readServerCertificate(paramActivity, "");
    if ((arrayOfByte1 != null) && (arrayOfByte1.length > 0) && (arrayOfByte2 != null) && (arrayOfByte2.length > 0))
    {
      this.jdField_a_of_type_JavaSecurityCertX509Certificate = a(arrayOfByte2);
      RNS_ = arrayOfByte1;
      this.jdField_e_of_type_ArrayOfByte = arrayOfByte1;
      b(paramActivity, paramString1, paramString2, paramString7);
      return;
    }
    a(paramActivity, paramString1, paramString2, paramString7);
  }


研究二、AES 密钥交换( rsa + base64 + hmac)

Hmac 是本机mac地址做相应的处理,从代码来看是做过MD5和SHA1的加密处理。

package com.rytong.app.emp;

  final String a(String paramString1, String paramString2, WaitDialog.Task paramTask)
  {
    int i1 = 1;
    String str1;
    if ((paramString2 == null) || (paramString1 == null))
    {
      str1 = paramString2;
      return str1;
    }
    String str2;
    if (paramString1.equals("01")) {
      str2 = paramString2;
    }
    for (;;)
    {
      if ((i1 != 0) && (this.s == null) && (this.t == null))
      {
        byte[] arrayOfByte3;
        if (ClientHello.mTlsVersion <= ClientHello.TLS_VERSION_1_3) //检查tls 1.3版本
        {
          byte[] arrayOfByte7 = Utils.getClientGMTUnixTime(); //android客户端时间
          byte[] arrayOfByte8 = ClientHello.getClientRandom(28); //
          byte[] arrayOfByte9 = new byte[32];
          System.arraycopy(arrayOfByte7, 0, arrayOfByte9, 0, arrayOfByte7.length);
          System.arraycopy(arrayOfByte8, 0, arrayOfByte9, arrayOfByte7.length, arrayOfByte8.length);
          String str3 = Base64.encode(arrayOfByte9);
          String str4 = (String)new HttpManager(this).sendPostRequest(Utils.getConfigStringFormAsset(this, "SERVER_URI") + Utils.getConfigStringFormAsset(this, "SESSION_KEY"), "clientrandom=" + Utils.escapeURIComponent(str3), paramTask, null, null);
          if (str4 == null)
          {
            throw new NullPointerException(Utils.getConstantStringFromAsset("ERR_ENCRYPT_SERVER_01"));
            if (paramString1.equals("A0"))
            {
              str2 = hsmcli.PkEncryptAPin(paramString2);
              i1 = 0;
              continue;
            }
            if (paramString1.equals("A1"))
            {
              str2 = hsmcli.PkEncryptAPin(paramString2);
              continue;
            }
            if (paramString1.equals("E0"))
            {
              str2 = hsmcli.PkEncryptEPin(paramString2);
              i1 = 0;
              continue;
            }
            if (paramString1.equals("E1"))
            {
              str2 = hsmcli.PkEncryptEPin(paramString2);
              continue;
            }
            if (paramString1.equals("AE0"))
            {
              str2 = hsmcli.PkEncryptAPin(paramString2) + hsmcli.PkEncryptEPin(paramString2);
              i1 = 0;
              continue;
            }
            if (!paramString1.equals("AE1")) {
              break label616;
            }
            str2 = hsmcli.PkEncryptAPin(paramString2) + hsmcli.PkEncryptEPin(paramString2);
            continue;
          }
          if (str4.indexOf("<error") != -1)
          {
            str1 = Utils.getXMLResponseAttribute(str4, "string=\"", 0, &#039;"&#039;);
            if ((HttpManager.isTimeout) || (HttpManager.isAppUpgrading)) {
              break;
            }
            throw new Exception(str1);
          }
          byte[] arrayOfByte10 = Base64.decodeToBytes(Utils.getXMLResponseAttribute(str4, "serverrandom=\"", 0, '"'));
          arrayOfByte3 = new byte[32 + arrayOfByte10.length];
          System.arraycopy(arrayOfByte9, 0, arrayOfByte3, 0, 32);
          System.arraycopy(arrayOfByte10, 0, arrayOfByte3, 32, arrayOfByte10.length);
        }
        for (;;)
        {
          byte[] arrayOfByte4 = HMac.PRF(ClientHello.PMS2_, HMac.TLS_ONCE_SECRET_CONST(), arrayOfByte3, 48);
          byte[] arrayOfByte5 = ClientHello.getAESKey(arrayOfByte4);
          byte[] arrayOfByte6 = ClientHello.getAESIv(arrayOfByte4);
          this.s = arrayOfByte5;
          this.t = arrayOfByte6;
          return Base64.encode(AESCipher.encrypt(str2.getBytes("UTF-8"), arrayOfByte5, arrayOfByte6));
          byte[] arrayOfByte1 = ClientHello.RNS_;
          byte[] arrayOfByte2 = ClientHello.getClientRandom(32);
          HttpManager.onceRNC = arrayOfByte2;
          arrayOfByte3 = new byte[arrayOfByte1.length + arrayOfByte2.length];
          System.arraycopy(arrayOfByte2, 0, arrayOfByte3, 0, arrayOfByte1.length);
          System.arraycopy(arrayOfByte1, 0, arrayOfByte3, arrayOfByte1.length, arrayOfByte2.length);
        }
      }
      if ((this.s != null) && (this.t != null)) {
        return Base64.encode(AESCipher.encrypt(str2.getBytes("UTF-8"), this.s, this.t));
      }
      return str2;
      label616:
      str2 = paramString2;
      i1 = 0;
    }
  }

(2)Token分析

大家可以看出来:

POST http://newbank.95526.mobi:80/map/get_pic?app=ebank&o=i HTTP/1.1
Host: newbank.95526.mobi:80
User-Agent: 北京银行 2.1.1 (iPhone; iPhone OS 8.4.1; zh_CN)
Connection: keep-alive
Accept-Encoding: gzip
Content-Length: 204
Cookie: _session_id=8aefb42a84c3a5741abf90095184bd86;
Connection: keep-alive
X-EMP-Signature: KiDQb3Eja8yoOrYMyb2Z2fPwauQ=
X-Emp-Cookie: _session_id=8aefb42a84c3a5741abf90095184bd86;

这个是报文签名,每次都不一样,大家就别想破解了,PASS。。

No.4、安全增强测试

(1)自绘键盘分析

银行应用有一个特殊的功能,那就是需要输入银行的账号和密码,而且银行密码都是六位数字,如何解决其安全问题,一般都使用自绘键盘处理输入的密码过程。

首先先检测一下是否为目前最安全的动态自绘键盘。

看上面两张图,会发现键盘数字出现的位置都不一样,那么证明为自绘随机键盘,

通过源代码可以搜索,keyboardView、InputMethodManager查找其编程逻辑。

攻击方式检测:

安全评估:没有使用第三方键盘SDK做安全加固,获取其保存内容不是无可能。建议使用专业的专业SDK键盘。

(2)So文件分析

AppVerify.So:联网校验所有app的文件是否被篡改。

程序入口:JNI_Onload()

(1)加载com/rytong/tools/crypto/AppVerify.So 文件

(2)调用GetApkPath(_JNIEnv *,std::string,_jobject *)

(3)调用VerifyHash(_JNIEnv *,char *,char const*,int)

(4)_Z10VerifyHashP7_JNIEnvPcPKci

  Java_com_rytong_tools_crypto_AppVerify_verifyHashByC (使用SHA1算法校验)

(5)调用update 更新

(6)GetApkMFData =》加载META-INF/MANIFEST.MF

对所有文件做完整性校验很必要,防止二次打包,好思路。

0×02、安全建议

本银行程序最核心的安全机制是使用RSA非对称加密算法+本地HMAC,充分保障了SSL数据传输安全,当然,没有使用xposed hook通讯协议的测试方法验证其安全性。由于调用xposed需要重新编译apk加log看数据结果,比较复杂下期再说。

当然也要做出以下几条:

(1)开发者遵守App的安全开发代码规范。

(2)使用成熟安全组件、如:软键盘、清场,特别是推送服务不建议使用xmpp。

(3)定期对客户端进行安全评估。(众测模式)。

(4)有必要做App安全加固(建议使用传统安全厂商)、阻止代码反编译、阻止APP运行时被动态注入。

* 作者:bt0sea,本文属FreeBuf原创奖励计划文章,


被黑站点统计 - 文章版权1、本主题所有言论和图片纯属会员个人意见,与本文章立场无关
2、本站所有主题由该文章作者发表,该文章作者与被黑站点统计享有文章相关版权
3、其他单位或个人使用、转载或引用本文时必须同时征得该文章作者和被黑站点统计的同意
4、文章作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
7、被黑站点统计管理员有权不事先通知发贴者而删除本文

免责声明

本站主要通过网络搜集国内被黑网站信息,统计分析数据,为部署安全型网络提供强有力的依据.本站所有工作人员均不参与黑站,挂马或赢利性行为,所有数据均为网民提供,提交者不一定是黑站人,所有提交采取不记名,先提交先审核的方式,如有任何疑问请及时与我们联系.

admin  的文章


微信公众号

微信公众号


Copyright © 2012-2022被黑网站统计系统All Rights Reserved
页面总访问量:22062424(PV) 页面执行时间:132.744(MS)
  • xml
  • 网站地图