[免费专栏] Android安全之绕过WebView SSL Pinning抓HTTPS数据


也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大

少走了弯路,也就错过了风景,无论如何,感谢经历


转移发布平台通知:将不再在CSDN博客发布新文章,敬请移步知识星球

感谢大家一直以来对我CSDN博客的关注和支持,但是我决定不再在这里发布新文章了。为了给大家提供更好的服务和更深入的交流,我开设了一个知识星球,内部将会提供更深入、更实用的技术文章,这些文章将更有价值,并且能够帮助你更好地解决实际问题。期待你加入我的知识星球,让我们一起成长和进步


Android安全付费专栏长期更新,本篇最新内容请前往:

:样本APK下载地址,请见文章末尾

0x01 前言

Android 中的证书固定,Android WebView 中证书锁定,在Android 7.0 Nougat (SDK 24)开始才有的,在 Android 7.0 Nougat (SDK 24)之前,无论开发人员使用什么库,都无法真正管理 Webview 上的固定,因为Android 7.0 Nougat (SDK 24)的网络安全配置允许应用程序定义自己的规则集。

为了不让攻击者抓到数据包货执行中间人攻击,开发人员使用了WebView SSLPinning,如果我们想成功地对固定的证书域执行中间人攻击,就必须获得实际证书或能够生成有效证书(来自受信任的证书颁发机构)

1.1 WebView 忽略证书验证

1.1.1 Okhttp3忽略HTTPS证书校验

import java.security.SecureRandom;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class SSLSocketClient {

    public static SSLSocketFactory getSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, getTrustManager(), new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    private static TrustManager[] getTrustManager() {
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[]{};
                    }
                }
        };
        return trustAllCerts;
    }

    public static HostnameVerifier getHostnameVerifier() {
        HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        };
        return hostnameVerifier;
    }
}
sClient = new OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .sslSocketFactory(SSLSocketClient.getSSLSocketFactory())
        .hostnameVerifier(SSLSocketClient.getHostnameVerifier())
        .build();

1.1.2 WebView忽略HTTPS证书校验

Android WebView组件加载网页发生证书认证错误时,会调用WebViewClient类的onReceivedSslError方法,如果该方法实现调用了handler.proceed()来忽略该证书错误,则会受到中间人攻击的威胁,可能导致隐私泄露

httptest APP中,WebView忽略了SSL证书错误,可能引起中间人攻击

/*
* https协议
* WebView 不进行证书校验
*/
button_webview_ssl_without_ca.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        MyWebViewClient mWebViewClient = new MyWebViewClient();
        mWebViewClient.setCheckflag("trustAllCerts");
        mWebview.setWebViewClient(mWebViewClient);
        mWebview.loadUrl("https://www.baidu.com/?q=WebView_without_CAcheck");
    }
});

WebView直接调用onReceivedSslError方法时,直接执行handler.proceed()来忽略该证书错误,setWebViewClient() 实现方法如下:

wv.setWebViewClient(new WebViewClient() {
            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                handler.proceed();//忽略证书错误继续加载页面
            }
        });

客户端访问使用Https协议加密的url时,如果服务器证书校验错误,客户端应该拒绝继续加载页面

在使用WebView进行HTTPS请求时,同样可以对HTTPS证书进行校验,WebView控件对请求逻辑进行了封装,所以进行证书校验更加方便,具体代码如下所示,handler.cancel()相当于使用系统证书校验::

private class MyWebViewClient extends WebViewClient {
    private String checkflag="checkCerts"; // 是否忽略证书校验

    public void setCheckflag(String checkflag) {
        this.checkflag = checkflag;
    }

    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        if("trustAllCerts".equals(checkflag)){//忽略证书校验
            handler.proceed();
        }else {
            handler.cancel();
            Toast.makeText(MainActivity.this, "证书异常,停止访问", Toast.LENGTH_SHORT).show();
        }
    }
}

打开Burp,并在Android 设备中配置好代理,然后打开APP后点击【WEBVIEW SSL(忽略证书校验)】按钮,APP成功访问,并在界面显示百度的一个gif图片,Burp也成功抓到信息,如下:

在这里插入图片描述

1.2 WebView 系统证书校验

WebView没有自定义SSL Pinning的实现方法,只能通过network_security_config.xml配置证书绑定

/*
 * https协议
 * WebView 使用系统证书校验
 * 默认证书链校验,只信任系统CA(根证书)
*
* tips: OKHTTP默认的https请求使用系统CA验证服务端证书(Android7.0以下还信任用户证书,Android7.0开始默认只信任系统证书)
 */
button_webview_ssl_with_system_ca.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        MyWebViewClient mWebViewClient = new MyWebViewClient();
        mWebViewClient.setCheckflag("checkCerts");
        mWebview.setWebViewClient(mWebViewClient);
        mWebview.loadUrl("https://www.baidu.com/?q=WebView_with_SystemCAcheck");
    }
});

在这里插入图片描述

