- A+
以前给一家证券机构做测试,第一次碰到了服务器双向认证的问题,当时双向认证的概念还没有推广开来,所以折腾了很久,虽然当时不知道原理,但是也算是解决了双向认证走代理的问题了。前段时间,碰到了一个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目录下存放:
我们双击p12安装一下,提示需要私钥密码:
用java代码模拟双向认证的请求的过程:
// 双向认证证书 KeyStore keyStore = KeyStore.getInstance("PKCS12"); KeyStore trustStore = KeyStore.getInstance("jks"); // keyStore是服务端验证客户端的证书,trustStore是客户端的信任证书 InputStream ksIn = new FileInputStream("E:/Java/jre8/lib/security/re/1.pfx"); InputStream tsIn = new FileInputStream(new File("E:/Java/jre8/lib/security/re/1")); keyStore.load(ksIn, "123456".toCharArray()); SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()) .loadKeyMaterial(keyStore, "123456".toCharArray()).setSecureRandom(new SecureRandom()).useSSL().build(); ConnectionSocketFactory pSocketFactory = new PlainConnectionSocketFactory(); SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext); Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory> create() .register("http", pSocketFactory).register("https", sslConnectionSocketFactory).build(); PoolingHttpClientConnectionManager secureConnectionManager = new PoolingHttpClientConnectionManager(r); HttpClientBuilder secureHttpBulder = HttpClients.custom().setConnectionManager(secureConnectionManager); HttpClient client = secureHttpBulder.build(); HttpGet httpGet = new HttpGet("https://xxx.com"); HttpResponse httpResponse1 = client.execute(httpGet);
反编译了代码后,发现被加固过,于是想脱壳,用了网上说的动态脱壳,不知道是不是水平问题,还是这个方法已经过去式了,反正没有成功,那咋办呢?
想到一个取巧的方法,直接搜历史app版本的记录,首先确定那个app之后开始时https的访问请求,然后在看这个app有没有加固过,最终是找到了一个年初的版本,这个版本已经开始使用了https,但是还没有完美的加固,至于历史版本,官网几乎删除了,不过有很多应用商店,如豌豆荚。(脉搏小编:历史版本这个取巧方法不错)
app依然有些地方被混淆了,不过无所谓,因为混淆代码一般混淆的都是自己编译的方法和类,向调用证书的函数方法,一般是组件类的,尝试的找了一下,总有一些蛛丝马迹:
最终还是找到了:
如此获取到了秘钥之后,就可以直接导入和生成证书了。
https://www.secpulse.com/archives/54027.html
参考
https://www.t00ls.net/thread-48538-1-1.html
- 我的微信
- 这是我的微信扫一扫
- 我的微信公众号
- 我的微信公众号扫一扫