欢迎光临散文网 会员登陆 & 注册

Fiddler Everywhere手机抓包&&绕过证书绑定

2020-11-15 21:25 作者:无情剑客Burning  | 我要投稿

在 抓包神器之Fiddler Everywhere及Wireshark 中讲解了Fiddler EveryWhere怎么抓取web的数据,这篇文章主要说怎么进行手机抓包以及绕过如何绕过ssl-pining技术实现抓包。

纠正一个错误

网上很多文章说,在进行手机抓包的时候,一定要保证手机和电脑在同一个局域网。这句话是不准确的,我认为这么说的基本上是两种情况: (1)人云亦云,别人这么说,我也这么说,应该不会错吧。 (2)对抓包的原理不理解,甚至于什么是中间人攻击都不清楚。

要实现抓包,需要的前提条件并不一定要在同一个局域网。但是应该保证手机能够ping同电脑的ip,因为需要在手机端下载和安装Fiddler Everywhere的证书。

手机抓包的配置

(1)设置手机网络代理,将代理设置为Fiddler Everywhere对应的ip和端口 (2)访问192.168.42.149(Fiddler Everywhere服务所对应的ip):8866(Fiddler Everywhere服务所对应的port)下载Fiddler Everywhere证书,并进行安装。本文使用的是ROOT的真机,系统版本是Android 10。如果是模拟器,可参考https://helloworddm.blog.csdn.net/article/details/98363056。 (3)可以愉快的抓包了。

证书绑定(Certificate Pinning)

Certificate Pinning,或者有叫作SSL Pinning/TLS Pinning的,都是指的同一个东西,中文翻译成证书锁定,最大的作用就是用来抵御针对CA的攻击。在实际当中,它一般被用来阻止man-in-the-middle(中间人攻击)。

说起中间人攻击,可能不是那么直观,但是这一类工具我们可能经常会用到,如Charles和Fiddler。如果一个应用使用了Certificate Pinning技术,那么你使用前边说的工具是无法直接来调试/监控应用的网络流量的。

当应用通过HTTPS握手连接到Fidder/Charles时,应用会检查请求的服务器的证书,如果发现与预设的不一致,会拒绝后续的网络请求,从而增加应用与服务器的安全通信。如下书某视频apk,由于采用ssl pinning技术,使得与服务器的连接无法建立。

实现

(1)教科书书式的实现。 语言使用的是Kotlin,Google欲使用Kotlin来取代Java,不过个人认为还有很长的路要走。


  1. // Load CAs from an InputStream

  2. // (could be from a resource or ByteArrayInputStream or ...)

  3. val cf: CertificateFactory = CertificateFactory.getInstance("X.509")

  4. // From https://www.washington.edu/itconnect/security/ca/load-der.crt

  5. val caInput: InputStream = BufferedInputStream(FileInputStream("load-der.crt"))

  6. val ca: X509Certificate = caInput.use {

  7.    cf.generateCertificate(it) as X509Certificate

  8. }

  9. System.out.println("ca=" + ca.subjectDN)


  10. // Create a KeyStore containing our trusted CAs

  11. val keyStoreType = KeyStore.getDefaultType()

  12. val keyStore = KeyStore.getInstance(keyStoreType).apply {

  13.    load(null, null)

  14.    setCertificateEntry("ca", ca)

  15. }


  16. // Create a TrustManager that trusts the CAs inputStream our KeyStore

  17. val tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm()

  18. val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm).apply {

  19.    init(keyStore)

  20. }


  21. // Create an SSLContext that uses our TrustManager

  22. val context: SSLContext = SSLContext.getInstance("TLS").apply {

  23.    init(null, tmf.trustManagers, null)

  24. }


  25. // Tell the URLConnection to use a SocketFactory from our SSLContext

  26. val url = URL("https://certs.cac.washington.edu/CAtest/")

  27. val urlConnection = url.openConnection() as HttpsURLConnection

  28. urlConnection.sslSocketFactory = context.socketFactory

  29. val inputStream: InputStream = urlConnection.inputStream

  30. copyInputStreamToOutputStream(inputStream, System.out)