绕过WebView 系统证书校验的方法,跟绕过HTTPS 系统证书校验的方法一样,只需要将抓包工具的证书安装到系统信任库里面即可正常抓包或反编译APK在<app>\res\xml\network_security_config.xml<app>\AndroidManifest.xml中添加如下信息后重打包APK并签名就能抓到数据:

<?xml version="1.0" encoding="utf-8"?>
  <network-security-config>
    <base-config> 
      <trust-anchors>
          <!-- 信任系统预置CA 证书 -->
          <certificates src="system" />
          <!-- 信任用户添加的 CA 证书,Charles 和 Fiddler 抓包工具安装的证书属于此类 -->
          <certificates src="user" />
      </trust-anchors>
    </base-config>
</network-security-config>

AndroidManifest.xml:

<application  android:networkSecurityConfig="@xml/network_security_config">

:在Android 7.0以前,应用默认会信任系统证书和用户证书,Android 7.0开始,默认只信任系统证书,在root环境下,将证书导入系统目录的方法。只要想办法把证书弄到系统证书列表,就可以抓包解密明文数据,具体的证书安装到系统证书目录的方法,如下:

目前在Android 7.0或以上的系统有启用了对第三方证书的限制,但在Android 7.0以下依旧可以将Fiddler/Charles/Burp的证书安装在用户的CA集中抓取https请求

将Burp或者Charles的证书作为系统级别的信任证书安装。建议重新生产burp证书并重启burp后再导出。系统级别的受信任证书以特殊格式存储在/system/etc/security/cacerts文件夹中。我们可以将Burp的证书写入此位置。

导出一个Burp/Charles证书之后,adb push到模拟器,然后修改后缀为crt直接安全,这种方式在Android 6及之前的版本可用,从Android 7.0开始,Android更改了信任用户安装的证书的默认行为,应用程序仅信任系统级CA。这样就导致安装证书后,如果尝试通过Burp或者Charlse代理访问https网站,系统会提示“该证书并非来自可信的授权中心”

对于Android 7.0 (API 24) 之后,做了些改动,使得系统安全性增加了,导致:

  • APP 默认不信任用户域的证书
    • 之前把抓包工具的ssl证书,通过【从SD卡安装】安装到受信任的凭据 -> 用户。就没用了,因为不再受信任了
    • 只信任(安装到)系统域的证书
    • 导致无法抓包https,抓出来的https的请求,都是加了密的,无法看到原文了

对此,总结出相关解决思路和方案:

  • 让系统信任抓包工具的ssl证书
    • 作为app的开发者自己:改自己的app的配置,允许https抓包(得到或本身有app的源码,把证书放到受系统信任的系统证书中去)
    • 把证书放到受系统信任的系统证书中去(root权限)
  • 绕开https不去校验
    • 借助于其他(JustTrustMe等)工具绕开https的校验(例如XPosed、太极XPosed等框架)

1) Android 7抓包问题解决方法

  1. 导出Burp的证书后,使用openssl来做一些改动,最后上传到/system/etc/security/cacerts

在这里插入图片描述
在这里插入图片描述

root@kali:~# openssl x509 -inform DER -in cert-der.crt -out PortSwiggerCA.pem
root@kali:~# openssl x509 -inform PEM -subject_hash_old -in PortSwiggerCA.pem|head -1
root@kali:~# mv PortSwiggerCA.pem 9999.0
  1. adb传输文件报错couldn‘t create file:Read-only file system解决方法

在这里插入图片描述

D:\>adb shell
VOG-AL00:/ # mount -o remount -o rw /system
mount -o remount -o rw /system
VOG-AL00:/ # exit;
exit;

D:\>adb root
adbd is already running as root

D:\>adb remount
remount succeeded

D:\>adb push 9999.0 /system/etc/security/cacerts
330 KB/s (1330 bytes in 0.003s)

D:\>adb shell chmod 644 /system/etc/security/cacerts/9999.0

在这里插入图片描述

再次输入adb push就能正常传输文件

在这里插入图片描述

2)Android10 抓包问题解决方法

安卓 10 采用了的安全策略-将系统分区 /system 挂载为只读,因此Android10以上需要刷入Magisk,在模块里搜索move Cert(MagiskTrustUserCerts) 安装模块,重启后证书就已经被移动到系统目录(system/etc/security/cacerts),可以正常抓包了

Magisk下载地址:https://github.com/topjohnwu/Magisk
MagiskTrustUserCerts下载地址:https://github.com/NVISOsecurity/MagiskTrustUserCerts

0x02 WebView SSL Pinning

2.1 绕过WebView证书文件校验

/*
* https协议 SSL Pinning WebView
* 通过network_security_config.xml中定义的证书和密钥进行绑定
*/
button_webview_ssl_pinning.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        MyWebViewClient mWebViewClient = new MyWebViewClient();
        mWebViewClient.setCheckflag("checkCerts");
        mWebview.setWebViewClient(mWebViewClient);
        mWebview.loadUrl("https://www.sogou.com/web?query=WebView_SSLPinningXML"); // 证书文件校验
    }
});

