[목차]
0. 개요
1. SSL Pinning 관련 클래스 소스 분석
2. 코드 작성 및 실행
0. 개요
실제 SSL Pinning이 적용된
금융권 등의 앱을 진단하는 경우
이를 우회하지 않으면
패킷 변조 등의 항목 진단이
불가능해 곤란을 겪을 수 있다.
이전 시간에는
누군가가 만들어 놓은
프리다 코드를 사용하여
SSL Pinning 우회 실습을
하였는데
이번에는 코드를 분석하여
직접 우회 코드를 작성,
실습해 본다.
1. SSL Pinning 관련 클래스 소스 분석
# smuldr.sslpin.PinnedSSLContextFactory 클래스
package smuldr.sslpin;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
/* loaded from: classes.dex */
public class PinnedSSLContextFactory {
private static final String TAG = "PinnedSSLContextFactory";
public static SSLContext getSSLContext(InputStream input) {
try {
Certificate ca = loadCertificate(input);
KeyStore keyStore = createKeyStore(ca);
TrustManager[] trustManagers = createTrustManager(keyStore);
return createSSLContext(trustManagers);
} catch (KeyManagementException e) {
Log.e(TAG, "Failed to initialize SSL Context", e);
return null;
} catch (KeyStoreException e2) {
Log.e(TAG, "Failed to get key store instance", e2);
return null;
} catch (CertificateException e3) {
Log.e(TAG, "Failed to create certificate factory", e3);
return null;
}
}
private static Certificate loadCertificate(InputStream input) throws CertificateException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return cf.generateCertificate(input);
}
private static KeyStore createKeyStore(Certificate ca) throws KeyStoreException {
try {
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
return keyStore;
} catch (IOException e) {
Log.e(TAG, "Could not load key store", e);
return null;
} catch (NoSuchAlgorithmException e2) {
Log.e(TAG, "Could not load key store", e2);
return null;
} catch (CertificateException e3) {
Log.e(TAG, "Could not load key store", e3);
return null;
}
}
private static TrustManager[] createTrustManager(KeyStore keyStore) throws KeyStoreException {
try {
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
return tmf.getTrustManagers();
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "Failed to get trust manager factory with default algorithm", e);
return null;
}
}
private static SSLContext createSSLContext(TrustManager[] trustManagers) throws KeyManagementException {
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, null);
return context;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "Failed to initialize SSL context with TLS algorithm", e);
return null;
}
}
}
후킹 포인트는
여러 가지가 있을 수 있는데
그중에서 가장 복잡한
smuldr.sslpin.PinnedSSLContextFactory 클래스의
PinnedSSLContextFactory 클래스 내
getSSLContext 메서드 4가지를
변조해 본다.
2. 코드 작성 및 실행
# SSL 통신 시 클라이언트 인증서 사용 순서
1) 클라이언트 인증서 로드 >
2) 로드한 클라이언트 인증서를 이용해 keyManager 만듦 >
3) 로드한 클라이언트 인증서 이용해 TrustManager 만듦 >
4) KeyManager, TrustManager를 이용해 SSLContext 생성
setTimeout(function(){
Java.perform(function(){
// # 후킹 타깃
// smuldr.sslpin.PinnedSSLContextFactory 클래스 내 PinnedSSLContextFactory 클래스의 getSSLContext 메서드 4가지
//
// public static SSLContext getSSLCOntext(InputStream, input){
// try{
// return createSSLContext(createTrustManager(createKeyStore(loadCertificate(input))));
// 01. 첫 번째 메서드 - loadCertificate (사용하려는 인증서 load)
var CertificateFactory = Java.use("java.security.cert.CertificateFactory");
var cf = CertificateFactory.getInstance("x.509");
var FileInputStream = Java.use("java.io.FileInputStream"); // java에서 기본적으로 사용하는 파일 입출력 인풋스트림
var fileInputStrem = FileInputStream.$new("/data/local/tmp/cert-der.crt"); // instance화 시켜줌
var ca = cf.generateCertificate(fileInputStrem);
console.log("\n[+] 1. 버프 인증서 정상 loading 완료!");
// 02. 두 번째 메서드 - CreateKeyStore (키 매니저 생성)
var KeyStore = Java.use("java.security.KeyStore");
var keyStoreType = KeyStore.getDefaultType();
var keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
console.log("[+] 2. 키 매니저 생성 완료!");
// 03. 세 번째 메서드 - createTrustManager (트러스트 매니저 생성) < 자격증명, 신뢰 결정 매니저
var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
var tmf_get = tmf.getTrustManagers();
console.log("[+] 3. 트러스트 매니저 생성 완료!\n\n[+] 앱에서 SSL PINNING 체크 후 request를 요청하세요! ");
// 04. 네 번째 메서드 - createSSLContext 생성
var SSLContext = Java.use("javax.net.ssl.SSLContext");
SSLContext.init.implementation = function(a, b, c){
SSLContext.init.call(this, a, tmf_get, c); // 기존에 작성된 코드 재 호출, 버프 인증서를 무조건 호출! call 메서드를 실행해 다른 새로운 객체의 init 메서드를 실행한다.
console.log("\n\n[+] SSL Pinning 정상 우회 성공!!");
}
})
})
돌려보면
정상 우회 성공!
# 참고로
프리다 없이 인증서를 변조하려는 경우
해당 앱이 무결성 검증을 하지 않는다는 가정하에
정상적으로 우회가 가능할 수 있다.
728x90
'APP 진단 > Android 2' 카테고리의 다른 글
LW 루팅 탐지 우회 - feat.frida (0) | 2023.03.10 |
---|---|
13. 프리다 실무 활용 - SSL Pinning 우회 (1) | 2022.12.16 |
12. 프리다 실무 활용 - 로그인 우회 (Sieve.apk) (1) | 2022.12.15 |
11. 프리다 실무 활용 - Password 복호화 (UnCrackable-Level1.apk) (0) | 2022.12.13 |
10. 프리다 실무 활용 - 루팅 탐지 우회 (UnCrackable-Level1.apk) (0) | 2022.12.13 |