0%

如何绕过app的ssl-pinning

什么是ssl-pinning?

为了防止中间人攻击,app很多采用了证书固定技术,就是证书的双向认证。 我们在burp抓取浏览器https的包时,有时候会弹出警告当前证书不可信,然后我们手动勾选信任即可进行抓包(这种就是单向验证,我们强制服务端接收我们自己的证书)。

app的双向验证就是,如果我们的证书和app内置在客户端的证书不一致,那么他就直接报错或者直接退出。所以我们在渗透测试app的业务时需要一点手段绕过ssl的双向认证,目前绕过ssl-pinning的主要手段就是hook和逆向源码重新修改修改 IPA 文件、打包、安装。

由于很多app都进行了加固,逆向源码是不太容易的事情,所以更多的人采用hook函数的方法来进行ssl-pinning的绕过,本篇文章就是介绍两种过SSL-unpining的绕过方法。

ssl-pinning实现方法

长话短说,只有知道实现方法才能更好的理解hook原理,绝大多数都采用的是Okhttp这个库。 在OKhttp中实现 SSL Pining十分简单。

先来看看官方文档,是英文的,但是为了阅读友好,我大概翻译了一下😂:

默认情况下,okhttp信任主机平台的证书颁发机构。此策略最大化了连接,但它会受到证书颁发机构攻击,如2011 DigiNotar攻击。它还假设您的https服务器的证书是由证书颁发机构签名的。
使用CertificatePinner限制信任哪些证书和证书颁发机构。证书固定增加了安全性,但限制了服务器团队更新其TLS证书的能力。未经服务器的tls管理员许可,请勿使用证书固定。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public CertificatePinning() {
client = new OkHttpClient.Builder()
.certificatePinner(new CertificatePinner.Builder()
.add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
.build())
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://publicobject.com/robots.txt")
.build();

Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

for (Certificate certificate : response.handshake().peerCertificates()) {
System.out.println(CertificatePinner.pin(certificate));
}
}

另外还有更简单的,直接在AndroidManifest.xml 中指定(好像有版本限制,但是版本我忘了😂):

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">appmattus.com</domain>
<pin-set>
<pin digest="SHA-256">4hw5tz+scE+TW+mlai5YipDfFWn1dqvfLG+nU7tq1V8=</pin>
<pin digest="SHA-256">YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=</pin>
</pin-set>
</domain-config>
</network-security-config>

先来了解下https的工作流程:

在会话的初始阶段,客户端第一次发送请求时,通过明文进行发送的,为了防止信息泄露,服务端在返回时则返回一个公钥和证书给客户端。客户端验证证书是否是认证机构颁发,证书是否在有效期内,若没问题则进行下一次请求。在下一次客户端发送请求的时,通过公钥对核心数据进行加密,服务端在接收到数据时,获取对称加密的密钥。此后请求就是通过对称加密的密钥进行加密。

那我们来捋一遍流程,安装app时app的自带证书就安置到了我们的手机,且此证书也存在在服务端,另外app的代码是开启了SSLpinning的,当我们用burp抓包时需要导入手机证书,那我们劫持https的流量就是用我们burp的证书加解密的,而burp的证书和app的自带证书不一致,所以直接down。多说一句,证书的一致性是通过sha/256来进行比较的。

xpose绕过ssl-pining

xpose下载地址:https://repo.xposed.info/module/de.robv.android.xposed.installer
注意:目前好像不支持android 9.0以上,我也不会写怎么安装的。恩,懒得写。

JustTrustMe模块绕过:
模块下载地址 https://github.com/Fuzion24/JustTrustMe
此模块是以前用的最多的模块,但是近来越来越不好用了,经过源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
package just.trust.me;

import android.content.Context;
import android.net.http.SslError;
import android.util.Log;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import java.security.SecureRandom;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.HostNameResolver;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.HttpParams;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

import static de.robv.android.xposed.XposedHelpers.callMethod;
import static de.robv.android.xposed.XposedHelpers.callStaticMethod;
import static de.robv.android.xposed.XposedHelpers.findAndHookConstructor;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import static de.robv.android.xposed.XposedHelpers.getObjectField;
import static de.robv.android.xposed.XposedHelpers.newInstance;
import static de.robv.android.xposed.XposedHelpers.setObjectField;
import static de.robv.android.xposed.XposedHelpers.findClass;

