搜索
查看: 935|回复: 0

如何突破双向证书认证防护

[复制链接]

1839

主题

2255

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
11913
发表于 2017-1-10 22:25:01 | 显示全部楼层 |阅读模式
以前给一家证券机构做测试,第一次碰到了服务器双向认证的问题,当时双向认证的概念还没有推广开来,所以折腾了很久,虽然当时不知道原理,但是也算是解决了双向认证走代理的问题了。前段时间,碰到了一个apk,想抓包看看数据,发现用的也是双向认证,所以就折腾了一下。
当服务器启用了双向认证之后,除了客户端去验证服务器端的证书外,服务器也同时需要验证客户端的证书,也就是会要求客户端提供自己的证书,如果没有通过验证,则会拒绝连接,如果通过验证,服务器获得用户的公钥。
正式因为如此,双向认证以便都是企业内部或者证券、银行等这类用户使用,而如何保证证书的合法和保密性,就不可能通过一个公开接口去提供给访问者下载,所以一般都是放入usb-key中,或者是提供一个身份认证接口,认证通过后,可以下载安装,但是一般不会如此使用,这样的话,使用者多个电脑都安装的话,其他人也就可以使用了,所以保证唯一性,大部分都会采用usb-key的方式,所以也就限制了双向认证的使用,但是这几年手机端应用的推广,和安全的推进,很多企业在apk中直接封装了客户端的证书,使得我们想对app基于行为的安全检测,无法成功。
突破证书限制
所以相比于单项的认证,其实也就是多了一个服务器端验证客户端证书的过程,而在以往的用代理工具如burp和fiddler这一类工具,抓取https的包时,除了浏览器获取的是代理工具的证书外,默认是不发送证书给服务器端的,而其实代理工具也提供了双向认证的证书发送,如fiddler的ClientCertificate.cer证书。
只要提供了客户端的证书,也就实现了双向认证的破解过程。
所以重点在于如何提取出证书来。

WEB应用上证书的提取
最简单的一种就是,直接安装证书,或者使用时查看证书属性,在双向认证中一般会弹出此框,或者usb-key中如果有导入证书的功能的话更好,一般安全系数高的话,是会设置各种门槛阻止你获取证书的。

安装完成后,就可以直接导出证书了。
或者直接通过keytools工具来生成证书。

安卓APP下的证书
在应用中嵌入证书,使得每次请求都读取证书并发送,这样做,证书一般就需要和安卓应用一起打包,甚至放置的trustStore信任集,就需要密码来单独提取和安装证书了。
拿到apk包,首先需要解压出来内部的文件:
可以搜索一些证书的后缀文件,例如cer/p12/pfx等,一般安卓下的为bks,也可以先去assets或者res目录下去找找。
例如我碰到的apk就在assets目录下存放:
用java代码模拟双向认证的请求的过程:
  1.         // 双向认证证书
  2.         KeyStore keyStore = KeyStore.getInstance("PKCS12");
  3.         KeyStore trustStore = KeyStore.getInstance("jks");
  4.         // keyStore是服务端验证客户端的证书,trustStore是客户端的信任证书
  5.         InputStream ksIn = new FileInputStream("E:/Java/jre8/lib/security/re/1.pfx");
  6.         InputStream tsIn = new FileInputStream(new File("E:/Java/jre8/lib/security/re/1"));

  7.         keyStore.load(ksIn, "123456".toCharArray());

  8.         SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
  9.                 .loadKeyMaterial(keyStore, "123456".toCharArray()).setSecureRandom(new SecureRandom()).useSSL().build();

  10.         ConnectionSocketFactory pSocketFactory = new PlainConnectionSocketFactory();
  11.         SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext);

  12.         Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory> create()
  13.                 .register("http", pSocketFactory).register("https", sslConnectionSocketFactory).build();
  14.         PoolingHttpClientConnectionManager secureConnectionManager = new PoolingHttpClientConnectionManager(r);

  15.         HttpClientBuilder secureHttpBulder = HttpClients.custom().setConnectionManager(secureConnectionManager);
  16.         HttpClient client = secureHttpBulder.build();

  17.         HttpGet httpGet = new HttpGet("https://xxx.com");
  18.         HttpResponse httpResponse1 = client.execute(httpGet);
复制代码

反编译了代码后,发现被加固过,于是想脱壳,用了网上说的动态脱壳,不知道是不是水平问题,还是这个方法已经过去式了,反正没有成功,那咋办呢?
想到一个取巧的方法,直接搜历史app版本的记录,首先确定那个app之后开始时https的访问请求,然后在看这个app有没有加固过,最终是找到了一个年初的版本,这个版本已经开始使用了https,但是还没有完美的加固,至于历史版本,官网几乎删除了,不过有很多应用商店,如豌豆荚。(脉搏小编:历史版本这个取巧方法不错)
app依然有些地方被混淆了,不过无所谓,因为混淆代码一般混淆的都是自己编译的方法和类,向调用证书的函数方法,一般是组件类的,尝试的找了一下,总有一些蛛丝马迹:

最终还是找到了:



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?Join BUC

x
过段时间可能会取消签到功能了
您需要登录后才可以回帖 登录 | Join BUC

本版积分规则

Powered by Discuz!

© 2012-2015 Baiker Union of China.

快速回复 返回顶部 返回列表