|
本帖最后由 ivi 于 2023-9-11 18:46 编辑
胡凯莉 HACK之道 2023-09-11 08:02 发表于重庆
原文:https://www.52pojie.cn/thread-1809966-1-1.html
了解了HTTPS的传输流程,再来了解一下抓包相关知识。一、前置知识
二、核心知识点
2.1 证书安装了解了中间人的攻击流程,也就知道了关键是这个证书,证书被校验成功即可以进行双向的传输解密 安卓7以后安装的证书是放在用户目录的,并不能被系统信任,所以无法加解密流量。
大多数抓不到包的原因就是证书安装了,但是在用户目录 解决方法: 1 使用MT管理器 把用户目录的证书移动到系统证书目录(据说可以支持到安卓10)
2 由于 Android 10 采用了某些安全策略,将系统分区 /system挂载为只读,就算你 root 了也没用,无法写入系统分区也就无法导入系统证书
3 修改源码默认信任用户证书(提供几个检测的源码定位代码)
/frameworks/base/core/java/android/security/net/config/NetworkSecurityConfig.java /frameworks/base/core/java/android/security/net/config/XmlConfigSource.java libcore/ojluni/src/main/java/java/net/NetworkInterface.java libcore/ojluni/src/main/java/java/lang/System.java
到此安装完证书 使用抓包工具(charles、fidder、Burpsuit等)就可以进行抓包了 但是我们使用的证书是中间人的证书 不是服务器直接下发的证书 所以只能解决80%的HTTPS抓包问题。
三、SSLPinning环境下如何抓包3.1 证书校验——SSL证书绑定上文可以了解到从 HTTP 到 HTTPS 数据在传输过程中添加了一层 加密(SSL/TLS),让我们数据流量处于加密状态,不再是明文可见。一旦 app 校验了证书的指纹信息。我们的证书不再受信任了。自然而然就无法建立连接,所以必须想办法让 app 信任,才能继续抓包。当然这个分为两种情况: 3.1.1 客户端校验服务器端的证书上篇文件提到了一个证书包含了很多信息,那么客户端校验的原理就是:在APP中预先设置好证书的信息,在证书校验阶段时与服务器返回的证书信息进行比较。 探索开发逻辑1 公钥校验
- private void doRequest(){
- new Thread(){
- @Override
- public void run() {
- final String CA_PUBLIC_KEY = "sha256/kO7OP94daK9P8+X52s00RvJLU0SiCXA9KAg9PelfwIw=";
- final String CA_DOMAIN = "www.52pojie.cn";
- //校验公钥
- CertificatePinner buildPinner = new CertificatePinner.Builder()
- .add(CA_DOMAIN, CA_PUBLIC_KEY)
- .build();
- OkHttpClient client = new OkHttpClient.Builder().certificatePinner(buildPinner).build();
- Request req = new Request.Builder().url("https://www.52pojie.cn/forum.php")
- .build();
- Call call = client.newCall(req);
- try {
- Response res = call.execute();
- Log.e("请求成功", "状态码:" + res.code());
- } catch (IOException e) {
- e.printStackTrace();
- Log.e("请求失败", "异常" + e);
- }
- }
- }.start();
- }
复制代码
- CertificatePinner buildPinner = new CertificatePinner.Builder()
- .add(CA_DOMAIN, CA_PUBLIC_KEY)
- .build();
- //将buildPinner 传给OkHttpclient
- OkHttpClient client = new OkHttpClient.Builder()
- .certificatePinner(buildPinner)
- .build();
复制代码
- public void check(String hostname, List<Certificate> peerCertificates)
- throws SSLPeerUnverifiedException {
- List<Pin> pins = findMatchingPins(hostname);
- if (pins.isEmpty()) return;
- if (certificateChainCleaner != null) {
- peerCertificates = certificateChainCleaner.clean(peerCertificates, hostname);
- }
- for (int c = 0, certsSize = peerCertificates.size(); c < certsSize; c++) {
- X509Certificate x509Certificate = (X509Certificate) peerCertificates.get(c);
- // Lazily compute the hashes for each certificate.
- ByteString sha1 = null;
- ByteString sha256 = null;
- for (int p = 0, pinsSize = pins.size(); p < pinsSize; p++) {
- Pin pin = pins.get(p);
- if (pin.hashAlgorithm.equals("sha256/")) {
- if (sha256 == null) sha256 = sha256(x509Certificate);
- if (pin.hash.equals(sha256)) return; // Success!
- } else if (pin.hashAlgorithm.equals("sha1/")) {
- if (sha1 == null) sha1 = sha1(x509Certificate);
- if (pin.hash.equals(sha1)) return; // Success!
- } else {
- throw new AssertionError("unsupported hashAlgorithm: " + pin.hashAlgorithm);
- }
- }
- }
复制代码- // Bypass OkHTTPv3 {1}
- var okhttp3_Activity_1 = Java.use('okhttp3.CertificatePinner');
- okhttp3_Activity_1.check.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
- console.log('[+] Bypassing OkHTTPv3 {1}: ' + a);
- return;
复制代码
- private void doRequest2(){
- X509TrustManager trustManager = new X509TrustManager() {
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
- }
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
- //服务器返回的证书
- X509Certificate cf = chain[0];
- //转换为RSA的公钥
- RSAPublicKey rsaPublicKey = (RSAPublicKey) cf.getPublicKey();
- //Base64 encode
- String ServerPubkey = Base64.encodeToString(rsaPublicKey.getEncoded(), 0);
- Log.e("服务器端返回的证书",ServerPubkey);
- //读取客户端资源目录中的证书
- InputStream client_input = getResources().openRawResource(R.raw.pojie);
- CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
- X509Certificate realCertificate = (X509Certificate) certificateFactory.generateCertificate(client_input);
- String realPubkey = Base64.encodeToString(realCertificate.getPublicKey().getEncoded(), 0);
- Log.e("客户端资源目录中的证书",realPubkey);
- cf.checkValidity();
- final boolean expected = realPubkey.equalsIgnoreCase(ServerPubkey);
- Log.e("eq = ",String.valueOf(expected));
- if (!expected){
- throw new CertificateException("证书不一致");
- }
- }
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
- };
- SSLSocketFactory factory = null;
- try {
- SSLContext sslContext = SSLContext.getInstance("SSL");
- sslContext.init(null,new TrustManager[]{trustManager},new SecureRandom());
- factory = sslContext.getSocketFactory();
- } catch (Exception e) {
- e.printStackTrace();
- }
- SSLSocketFactory finalFactory = factory;
- new Thread(){
- @Override
- public void run() {
- try {
- OkHttpClient client = new OkHttpClient.Builder().sslSocketFactory(finalFactory, trustManager).build();
- Request req = new Request.Builder().url("https://www.52pojie.cn/forum.php").build();
- Call call = client.newCall(req);
- Response res = call.execute();
- Log.e("请求发送成功","状态码:" + res.code());
- } catch (IOException e) {
- Log.e("请求发送失败","网络异常" + e);
- }
- }
- }.start();
- }
复制代码
- X509TrustManager trustManager = new X509TrustManager() {
- ...
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType) {
- ...
- }
- ....
- }
复制代码
- var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
- var SSLContext = Java.use('javax.net.ssl.SSLContext');
- // TrustManager (Android < 7) //
- ////////////////////////////////
- var TrustManager = Java.registerClass({
- // Implement a custom TrustManager
- name: 'dev.asd.test.TrustManager',
- implements: [X509TrustManager],
- methods: {
- checkClientTrusted: function(chain, authType) {},
- checkServerTrusted: function(chain, authType) {},
- getAcceptedIssuers: function() {return []; }
- }
- });
- // Prepare the TrustManager array to pass to SSLContext.init()
- var TrustManagers = [TrustManager.$new()];
- // Get a handle on the init() on the SSLContext class
- var SSLContext_init = SSLContext.init.overload(
- '[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');
- try {
- // Override the init method, specifying the custom TrustManager
- SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {
- console.log('[+] Bypassing Trustmanager (Android < 7) pinner');
- SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);
- };
- } catch (err) {
- console.log('[-] TrustManager (Android < 7) pinner not found');
- //console.log(err);
- }
复制代码
3 host(域名)校验
- private void doRequest3(){
- HostnameVerifier verifier = new HostnameVerifier() {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- if ("www.52pojie.cn".equalsIgnoreCase(hostname)){
- return true;
- }
- return false;
- }
- };
- new Thread() {
- @Override
- public void run() {
- try {
- OkHttpClient client = new OkHttpClient.Builder().hostnameVerifier(verifier).build();
- Request req = new Request.Builder().url("https://www.52pojie.cn/forum.php").build();
- Call call = client.newCall(req);
- Response res = call.execute();
- Log.e("请求发送成功", "状态码:" + res.code());
- } catch (IOException ex) {
- Log.e("Main", "网络请求异常" + ex);
- }
- }
- }.start();
- }
复制代码
3.1.2 服务器端证书校验
在客户端放入证书(p12/bks),客户端向服务端发送请求时,携带证书信息,在服务端会校验客户端携带过来的证书合法性
- private void doRequest4(){
- X509TrustManager trustManager = new X509TrustManager() {
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
- }
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
- }
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
- };
- HostnameVerifier verify = new HostnameVerifier() {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- return true;
- }
- };
- new Thread(){
- @Override
- public void run() {
- try {
- InputStream client_input = getResources().openRawResource(R.raw.client);
- Log.e("x",client_input.getClass().toString());
- SSLContext sslContext = SSLContext.getInstance("TLS");
- KeyStore keyStore = KeyStore.getInstance("PKCS12");
- keyStore.load(client_input, "demoli666".toCharArray());
- KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- keyManagerFactory.init(keyStore, "demoli666".toCharArray());
- sslContext.init(keyManagerFactory.getKeyManagers(), new TrustManager[]{trustManager}, new SecureRandom());
- SSLSocketFactory factory = sslContext.getSocketFactory();
- OkHttpClient client = new OkHttpClient.Builder().sslSocketFactory(factory, trustManager).hostnameVerifier(verify).build();
- Request req = new Request.Builder().url("https://xxx.xxx.xxx.xxx:443/index").build();
- Call call = client.newCall(req);
- Response res = call.execute();
- Log.e("请求发送成功","状态码:" + res.code());
- } catch (Exception e) {
- Log.e("请求发送失败","网络异常" + e);
- }
- }
- }.start();
- }
复制代码
解决方法找到证书文件(bsk/p12) 通过hook获取证书相关密码
- Java.perform(function () {
- function uuid(len, radix) {
- var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
- var uuid = [], i;
- radix = radix || chars.length;
- if (len) {
- // Compact form
- for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
- } else {
- // rfc4122, version 4 form
- var r;
- // rfc4122 requires these characters
- uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
- uuid[14] = '4';
- // Fill in random data. At i==19 set the high bits of clock sequence as
- // per rfc4122, sec. 4.1.5
- for (i = 0; i < 36; i++) {
- if (!uuid[i]) {
- r = 0 | Math.random() * 16;
- uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
- }
- }
- }
- return uuid.join('');
- }
- function storeP12(pri, p7, p12Path, p12Password) {
- var X509Certificate = Java.use("java.security.cert.X509Certificate")
- var p7X509 = Java.cast(p7, X509Certificate);
- var chain = Java.array("java.security.cert.X509Certificate", [p7X509])
- var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC");
- ks.load(null, null);
- ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(p12Password).toCharArray(), chain);
- try {
- var out = Java.use("java.io.FileOutputStream").$new(p12Path);
- ks.store(out, Java.use('java.lang.String').$new(p12Password).toCharArray())
- } catch (exp) {
- console.log(exp)
- }
- }
- //在服务器校验客户端的情形下,帮助dump客户端证书,并保存为p12的格式,证书密码为r0ysue
- Java.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function () {
- var result = this.getPrivateKey()
- var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName();
- storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue');
- return result;
- }
- Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function () {
- var result = this.getCertificateChain()
- var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName();
- storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue');
- return result;
- }
- });
复制代码
四、混淆代码1 关于混淆- Java.use('okhttp3.CertificatePinner');
- Java.use('com.square.okhttp.internal.tls.OkHostnamaVerifier');
复制代码- Java.use("java.security.KeyStore");
复制代码
2 解决方法- X509TrustManager trustManager = new X509TrustManager() {
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
- }
复制代码
2 主机校验
- HostnameVerifier verify = new HostnameVerifier() {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- return true;
- }
- };
复制代码
3 pinner 公钥校验 这个校验主要是调用了CertificatePinner类中check方法
- CertificatePinner buildPinner = new CertificatePinner.Builder()
- .add(CA_DOMAIN, CA_PUBLIC_KEY)
- .build();
- OkHttpClient client = new OkHttpClient.Builder().certificatePinner(buildPinner).build();
复制代码
- Java.perform(function () {
- var NativeSsl = Java.use('com.android.org.conscrypt.NativeSsl');
- NativeSsl.doHandshake.overload('java.io.FileDescriptor', 'int').implementation = function (a, b) {
- console.log("参数:", a, b);
- console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
- return this.doHandshake(a, b);
- };
- });
- // frida -UF -l 1.hook_check.js
复制代码
- Java.perform(function () {
- var Platform = Java.use('com.android.org.conscrypt.Platform');
- Platform.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.AbstractConscryptSocket').implementation = function (x509tm, chain, authType, socket) {
- console.log('\n[+] checkServer ',x509tm,JSON.stringify(x509tm) );
- // 这里会去调用客户端证书校验的方法,不执行,就是不去校验(直接通过)。
- //return this.checkServerTrusted(x509tm, chain, authType, socket);
- };
- });
复制代码
2 hostname校验
- 很少遇到 不写了
- Java.perform(function () {
- function getFieldValue(obj, fieldName) {
- var cls = obj.getClass();
- var field = cls.getDeclaredField(fieldName);
- field.setAccessible(true);
- var name = field.getName();
- var value = field.get(obj);
- return value;
- }
- function getMethodValue(obj, methodName) {
- var res;
- var cls = obj.getClass();
- var methods = cls.getDeclaredMethods();
- methods.forEach(function (method) {
- var method_name = method.getName();
- console.log(method_name, method);
- if (method_name === methodName) {
- method.setAccessible(true);
- res = method;
- return;
- }
- })
- return res;
- }
- var RealConnection = Java.use('uk.c');
- RealConnection.f.implementation = function (a, b, c, d) {
- try {
- console.log("===============");
- var route = getFieldValue(this, "c");
- console.log('route=', route);
- var address = getFieldValue(route, 'a');
- console.log('address=', address);
- var hostnameVerifier = getFieldValue(address, 'hostnameVerifier');
- console.log('hostnameVerifier=', hostnameVerifier);
- console.log('\n[+] hostnameVerifier', hostnameVerifier);
- } catch (e) {
- console.log(e);
- }
- return this.f(a, b, c, d);
- };
- });
复制代码
3 公钥pinner校验
- connectTls中就能找到他的类和方法被混淆后的名称,可以直接Hook
复制代码
六、特殊框架 flutter 环境下如何抓包
也可以直接找该函数的函数地址
- function hook_ssl_verify_result(address)
- {
- Interceptor.attach(address, {
- onEnter: function(args) {
- console.log("Disabling SSL validation")
- },
- onLeave: function(retval)
- {
- console.log("Retval: " + retval)
- retval.replace(0x1);
- }
- });
- }
- function disablePinning(){
- // Change the offset on the line below with the binwalk result
- // If you are on 32 bit, add 1 to the offset to indicate it is a THUMB function: .add(0x1)
- // Otherwise, you will get 'Error: unable to intercept function at ......; please file a bug'
- // 0x393DA4 换成你找到的函数地址
- var address = Module.findBaseAddress('libflutter.so').add(0x393DA4)
- hook_ssl_verify_result(address);
- }
- setTimeout(disablePinning, 1000)
复制代码
七、非root环境如何抓包八、自实现SSL/TLS框架九、其他抓包技巧
乌雲安全 乌雲安全,致力于网络安全攻防、内网渗透、代码审计、安卓逆向、CTF比赛、应急响应、安全运维、安全架构、linux技巧等技术干货分享。
29篇原创内容
公众号
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|