:【确定样本APK中编译好的证书是否同学你在使用的时候失效了,建议自行使用下面的方法自行编译】此处的本地证书要替换一下为最新的,下载官网最新的sogou.pem然后在/替换掉/res/raw/目录下的sogou.pem文件

openssl s_client -connect sogou.com:443 -servername sogou.com | openssl x509 -out sogou.pem

在这里插入图片描述
在这里插入图片描述

network_security_config.xml配置的WebView的证书文件校验和公钥校验内容:

在这里插入图片描述

正常访问不挂代理,内容如下:

在这里插入图片描述

挂代理访问,内容如下:

在这里插入图片描述

绕过方式:利用Android 特性绕过,使用Frida

JS脚本:

Java.perform(function() {
try {
		var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
		TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
			console.log('[+] Bypassing TrustManagerImpl (Android > 7): ' + host);
			return untrustedChain;
		};
	} catch (err) {
		console.log('[-] TrustManagerImpl (Android > 7) pinner not found');
		//console.log(err);
	}

});

在这里插入图片描述

2.2 绕过WebView证书公钥校验

/*
* https协议 SSL Pinning WebView
* 通过network_security_config.xml中定义的证书和密钥进行绑定
*/
button_webview_ssl_pinning.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        MyWebViewClient mWebViewClient = new MyWebViewClient();
        mWebViewClient.setCheckflag("checkCerts");
        mWebview.setWebViewClient(mWebViewClient);
        mWebview.loadUrl("https://www.sogou.com/web?query=WebView_SSLPinningXML"); // 证书文件校验
        // mWebview.loadUrl("https://www.zhihu.com/"); // 证书公钥校验
    }
});

:【确定样本APK中编译好的证书是否同学你在使用的时候失效了,建议自行使用下面的方法自行编译】此处证书公钥要替换一下

openssl s_client -connect zhihu.com:443 -servername zhihu.com | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

在这里插入图片描述

在这里插入图片描述

正常访问不挂代理,内容如下:

在这里插入图片描述

挂代理访问,内容如下:

在这里插入图片描述

绕过方式:利用Android 特性绕过,使用Frida

JS脚本:

Java.perform(function() {
try {
		var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
		TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
			console.log('[+] Bypassing TrustManagerImpl (Android > 7): ' + host);
			return untrustedChain;
		};
	} catch (err) {
		console.log('[-] TrustManagerImpl (Android > 7) pinner not found');
		//console.log(err);
	}

});

在这里插入图片描述

当然,也可以通过res/xml/network_security_config.xml配置文件对服务端证书进行校验,例如针对证书校验,反编译APK将对应的证书替换为Burp的即可绕过;公钥校验的同理

在这里插入图片描述

参考链接

https://www.cnblogs.com/Potato-Eater/p/9792725.html

https://ch3nye.top/Android-HTTPS%E8%AE%A4%E8%AF%81%E7%9A%84N%E7%A7%8D%E6%96%B9%E5%BC%8F%E5%92%8C%E5%AF%B9%E6%8A%97%E6%96%B9%E6%B3%95%E6%80%BB%E7%BB%93/


你以为你有很多路可以选择,其实你只有一条路可以走


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Android提供了WebView控件来加载和显示Web页面。在使用WebView加载HTTPS网页时,SSL(即Secure Sockets Layer)是必需的。 SSL是一种用于在Internet上保护数据传输安全的加密协议。它确保在浏览器和服务器之间传输的数据是加密的,以防止第三方篡改或窃听数据。 要在Android WebView中使用SSL,需要采取以下步骤: 1. 配置WebView设置:在代码中,我们可以通过设置WebView的WebSettings对象来启用JavaScript和SSL,以便加载HTTPS网页。可以使用以下代码进行设置: ``` WebView webView = findViewById(R.id.webview); WebSettings webSettings = webView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setDomStorageEnabled(true); webSettings.setAppCacheEnabled(true); webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); ``` 2. 导入SSL证书:有时候,我们需要导入服务器的SSL证书,以便WebView可以信任该服务器。可以使用以下代码导入SSL证书: ``` InputStream inputStream = getAssets().open("ssl_cert.cer"); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); X509Certificate x509Certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream); inputStream.close(); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); keyStore.setCertificateEntry("ssl_cert", x509Certificate); String defaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(defaultAlgorithm); keyManagerFactory.init(keyStore, null); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(defaultAlgorithm); trustManagerFactory.init(keyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), null); SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); webSettings.setJavaScriptCanOpenWindowsAutomatically(true); webSettings.setUseWideViewPort(true); webSettings.setBuiltInZoomControls(true); webSettings.setDisplayZoomControls(false); webSettings.setSupportZoom(true); webSettings.setAllowFileAccess(true); webSettings.setAllowContentAccess(true); webView.setWebViewClient(new WebViewClient()); webView.getSettings().setJavaScriptEnabled(true); webView.loadUrl("https://www.example.com"); ``` 通过以上步骤,我们就可以在Android WebView中加载HTTPS安全网页并保持通信的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橙留香Park

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值