public class Main implements IXposedHookLoadPackage {

private static final String TAG = "JustTrustMe";
String currentPackageName = "";

public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {

currentPackageName = lpparam.packageName;
/* Apache Hooks */
/* external/apache-http/src/org/apache/http/impl/client/DefaultHttpClient.java */
/* public DefaultHttpClient() */
Log.d(TAG, "Hooking DefaultHTTPClient for: " + currentPackageName);
findAndHookConstructor(DefaultHttpClient.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {

setObjectField(param.thisObject, "defaultParams", null);
setObjectField(param.thisObject, "connManager", getSCCM());
}
});

/* external/apache-http/src/org/apache/http/impl/client/DefaultHttpClient.java */
/* public DefaultHttpClient(HttpParams params) */
Log.d(TAG, "Hooking DefaultHTTPClient(HttpParams) for: " + currentPackageName);
findAndHookConstructor(DefaultHttpClient.class, HttpParams.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {

setObjectField(param.thisObject, "defaultParams", (HttpParams) param.args[0]);
setObjectField(param.thisObject, "connManager", getSCCM());
}
});

/* external/apache-http/src/org/apache/http/impl/client/DefaultHttpClient.java */
/* public DefaultHttpClient(ClientConnectionManager conman, HttpParams params) */
Log.d(TAG, "Hooking DefaultHTTPClient(ClientConnectionManager, HttpParams) for: " + currentPackageName);
findAndHookConstructor(DefaultHttpClient.class, ClientConnectionManager.class, HttpParams.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {

HttpParams params = (HttpParams) param.args[1];

setObjectField(param.thisObject, "defaultParams", params);
setObjectField(param.thisObject, "connManager", getCCM(param.args[0], params));
}
});

/* external/apache-http/src/org/apache/http/conn/ssl/SSLSocketFactory.java */
/* public SSLSocketFactory( ... ) */
Log.d(TAG, "Hooking SSLSocketFactory(String, KeyStore, String, KeyStore) for: " + currentPackageName);
findAndHookConstructor(SSLSocketFactory.class, String.class, KeyStore.class, String.class, KeyStore.class,
SecureRandom.class, HostNameResolver.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {

String algorithm = (String) param.args[0];
KeyStore keystore = (KeyStore) param.args[1];
String keystorePassword = (String) param.args[2];
SecureRandom random = (SecureRandom) param.args[4];

KeyManager[] keymanagers = null;
TrustManager[] trustmanagers = null;

if (keystore != null) {
keymanagers = (KeyManager[]) callStaticMethod(SSLSocketFactory.class, "createKeyManagers", keystore, keystorePassword);
}

trustmanagers = new TrustManager[]{new ImSureItsLegitTrustManager()};

setObjectField(param.thisObject, "sslcontext", SSLContext.getInstance(algorithm));
callMethod(getObjectField(param.thisObject, "sslcontext"), "init", keymanagers, trustmanagers, random);
setObjectField(param.thisObject, "socketfactory",
callMethod(getObjectField(param.thisObject, "sslcontext"), "getSocketFactory"));
}

});


/* external/apache-http/src/org/apache/http/conn/ssl/SSLSocketFactory.java */
/* public static SSLSocketFactory getSocketFactory() */
Log.d(TAG, "Hooking static SSLSocketFactory(String, KeyStore, String, KeyStore) for: " + currentPackageName);
findAndHookMethod("org.apache.http.conn.ssl.SSLSocketFactory", lpparam.classLoader, "getSocketFactory", new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return (SSLSocketFactory) newInstance(SSLSocketFactory.class);
}
});

/* external/apache-http/src/org/apache/http/conn/ssl/SSLSocketFactory.java */
/* public boolean isSecure(Socket) */
Log.d(TAG, "Hooking SSLSocketFactory(Socket) for: " + currentPackageName);
findAndHookMethod("org.apache.http.conn.ssl.SSLSocketFactory", lpparam.classLoader, "isSecure", Socket.class, new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return true;
}
});

