[목차]

 

 

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

+ Recent posts