具体套路如下:

  • 加载证书文件,并使用CertificateFactory生成一个X509Certificate的实例

  • 创建一个KeyStore实例,并把前边的X509Certificate实例加进去,并起一个别名

注意,这里其实是可以加多个证书进去的,但是注意别名不要重复,因为底层实现是使用一个Map存储别名与证书的

  • 创建一个TrustManager,并且使用前边的KeyStore实例进行初始化

  • 创建一个SSLContext,并且使用前边的TrustManager实例进行初始化

  • 最后,使用SSLContext创建一个SSLSocketFactory实例,并且把它赋值给我们用于https的请求连接对象HttpsURLConnection

(1)Okhttp实现


  1. String hostname = "publicobject.com";

  2. CertificatePinner certificatePinner = new CertificatePinner.Builder()

  3. .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")

  4. .build();

  5. OkHttpClient client = OkHttpClient.Builder()

  6. .certificatePinner(certificatePinner)

  7. .build();


  8. Request request = new Request.Builder()

  9. .url("https://" + hostname)

  10. .build();

  11. client.newCall(request).execute();


那么如何拿到上面所需要的hash值呢?官方给的一个方法是,先填写一个错的hash值,然后根据随后的exception的stack trace message,得到对应的hash值。

其实也可以通过openssl提供的命令直接从der或者pem格式的证书中计算出来,由于命令相对复杂一些,我写了一个简单的脚本封装了一下,支持两种格式的证书。

Frida绕过证书绑定


  1. setTimeout(function(){

  2. Java.perform(function (){

  3.        console.log("");

  4.        console.log("[.] Cert Pinning Bypass/Re-Pinning");


  5. var CertificateFactory = Java.use("java.security.cert.CertificateFactory");

  6. var FileInputStream = Java.use("java.io.FileInputStream");

  7. var BufferedInputStream = Java.use("java.io.BufferedInputStream");

  8. var X509Certificate = Java.use("java.security.cert.X509Certificate");

  9. var KeyStore = Java.use("java.security.KeyStore");

  10. var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");

  11. var SSLContext = Java.use("javax.net.ssl.SSLContext");


  12. // Load CAs from an InputStream

  13.        console.log("[+] Loading our CA...")

  14. var cf = CertificateFactory.getInstance("X.509");


  15. try {

  16. var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt");

  17. }

  18. catch(err) {

  19.            console.log("[o] " + err);

  20. }


  21. var bufferedInputStream = BufferedInputStream.$new(fileInputStream);

  22. var ca = cf.generateCertificate(bufferedInputStream);

  23.        bufferedInputStream.close();


  24. var certInfo = Java.cast(ca, X509Certificate);

  25.        console.log("[o] Our CA Info: " + certInfo.getSubjectDN());


  26. // Create a KeyStore containing our trusted CAs

  27.        console.log("[+] Creating a KeyStore for our CA...");

  28. var keyStoreType = KeyStore.getDefaultType();

  29. var keyStore = KeyStore.getInstance(keyStoreType);

  30.        keyStore.load(null, null);

  31.        keyStore.setCertificateEntry("ca", ca);


  32. // Create a TrustManager that trusts the CAs in our KeyStore

  33.        console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore...");

  34. var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();

  35. var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);

  36.        tmf.init(keyStore);

  37.        console.log("[+] Our TrustManager is ready...");


  38.        console.log("[+] Hijacking SSLContext methods now...")

  39.        console.log("[-] Waiting for the app to invoke SSLContext.init()...")


  40. SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) {

  41.            console.log("[o] App invoked javax.net.ssl.SSLContext.init...");

  42. SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c);

  43.            console.log("[+] SSLContext initialized with our custom TrustManager!");

  44. }

  45. });

  46. },0);

写在最后

基于本片文章的绕过技术,大部分基于服务器端的验证都是可以绕过的,但是如果是服务需要检验app的证书那,这个时候Fiddler Everywhere就显得力不从心了,Fiddler Everywhere没有办法导入其他的证书。这时候就只能使用Charlers之类的软件的。

公众号

更多内容,欢迎关注我的微信公众号:无情剑客。


Fiddler Everywhere手机抓包&&绕过证书绑定的评论 (共 条)

分享到微博请遵守国家法律