/* JSSE Hooks */
/* libcore/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java */
/* public final TrustManager[] getTrustManager() */
Log.d(TAG, "Hooking TrustManagerFactory.getTrustManagers() for: " + currentPackageName);
findAndHookMethod("javax.net.ssl.TrustManagerFactory", lpparam.classLoader, "getTrustManagers", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {

if (hasTrustManagerImpl()) {
Class<?> cls = findClass("com.android.org.conscrypt.TrustManagerImpl", lpparam.classLoader);

TrustManager[] managers = (TrustManager[])param.getResult();
if(managers.length > 0 && cls.isInstance(managers[0]))
return;
}

param.setResult(new TrustManager[]{new ImSureItsLegitTrustManager()});
}
});

/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
/* public void setDefaultHostnameVerifier(HostnameVerifier) */
Log.d(TAG, "Hooking HttpsURLConnection.setDefaultHostnameVerifier for: " + currentPackageName);
findAndHookMethod("javax.net.ssl.HttpsURLConnection", lpparam.classLoader, "setDefaultHostnameVerifier",
HostnameVerifier.class, new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
});

/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
/* public void setSSLSocketFactory(SSLSocketFactory) */
Log.d(TAG, "Hooking HttpsURLConnection.setSSLSocketFactory for: " + currentPackageName);
findAndHookMethod("javax.net.ssl.HttpsURLConnection", lpparam.classLoader, "setSSLSocketFactory", javax.net.ssl.SSLSocketFactory.class,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
});

/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */
/* public void setHostnameVerifier(HostNameVerifier) */
Log.d(TAG, "Hooking HttpsURLConnection.setHostnameVerifier for: " + currentPackageName);
findAndHookMethod("javax.net.ssl.HttpsURLConnection", lpparam.classLoader, "setHostnameVerifier", HostnameVerifier.class,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
});


/* WebView Hooks */
/* frameworks/base/core/java/android/webkit/WebViewClient.java */
/* public void onReceivedSslError(Webview, SslErrorHandler, SslError) */
Log.d(TAG, "Hooking WebViewClient.onReceivedSslError(WebView, SslErrorHandler, SslError) for: " + currentPackageName);

findAndHookMethod("android.webkit.WebViewClient", lpparam.classLoader, "onReceivedSslError",
WebView.class, SslErrorHandler.class, SslError.class, new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
((android.webkit.SslErrorHandler)param.args[1]).proceed();
return null;
}
});

/* frameworks/base/core/java/android/webkit/WebViewClient.java */
/* public void onReceivedError(WebView, int, String, String) */
Log.d(TAG, "Hooking WebViewClient.onReceivedSslError(WebView, int, string, string) for: " + currentPackageName);

findAndHookMethod("android.webkit.WebViewClient", lpparam.classLoader, "onReceivedError",
WebView.class, int.class, String.class, String.class, new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
});

// Multi-dex support: https://github.com/rovo89/XposedBridge/issues/30#issuecomment-68486449
findAndHookMethod("android.app.Application",
lpparam.classLoader,
"attach",
Context.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// Hook OkHttp or third party libraries.
Context context = (Context) param.args[0];
processOkHttp(context.getClassLoader());
}
}
);

/* Only for newer devices should we try to hook TrustManagerImpl */
if (hasTrustManagerImpl()) {

/* external/conscrypt/src/platform/java/org/conscrypt/TrustManagerImpl.java#217 */
/* public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
String authType, String host) throws CertificateException */
Log.d(TAG, "Hooking com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(X509Certificate[]) for: " + currentPackageName);
findAndHookMethod("com.android.org.conscrypt.TrustManagerImpl", lpparam.classLoader,
"checkServerTrusted", X509Certificate[].class, String.class,
String.class, new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
ArrayList<X509Certificate> list = new ArrayList<X509Certificate>();
return list;
}
});
}
} // End Hooks

/* Helpers */
// Check for TrustManagerImpl class
public boolean hasTrustManagerImpl() {

try {
Class.forName("com.android.org.conscrypt.TrustManagerImpl");
} catch(ClassNotFoundException e) {
return false;
}
return true;
}

//Create a SingleClientConnManager that trusts everyone!
public ClientConnectionManager getSCCM() {

KeyStore trustStore;
try {

trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);

SSLSocketFactory sf = new TrustAllSSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));

ClientConnectionManager ccm = new SingleClientConnManager(null, registry);

return ccm;

} catch (Exception e) {
return null;
}
}

//This function creates a ThreadSafeClientConnManager that trusts everyone!
public ClientConnectionManager getTSCCM(HttpParams params) {

KeyStore trustStore;
try {

trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);

SSLSocketFactory sf = new TrustAllSSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));

ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

return ccm;

} catch (Exception e) {
return null;
}
}

//This function determines what object we are dealing with.
public ClientConnectionManager getCCM(Object o, HttpParams params) {

String className = o.getClass().getSimpleName();

if (className.equals("SingleClientConnManager")) {
return getSCCM();
}
else if (className.equals("ThreadSafeClientConnManager")) {
return getTSCCM(params);
}

return null;
}

void processOkHttp(ClassLoader classLoader) {
/* hooking OKHTTP by SQUAREUP */
/* com/squareup/okhttp/CertificatePinner.java available online @ https://github.com/square/okhttp/blob/master/okhttp/src/main/java/com/squareup/okhttp/CertificatePinner.java */
/* public void check(String hostname, List<Certificate> peerCertificates) throws SSLPeerUnverifiedException{}*/
/* Either returns true or a exception so blanket return true */
/* Tested against version 2.5 */
Log.d(TAG, "Hooking com.squareup.okhttp.CertificatePinner.check(String,List) (2.5) for: " + currentPackageName);

try {
classLoader.loadClass("com.squareup.okhttp.CertificatePinner");
findAndHookMethod("com.squareup.okhttp.CertificatePinner",
classLoader,
"check",
String.class,
List.class,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return true;
}
});
} catch(ClassNotFoundException e) {
// pass
Log.d(TAG, "OKHTTP 2.5 not found in " + currentPackageName + "-- not hooking");
}

//https://github.com/square/okhttp/blob/parent-3.0.1/okhttp/src/main/java/okhttp3/CertificatePinner.java#L144
Log.d(TAG, "Hooking okhttp3.CertificatePinner.check(String,List) (3.x) for: " + currentPackageName);

try {
classLoader.loadClass("okhttp3.CertificatePinner");
findAndHookMethod("okhttp3.CertificatePinner",
classLoader,
"check",
String.class,
List.class,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return null;
}
});
} catch(ClassNotFoundException e) {
Log.d(TAG, "OKHTTP 3.x not found in " + currentPackageName + " -- not hooking");
// pass
}

//https://github.com/square/okhttp/blob/parent-3.0.1/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java
try {
classLoader.loadClass("okhttp3.internal.tls.OkHostnameVerifier");
findAndHookMethod("okhttp3.internal.tls.OkHostnameVerifier",
classLoader,
"verify",
String.class,
javax.net.ssl.SSLSession.class,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return true;
}
});
} catch(ClassNotFoundException e) {
Log.d(TAG, "OKHTTP 3.x not found in " + currentPackageName + " -- not hooking OkHostnameVerifier.verify(String, SSLSession)");
// pass
}

//https://github.com/square/okhttp/blob/parent-3.0.1/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java
try {
classLoader.loadClass("okhttp3.internal.tls.OkHostnameVerifier");
findAndHookMethod("okhttp3.internal.tls.OkHostnameVerifier",
classLoader,
"verify",
String.class,
java.security.cert.X509Certificate.class,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return true;
}
});
} catch(ClassNotFoundException e) {
Log.d(TAG, "OKHTTP 3.x not found in " + currentPackageName + " -- not hooking OkHostnameVerifier.verify(String, X509)(");
// pass
}
}

class ImSureItsLegitTrustManager implements 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];
}
}

/* This class creates a SSLSocket that trusts everyone. */
public class TrustAllSSLSocketFactory extends SSLSocketFactory {

SSLContext sslContext = SSLContext.getInstance("TLS");

public TrustAllSSLSocketFactory(KeyStore truststore) throws
NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);

TrustManager tm = new X509TrustManager() {

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}

public X509Certificate[] getAcceptedIssuers() {
return null;
}
};

sslContext.init(null, new TrustManager[] { tm }, null);
}

@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}

@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
}
}
// findAndHookMethod 就是hook对应方法 它hook的函数就是 证书校验用到的函数

通过上述代码我们发现JustTrustMe几乎hook住了所有有关证书验证的操作通过修改返回值、忽略证书错误等方法来进行绕过。

但是JustTrustMe没有hook public OkHttpClient.Builder hostnameVerifier(Okhttp 中如果不指定 HostnameVerifier 默认调用的是 OkHostnameVerifier.verify 进行服务器主机名校验;如果设置了 HostnameVerifier,则默认调用的是自定义的 verify 方法,而OkHttpClient.Builder是用来对OkHttpClient进行设置的),所以这里就很可能出问题。至于其他的漏掉的hook目前还未测出。也有可能是他是github星星最多的项目,大家都对它进行下手了,😂。总之JustTrustMe有时候会不起作用。所以我平时也不是经常用它。

ssl-unpining模块绕过

此模块是我用的最多的,他比JustTrustMe 更稳定、hook的更全面,一般可以直接hook抓包,而且由于它同样不需要我们编写代码,是最方便新手的了。

模块下载地址:https://github.com/ac-pm/SSLUnpinning_Xposed

直接安装后重启配置代理抓包即可,简单粗暴。

frida绕过SSLPinning

frida确实是很好用的一个工具,它不需要像xpose那样频繁重启,而且还可以定制化hook自己想修改的函数。我平常用frida是通过js代码来进行hook的

下面就是我常用的一个ssl-Unpinning的脚本(其实就是通过人家开源的修改而来):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*
Android SSL Re-pinning frida script v0.2 030417-pier

$ adb push burpca-cert-der.crt /data/local/tmp/cert-der.crt
$ frida -U -f it.app.mobile -l frida-android-repinning.js --no-pause

https://techblog.mediaservice.net/2017/07/universal-android-ssl-pinning-bypass-with-frida/
*/

setTimeout(function(){
Java.perform(function (){
console.log("");
console.log("[.] Cert Pinning Bypass/Re-Pinning");

var CertificateFactory = Java.use("java.security.cert.CertificateFactory");
var FileInputStream = Java.use("java.io.FileInputStream");
var BufferedInputStream = Java.use("java.io.BufferedInputStream");
var X509Certificate = Java.use("java.security.cert.X509Certificate");
var KeyStore = Java.use("java.security.KeyStore");
var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
var SSLContext = Java.use("javax.net.ssl.SSLContext");

// Load CAs from an InputStream
console.log("[+] Loading our CA...")
cf = CertificateFactory.getInstance("X.509");

try {
var fileInputStream = FileInputStream.$new("/data/local/tmp/1111.crt");
}
catch(err) {
console.log("[o] " + err);
}

var bufferedInputStream = BufferedInputStream.$new(fileInputStream);
var ca = cf.generateCertificate(bufferedInputStream);
bufferedInputStream.close();

var certInfo = Java.cast(ca, X509Certificate);
console.log("[o] Our CA Info: " + certInfo.getSubjectDN());

// Create a KeyStore containing our trusted CAs
console.log("[+] Creating a KeyStore for our CA...");
var keyStoreType = KeyStore.getDefaultType();
var keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

// Create a TrustManager that trusts the CAs in our KeyStore
console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore...");
var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
console.log("[+] Our TrustManager is ready...");

console.log("[+] Hijacking SSLContext methods now...")
console.log("[-] Waiting for the app to invoke SSLContext.init()...")

SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) {
console.log("[o] App invoked javax.net.ssl.SSLContext.init...");
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c);
console.log("[+] SSLContext initialized with our custom TrustManager!");
}
});
},0);

使用步骤:

1、导出burp证书放入手机的/data/local/tmp内,证书格式为crt(可直接burp证书修改后缀名),最好也在手机上安装下这个证书方便以后抓包的操作

2、adb shell 进入手机命令行 开启 frida server

3、修改frida-android-repining_sa1.js代码中28行的证书路径

4、frida -U -f 包名 -l .\frida-android-repining_sa1.js –no-pause

结语

上述这些都是我绕过SSL-Pinning的一些经验,如果有错误的地方还希望大家可知指出来,帮助我更好的学习andriod安全.然后想说最近访问点涨的有点猛,不知道是真的有人在看吗?还是有人扫描?我很慌!!!


采用署名-非商业性使用-相同方式共享 4.0(CC BY-NC-SA 4.0)许可协议
「分享也是一种学习」